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

时间:2017-04-12 05:18:42

标签: java parsing datetime java-8 iso8601

我对java 8日期格式/解析功能有点沮丧。我试图找到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提供了更多格式灵活性并且更易于使用。

3 个答案:

答案 0 :(得分:20)

TL;博士

直到错误修复:

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的一部分。但它适用于全时区。您的输入字符串只有offset-from-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

更强大的解决方法是在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)。

DateTimeFormatterBuilder

对于通过DateTimeFormatterBuilder创建的更灵活的格式化程序,请参阅this excellent Answer重复的问题。

Instant

如果您想使用始终为UTC的值(您应该这样做),请提取Instant个对象。

Instant instant = odt.toInstant();

ZonedDateTime

如果您想通过某个地区wall-clock time的镜头观看该时刻,请应用时区。

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

请参阅此code run live at IdeOne.com

许多问题在许多答案中已经涵盖了很多次。请在发布前彻底搜索Stack Overflow。你会发现很多甚至数百个例子。

关于 java.time

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

现在位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

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

从哪里获取java.time类?

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

答案 1 :(得分:2)

简短:不是错误,只是您的模式是错误的。

请使用专为时区偏移量设计的OffsetDateTime类型,并以这种方式使用模式:

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

详细问题:

a)12小时制与24小时制

“ h”表示12小时制的AM / PM的小时数,但显然,按照ISO-8601的要求,在24小时制中需要“ H”。

b)零偏移的形式

如果要解析零偏移量,例如“ +0000”而不是“ Z”(如ISO纸张中所述),则不应使用模式符号“ X”,而应使用“ ZZZ”。引用pattern syntax

Offset Z(偏移Z):这将根据图案数量格式化偏移 字母。一,二或三个字母输出小时和分钟, 没有冒号,例如'+0130'。当 偏移量为零。

c)您的输入不兼容ISO-8601,因此Java中没有错误

您认为“ 2018-02-13T10:20:12.120 + 0000”应为有效ISO的假设是错误的,因为您混合了基本格式(在胶印部分中)和扩展格式,而ISO纸中明确禁止使用扩展格式(请参见第4.3.2节(示例部分)和4.3.3d)。引用ISO-8601:

[...]表达式应完全采用基本格式,其中 情况下所需的最小分隔符数 使用表达式,或完全使用扩展格式[...]

B。Bourque关于java.time有错误的说法是基于对ISO兼容性的同样错误预期。假设ISO_OFFSET_DATE_TIME的文档仅描述了对扩展ISO格式的支持。另请参阅相关的JDK issue。并非所有ISO-8601变体都直接受支持,因此以正确的方式基于模式的解析器构造是可以的。

答案 2 :(得分:0)

如果偏移量+0000,请尝试

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