简体   繁体   English

无法将 ISO 8601 格式的字符串解析为 Java 8 日期,在偏移量中缺少冒号

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

I'm a little bit frustrated of java 8 date format/parse functionality.我对 java 8 日期格式/解析功能有点沮丧。 I was trying to find Jackson configuration and DateTimeFormatter to parse "2018-02-13T10:20:12.120+0000" string to any Java 8 date, and didn't find it.我试图找到 Jackson 配置和DateTimeFormatter"2018-02-13T10:20:12.120+0000"字符串解析为任何 Java 8 日期,但没有找到。
This is java.util.Date example which works fine:这是java.util.Date示例,它工作正常:

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

The same format doesn't work with new date time api相同的格式不适用于新的日期时间 api

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

We should be able to format/parse date in any format suitable for FE UI application.我们应该能够以适合 FE UI 应用程序的任何格式格式化/解析日期。 Maybe I misunderstand or mistake something, but I think java.util.Date gives more format flexibility and easier to use.也许我误解或弄错了一些东西,但我认为java.util.Date提供了更多的格式灵活性和更易于使用。

tl;dr tl;博士

Until bug is fixed:直到错误修复:

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

When bug is fixed:当错误被修复时:

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

Details细节

You are using the wrong classes.您使用了错误的类。

Avoid the troublesome old legacy classes such as Date , Calendar , and SimpleDateFormat .避免麻烦的旧遗留类,例如DateCalendarSimpleDateFormat Now supplanted by the java.time classes.现在被java.time类取代。

The ZonedDateTime class you used is good, it is part of java.time.您使用的ZonedDateTime类很好,它是 java.time 的一部分。 But it is intended for a full time zone.但它适用于全时区。 Your input string has merely an offset-from-UTC .您的输入字符串只有一个offset-from-UTC A full time zone, in contrast, is a collection of offsets in effect for a region at different points in time, past, present, and future.相比之下,完整时区是一个区域在过去、现在和未来的不同时间点有效的偏移量集合。 For example, with Daylight Saving Time (DST) in most of North America, the offsets change twice a year growing smaller in the Spring as we shift clocks forward an hour, and restoring to a longer value in the Autumn when we shift clocks back an hour.例如,在北美大部分地区的夏令时 (DST) 中,偏移量每年更改两次,当我们将时钟向前移动一个小时时,偏移量在春季变小,而当我们将时钟向后移动一个小时时,偏移量会在秋季恢复到更长的值。小时。

OffsetDateTime

For only an offset rather than a time zone, use the OffsetDateTime class.对于仅偏移量而不是时区,请使用OffsetDateTime类。

Your input string complies with the ISO 8601 standard.您的输入字符串符合ISO 8601标准。 The java.time classes use the standard formats by default when parsing/generating strings. java.time 类在解析/生成字符串时默认使用标准格式。 So no need to specify a formatting pattern.所以不需要指定格式模式。

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

Well, that should have worked.嗯,那应该有效。 Unfortunately, there is a bug in Java 8 (at least up through Java 8 Update 121) where that class fails to parse an offset omitting the colon between hours and minutes.不幸的是, Java 8 中存在一个错误(至少到 Java 8 Update 121),该类无法解析忽略小时和分钟之间冒号的偏移量。 So the bug bites on +0000 but not +00:00 .所以臭虫咬住了+0000而不是+00:00 So until a fix arrives, you have a choice of two workarounds: (a) a hack, manipulating the input string, or (b) define an explicit formatting pattern.因此,在修复到来之前,您可以选择两种解决方法:(a) hack,操作输入字符串,或 (b) 定义显式格式模式。

The hack: Manipulate the input string to insert the colon.技巧:操作输入字符串以插入冒号。

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

DateTimeFormatter

The more robust workaround is to define and pass a formatting pattern in a DateTimeFormatter object.更可靠的解决方法是在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 odt.toString(): 2018-02-13T10:20:12.120Z

By the way, here is a tip: I have found that with many protocols and libraries, your life is easier if your offsets always have the colon, always have both hours and minutes (even if minutes are zero), and always use a padding zero ( -05:00 rather than -5 ).顺便说一句,这里有一个提示:我发现对于许多协议和库,如果您的偏移量总是带有冒号,总是有小时和分钟(即使分钟为零),并且总是使用填充,那么您的生活会更轻松零( -05:00而不是-5 )。

DateTimeFormatterBuilder

For a more flexible formatter, created via DateTimeFormatterBuilder , see this excellent Answer on a duplicate Question.有关通过DateTimeFormatterBuilder创建的更灵活的格式化程序,请参阅有关重复问题的优秀答案

Instant

If you want to work with values that are always in UTC (and you should), extract an Instant object.如果您想使用始终为 UTC 的值(您应该这样做),请提取Instant对象。

Instant instant = odt.toInstant();

ZonedDateTime

If you want to view that moment through the lens of some region's wall-clock time , apply a time zone.如果您想通过某个地区挂钟时间的镜头查看那一刻,请应用时区。

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

See this code run live at IdeOne.com .查看此代码在 IdeOne.com 上实时运行

All of this has been covered many times in many Answers for many Questions.所有这些都在许多问题的许多答案中多次提及。 Please search Stack Overflow thoroughly before posting.请在发帖前彻底搜索 Stack Overflow。 You would have discovered many dozens, if not hundreds, of examples.你会发现几十个甚至几百个例子。


About java.time关于java.time

The java.time framework is built into Java 8 and later. java.time框架内置于 Java 8 及更高版本中。 These classes supplant the troublesome old legacy date-time classes such as java.util.Date , Calendar , & SimpleDateFormat .这些类取代麻烦的老传统日期时间类,如java.util.DateCalendar ,和SimpleDateFormat

The Joda-Time project, now in maintenance mode , advises migration to the java.time classes.现在处于维护模式Joda-Time项目建议迁移到java.time类。

To learn more, see the Oracle Tutorial .要了解更多信息,请参阅Oracle 教程 And search Stack Overflow for many examples and explanations.并在 Stack Overflow 上搜索许多示例和解释。 Specification is JSR 310 .规范是JSR 310

You may exchange java.time objects directly with your database.您可以直接与您的数据库交换java.time对象。 Use a JDBC driver compliant with JDBC 4.2 or later.使用符合JDBC 4.2或更高版本的JDBC 驱动程序 No need for strings, no need for java.sql.* classes.不需要字符串,不需要java.sql.*类。

Where to obtain the java.time classes?从哪里获得 java.time 类?

The ThreeTen-Extra project extends java.time with additional classes. ThreeTen-Extra项目用额外的类扩展了 java.time。 This project is a proving ground for possible future additions to java.time.该项目是未来可能添加到 java.time 的试验场。 You may find some useful classes here such as Interval , YearWeek , YearQuarter , and more .您可以在这里找到一些有用的类,比如IntervalYearWeekYearQuarter ,和更多

Short: Not a bug, just your pattern is wrong.简短:不是错误,只是您的模式错误。

Please use the type OffsetDateTime which is especially designed for time zone offsets and use a pattern this way:请使用专为时区偏移设计的类型OffsetDateTime并以这种方式使用模式:

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

Problems in detail:问题详细:

a) 12-hour-clock versus 24-hour-clock a) 12 小时制与 24 小时制

"h" indicates the hour of AM/PM on a 12-hour-clock but you obviously need "H" for the 24-hour-clock as required by ISO-8601. “h”表示 12 小时制的 AM/PM 小时,但显然您需要按照 ISO-8601 的要求使用“H”来表示 24 小时制。

b) The form of zero offset b) 零偏移的形式

If you want to parse zero offset like "+0000" instead of "Z" (as described in ISO-paper) you should not use the pattern symbol "X" but "ZZZ".如果您想解析零偏移,如“+0000”而不是“Z”(如 ISO 论文中所述),则不应使用模式符号“X”而应使用“ZZZ”。 Citing the pattern syntax :引用模式语法

Offset Z: This formats the offset based on the number of pattern letters.偏移 Z:这会根据模式字母的数量格式化偏移量。 One, two or three letters outputs the hour and minute, without a colon, such as '+0130'.一、二或三个字母输出小时和分钟,不带冒号,例如“+0130”。 The output will be '+0000' when the offset is zero.当偏移量为零时,输出将为“+0000”。

c) Your input is NOT ISO-8601-compatible therefore no bug in Java c) 您的输入与 ISO-8601 不兼容,因此在 Java 中没有错误

Your assumption that "2018-02-13T10:20:12.120+0000" shall be valid ISO is wrong because you are mixing basic format (in the offset part) and extended format which is explicitly prohibited in ISO-paper (see sections 4.3.2 (example part) and 4.3.3d).您认为“2018-02-13T10:20:12.120+0000”应为有效 ISO 的假设是错误的,因为您混合了基本格式(在偏移部分)和 ISO 论文中明确禁止的扩展格式(请参阅第 4.3 节。 2(示例部分)和 4.3.3d)。 Citing ISO-8601:引用 ISO-8601:

[...]the expression shall either be completely in basic format, in which case the minimum number of separators necessary for the required expression is used, or completely in extended format[...] [...]表达式要么完全采用基本格式,在这种情况下使用所需表达式所需的最少分隔符,或者完全采用扩展格式[...]

The statement of B. Bourque that java.time has a bug is based on the same wrong expectation about ISO-compatibility. B. Bourque 关于java.time存在错误的声明是基于对 ISO 兼容性的同样错误预期。 And the documentation of let's say ISO_OFFSET_DATE_TIME describes the support of the extended ISO-format only.假设ISO_OFFSET_DATE_TIME的文档ISO_OFFSET_DATE_TIME描述了对扩展 ISO 格式的支持。 See also the related JDK issue .另请参阅相关的JDK 问题 Not all ISO-8601-variants are directly supported hence a pattern-based construction of the parser in the right way is okay.并非所有 ISO-8601 变体都得到直接支持,因此以正确的方式构建基于模式的解析器是可以的。

if offset +0000 try this如果偏移 +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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM