无法解析ISO 8601格式的字符串,缺少冒号的冒号,到Java 8 Date

我对java 8日期格式/解析function有点沮丧。 我试图找到Jackson配置和DateTimeFormatter来解析"2018-02-13T10:20:12.120+0000"字符串到任何Java 8日期,并没有找到它。
这是java.util.Date示例,它工作正常:

 Date date = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSZZZ") .parse("2018-02-13T10:20:12.120+0000"); 

相同的格式不适用于新的日期时间api

 ZonedDateTime dateTime = ZonedDateTime.parse("2018-02-13T10:20:12.120+0000", DateTimeFormatter.ofPattern("yyyy-MM-dd'T'hh:mm:ss.SSSZZZ")); 

我们应该能够以适合FE UI应用程序的任何格式格式化/解析日期。 也许我误解或误解了一些东西,但我认为java.util.Date提供了更多的格式灵活性并且更易于使用。

TL;博士

直到bug被修复:

 OffsetDateTime.parse( "2018-02-13T10:20:12.120+0000" , DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSX" ) ) 

当错误修复时:

 OffsetDateTime.parse( "2018-02-13T10:20:12.120+0000" ) 

细节

您使用的是错误的类。

避免麻烦的旧遗留类,如DateCalendarSimpleDateFormat 。 现在取代了java.time类。

您使用的ZonedDateTime类很好,它是java.time的一部分。 但它适用于全时区。 您的输入字符串只有一个与UTC的偏移量 。 相比之下,全时区是对不同时间点,过去,现在和将来的区域有效的偏移的集合。 例如,在北美大部分地区使用夏令时(DST)时,每年两次的偏移量会在春季变小,因为我们将时钟向前移动一小时,而在秋季我们将时钟向后移动时恢复到更长的值。小时。

OffsetDateTime

仅对于偏移而不是时区,请使用OffsetDateTime类。

您的输入字符串符合ISO 8601标准。 解析/生成字符串时,java.time类在默认情况下使用标准格式。 因此无需指定格式化模式。

 OffsetDateTime odt = OffsetDateTime.parse( "2018-02-13T10:20:12.120+0000" ); 

应该是有效的。 遗憾的是, Java 8中存在一个错误 (至少通过Java 8 Update 121),该类无法解析在小时和分钟之间省略冒号的偏移量。 所以虫子咬了+0000但不是+00:00 。 因此,在修复程序到达之前,您可以选择两种解决方法:(a)hack,操作输入字符串,或(b)定义显式格式化模式。

hack:操纵输入字符串以插入冒号。

 String input = "2018-02-13T10:20:12.120+0000".replace( "+0000" , "+00:00" ); OffsetDateTime odt = OffsetDateTime.parse( input ); 

更强大的解决方法是在DateTimeFormatter对象中定义和传递格式化模式。

 String input = "2018-02-13T10:20:12.120+0000" ; DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSX" ); OffsetDateTime odt = OffsetDateTime.parse( input , f ); 

odt.toString():2018-02-13T10:20:12.120Z

顺便说一句,这里有一个提示:我发现有很多协议和库,如果你的偏移总是有冒号,你的生活会更容易,总是有小时和分钟(即使分钟为零),并且总是使用填充零( -05:00而不是-5 )。

Instant

如果要使用始终为UTC(并且应该)的值,请提取Instant对象。

 Instant instant = odt.toInstant(); 

ZonedDateTime

如果您想通过某个区域的挂钟时间镜头查看该时刻,请应用时区。

 ZoneId z = ZoneId.of( "America/Montreal" ); ZonedDateTime zdt = odt.atZoneSameInstant( z ); 

请参阅IdeOne.com上的此代码 。

在许多问题的答案中,所有这些都已被多次涵盖。 请在发布前彻底搜索Stack Overflow。 你会发现很多甚至数百个例子。


关于java.time

java.time框架内置于Java 8及更高版本中。 这些类取代了麻烦的旧遗留日期时间类,如java.util.DateCalendarSimpleDateFormat

现在处于维护模式的Joda-Time项目建议迁移到java.time类。

要了解更多信息,请参阅Oracle教程 。 并搜索Stack Overflow以获取许多示例和解释。 规范是JSR 310 。

您可以直接与数据库交换java.time对象。 使用符合JDBC 4.2或更高版本的JDBC驱动程序 。 不需要字符串,不需要java.sql.*类。

从哪里获取java.time类?

  • Java SE 8Java SE 9Java SE 10及更高版本
    • 内置。
    • 带有捆绑实现的标准Java API的一部分。
    • Java 9增加了一些小function和修复。
  • Java SE 6Java SE 7
    • 许多java.timefunction都被反向移植到ThreeTen-Backport中的 Java 6和7。
  • Android的
    • 更高版本的Android捆绑java.time类的实现。
    • 对于早期的Android(<26), ThreeTenABP项目采用ThreeTen-Backport (如上所述)。 请参见如何使用ThreeTenABP ….

ThreeTen-Extra项目使用其他类扩展了java.time。 该项目是未来可能添加到java.time的试验场。 您可以在这里找到一些有用的课程,如IntervalYearWeekYearQuarter等。

您可以使用以下代码完成工作。

 String str = "01/01/2015"; DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy"); LocalDate dateTime = LocalDate.parse(str, formatter); System.out.println(dateTime.format(formatter)); // not using toString