Java 8日期和时间:在偏移量中解析没有冒号的ISO 8601字符串

u_b*_*u_b 23 java datetime iso8601 datetime-parsing timezone-offset

我们尝试使用时区偏移量解析以下ISO 8601 DateTime字符串:

final String input = "2022-03-17T23:00:00.000+0000";

OffsetDateTime.parse(input);
LocalDateTime.parse(input, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
Run Code Online (Sandbox Code Playgroud)

由于时区偏移中的冒号,两种方法都失败(这OffsetDateTime也是有意义的DateTimeFormatter.ISO_OFFSET_DATE_TIME).

java.time.format.DateTimeParseException:无法在索引23处解析文本'2022-03-17T23:00:00.000 + 0000'

但根据维基百科,时区偏移有4种有效格式:

<time>Z 
<time>±hh:mm 
<time>±hhmm 
<time>±hh
Run Code Online (Sandbox Code Playgroud)

其他框架/语言可以解析这个字符串而没有任何问题,例如Javascript Date()或Jacksons ISO8601Utils(他们在这里讨论这个问题)

现在我们可以DateTimeFormatter使用复杂的RegEx 编写自己的,但在我看来,java.time库应该能够默认解析这个有效的ISO 8601字符串,因为它是有效的.

现在我们使用Jacksons ISO8601DateFormat,但我们更愿意使用官方date.time库来使用.你有什么方法可以解决这个问题?

小智 29

如果你想解析偏移(占所有有效格式Z,±hh:mm,±hhmm±hh),一个替代方法是使用一个java.time.format.DateTimeFormatterBuilder可选模式(可惜的是,似乎没有单一的模式字母来匹配他们全部):

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    // date/time
    .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
    // offset (hh:mm - "+00:00" when it's zero)
    .optionalStart().appendOffset("+HH:MM", "+00:00").optionalEnd()
    // offset (hhmm - "+0000" when it's zero)
    .optionalStart().appendOffset("+HHMM", "+0000").optionalEnd()
    // offset (hh - "Z" when it's zero)
    .optionalStart().appendOffset("+HH", "Z").optionalEnd()
    // create formatter
    .toFormatter();
System.out.println(OffsetDateTime.parse("2022-03-17T23:00:00.000+0000", formatter));
System.out.println(OffsetDateTime.parse("2022-03-17T23:00:00.000+00", formatter));
System.out.println(OffsetDateTime.parse("2022-03-17T23:00:00.000+00:00", formatter));
System.out.println(OffsetDateTime.parse("2022-03-17T23:00:00.000Z", formatter));
Run Code Online (Sandbox Code Playgroud)

上面的所有四个案例都会解析它2022-03-17T23:00Z.


如果需要,您还可以定义单个字符串模式,[]用于分隔可选部分:

// formatter with all possible offset patterns
DateTimeFormatter formatter = DateTimeFormatter
    .ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS[xxx][xx][X]");
Run Code Online (Sandbox Code Playgroud)

此格式化程序也适用于所有情况,就像上面的格式化程序一样.检查javadoc以获取有关每个模式的更多详细信息.


笔记:

  • 具有上述可选部分的格式化程序适用于解析,但不适用于格式化.格式化时,它将打印所有可选部分,这意味着它将多次打印偏移.因此,要格式化日期,只需使用另一个格式化程序.
  • 第二个格式化程序在小数点后正好接受3位数(因为.SSS).另一方面,ISO_LOCAL_DATE_TIME更灵活:秒和纳秒是可选的,它也接受小数点后的0到9位数.选择最适合您输入数据的那个.

  • 感谢您的输入,它目前可能是我所提问题的最佳解决方案。尽管我希望java.time库将来能为这种有效的ISO格式提供内置的DateTimeFormatter,而不要让我自己创建一个。 (2认同)

Jon*_*eet 6

您不需要编写复杂的正则表达式-您可以构建一个可以DateTimeFormatter轻松使用该格式的正则表达式:

DateTimeFormatter formatter =
    DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX", Locale.ROOT);

OffsetDateTime odt = OffsetDateTime.parse(input, formatter);
Run Code Online (Sandbox Code Playgroud)

这也将接受“ Z”而不是“ 0000”。它不会接受“ +00:00”(带有冒号或类似字符。对于文档来说,这是令人惊讶的,但是如果您的值始终带有UTC偏移而没有冒号,则应该可以。