Android转换日期时间解析错误(甚至尝试了joda时间)
我正在解析许多新闻源,每个项目的pubDate遵循相同的格式:
2017年6月11日星期日太阳18:18:23 +0000
不幸的是,一个Feed没有:
2017年6月10日星期六美国东部时间12:49:45
我试图使用androids java date和SimpleDateFormat
来解析日期并没有运气:
try { Calendar cal = Calendar.getInstance(); TimeZone tz = cal.getTimeZone(); SimpleDateFormat readDate = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z"); readDate.setTimeZone(TimeZone.getTimeZone("UTC")); Date date = readDate.parse(rssDateTime); SimpleDateFormat writeDate = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z"); writeDate.setTimeZone(tz); parsedDate = writeDate.format(date); } catch (ParseException e) { e.printStackTrace(); }
哪个引发错误:
java.text.ParseException:无法解析的日期:“星期六,2017年6月3日19:53:09 EST”(偏移26)
我也尝试过使用joda时间:
DateTime dtUTC = null; DateTimeZone timezone = DateTimeZone.getDefault(); DateTimeFormatter formatDT = DateTimeFormat.forPattern("EEE, d MMM yyyy HH:mm:ss Z"); DateTime dtRssDateTime = formatDT.parseDateTime(rssDateTime); DateTime now = new DateTime(); DateTime nowUTC = new LocalDateTime(now).toDateTime(DateTimeZone.UTC); long instant = now.getMillis(); long instantUTC = nowUTC.getMillis(); long offset = instantUTC - instant; dtUTC = dtRssDateTime.withZoneRetainFields(timezone); dtUTC = dtUTC.minusMillis((int) offset); String returnTimeDate = ""; returnTimeDate = dtUTC.toString(formatDT);
哪个引发错误:
引起:java.lang.IllegalArgumentException:格式无效:“星期六,2017年6月10日12:49:45 EST”在“EST”格式错误
有没有人遇到过这个?
首先,如果您正在开始一个新项目,我建议您使用新的日期时间API而不是joda-time(更多内容如下)。 无论如何,这是两者的解决方案。
乔达时间
问题是模式Z
是偏移量(格式为+0000
或-0100
),但字符串EST
是时区短名称,由模式z
解析(有关详细信息,请查看jodatime javadoc )。
因此,您需要一个带有可选部分的模式,可以同时接收一个或另一个。 您可以使用org.joda.time.format.DateTimeFormatterBuilder
类来完成此org.joda.time.format.DateTimeFormatterBuilder
。
首先,您需要创建2个org.joda.time.format.DateTimeParser
实例(一个用于Z
,另一个用于z
),并将它们添加为可选的解析器。 然后使用下面的代码创建org.joda.time.format.DateTimeFormatter
。 请注意,我还使用了java.util.Locale
,只是为了确保它正确解析工作日和月份名称(因此您不依赖于默认语言环境,这可能因系统/机器而异):
// offset parser (for "+0000") DateTimeParser offsetParser = new DateTimeFormatterBuilder().appendPattern("Z").toParser(); // timezone name parser (for "EST") DateTimeParser zoneNameParser = new DateTimeFormatterBuilder().appendPattern("z").toParser(); // formatter for both patterns DateTimeFormatter fmt = new DateTimeFormatterBuilder() // append common pattern .appendPattern("EEE, d MMM yyyy HH:mm:ss ") // optional offset .appendOptional(offsetParser) // optional timezone name .appendOptional(zoneNameParser) // create formatter (use English Locale to make sure it parses weekdays and month names independent of JVM config) .toFormatter().withLocale(Locale.ENGLISH) // make sure the offset "+0000" is parsed .withOffsetParsed(); // parse the strings DateTime est = fmt.parseDateTime("Sat, 10 Jun 2017 12:49:45 EST"); DateTime utc = fmt.parseDateTime("Sun, 11 Jun 2017 18:18:23 +0000"); System.out.println(est); System.out.println(utc);
输出将是:
2017-06-10T12:49:45.000-04:00
2017-06-11T18:18:23.000Z
如果它们不像您期望的那样(或者您仍然遇到错误),请参阅下面的注释。
备注 :
-
请注意,
EST
打印为偏移-0400
的日期/时间。 这是因为EST
内部成为America/New_York
时区,现在是夏令时,它的偏移量是-0400
(我可以通过做DateTimeZone.forTimeZone(TimeZone.getTimeZone("EST"))
解决这个问题。DateTimeZone.forTimeZone(TimeZone.getTimeZone("EST"))
问题是:这些3个字母的名称含糊不清,而且不是标准的 ,joda-time只是假定它们是“默认”。所以,如果你不期望这个时区,并且你不想依赖默认值,你可以使用地图自定义值,如下所示:// mapping EST to some other timezone (I know it's wrong and Chicago is not EST, it's just an example) Map
map = new LinkedHashMap<>(); map.put("EST", DateTimeZone.forID("America/Chicago")); // parser for my custom map DateTimeParser customTimeZoneParser = new DateTimeFormatterBuilder().appendTimeZoneShortName(map).toParser(); DateTimeFormatter fmt = new DateTimeFormatterBuilder() // append common pattern .appendPattern("EEE, d MMM yyyy HH:mm:ss ") // optional offset .appendOptional(offsetParser) // optional custom timezone name .appendOptional(customTimeZoneParser) // optional timezone name (accepts all others that are not in the map) .appendOptional(zoneNameParser) // create formatter (use English Locale to make sure it parses weekdays and month names independent of JVM config) .toFormatter().withLocale(Locale.ENGLISH) // make sure the offset "+0000" is parsed .withOffsetParsed(); System.out.println(fmt.parseDateTime("Sat, 10 Jun 2017 12:49:45 EST"));
这将解析EST
为America/Chicago
(我知道它错了,芝加哥不是EST
,它只是一个如何使用地图更改默认值的示例),输出将是:
2017-06-10T12:49:45.000-05:00
如果上面的第一个代码出错,你也可以使用它,将EST
映射到所需的时区(取决于你正在使用的jodatime和Java的版本, EST
可能没有映射到默认值并抛出exception,所以使用自定义地图避免这种情况)。
新的日期时间API
正如在@Ole VV的评论中所说的那样 (我昨天没有时间写),joda-time正在被新的Java的Date and Time API取代, 与旧的Date
和SimpleDateFormat
类相比 ,它远远优于Java 。
如果您使用Java> = 8,则java.time
包已经是JDK的一部分。 对于Java <= 7,有ThreeTen Backport 。 对于Android ,还有ThreeTenABP (更多关于如何在这里使用它)。
如果您正在开始一个新项目,请考虑新的API而不是joda-time,因为在joda的网站上它说: 注意Joda-Time被认为是一个很大程度上“完成”的项目。 没有计划重大改进。 如果使用Java SE 8,请迁移到java.time(JSR-310) 。
以下代码适用于两者。 唯一的区别是包名称(在Java 8中是java.time
,在ThreeTen Backport(或Android的ThreeTenABP)中是org.threeten.bp
),但类和方法名称是相同的。
这个想法与jodatime非常相似,但有一点不同:
- 你可以使用可选的section delimiters
[]
- 需要一个具有自定义时区名称的集合(将
EST
映射到某个有效的非模糊时区)(因为EST
未映射到任何默认值) - 使用了一个新类:
ZonedDateTime
,它表示带有时区的日期和时间(因此它涵盖了两种情况)
只是提醒这些类在java.time
包中(或者在org.threeten.bp
取决于你正在使用的Java版本,如上所述):
// set with custom timezone names Set set = new HashSet<>(); // when parsing, ambiguous EST uses to New York set.add(ZoneId.of("America/New_York")); DateTimeFormatter fmt = new DateTimeFormatterBuilder() // append pattern, with optional offset (delimited by []) .appendPattern("EEE, d MMM yyyy HH:mm:ss[ Z]") // append optional timezone name with custom set for EST .optionalStart().appendLiteral(" ").appendZoneText(TextStyle.SHORT, set).optionalEnd() // create formatter using English locale to make sure it parses weekdays and month names correctly .toFormatter(Locale.ENGLISH); ZonedDateTime est = ZonedDateTime.parse("Sat, 10 Jun 2017 12:49:45 EST", fmt); ZonedDateTime utc = ZonedDateTime.parse("Sun, 11 Jun 2017 18:18:23 +0000", fmt); System.out.println(est); // 2017-06-10T12:49:45-04:00[America/New_York] System.out.println(utc); // 2017-06-11T18:18:23Z
输出将是:
2017-06-10T12:49:45-04:00 [美国/纽约]
2017-06-11T18:18:23Z
请注意,在第一种情况下, EST
设置为America/New_York
(由自定义集配置)。 appendZoneText
使用自定义集中的值来解决不明确的情况。
第二种情况设置为UTC,偏移量为+0000
。
如果你想将第一个对象转换为UTC,它就是直截了当的:
System.out.println(est.withZoneSameInstant(ZoneOffset.UTC)); // 2017-06-10T16:49:45Z
输出将是纽约的转换为UTC的日期/时间:
2017-06-10T16:49:45Z
当然,您可以使用所需的任何时区或偏移量来ZoneId
ZoneOffset
(使用ZoneId
和ZoneOffset
类,请查看javadoc以获取更多详细信息)。