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

Cannot parse String in ISO 8601 format, lacking colon in offset, to Java 8 Date

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

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

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

1
2
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被修复:

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

当错误修复时:

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

细节

您使用的是错误的类。

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

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

OffsetDateTime

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

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

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

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

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

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

DateTimeFormatter

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

1
2
3
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)。

DateTimeFormatterBuilder

对于通过DateTimeFormatterBuilder创建的更灵活的格式化程序,请参阅此重复问题的优秀答案。

Instant

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

1
Instant instant = odt.toInstant();

ZonedDateTime

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

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

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

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

关于java.time

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

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

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

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

从哪里获取java.time类?

  • Java SE 8,Java SE 9,Java SE 10及更高版本

    • 内置。
    • 带有捆绑实现的标准Java API的一部分。
    • Java 9增加了一些小功能和修复。
  • Java SE 6和Java SE 7

    • 许多java.time功能都被反向移植到Java 6& 7在ThreeTen-Backport。
  • Android的

    • 更高版本的Android捆绑java.time类的实现。
    • 对于早期的Android(<26),ThreeTenABP项目采用ThreeTen-Backport(如上所述)。请参见如何使用ThreeTenABP ....

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