添加到 DateTimeFormatterBuilder 的文字破折号导致解析失败

Literal dash added to DateTimeFormatterBuilder causes parsing to fail

我正在尝试创建一个 DateTimeFormatter 来匹配以下示例(它实际上比这个稍微复杂一些,但这应该无关紧要)。

20180302-17:45:21

我写了以下内容,但它导致异常:

new DateTimeFormatterBuilder()
    .append(DateTimeFormatter.BASIC_ISO_DATE)
    .appendLiteral('-')
    .append(DateTimeFormatter.ISO_LOCAL_TIME)
    .toFormatter()
    .parse("20180302-17:45:21");

例外情况是:

Exception in thread "main" java.time.format.DateTimeParseException: Text '20180302-17:45:21' could not be parsed at index 11
    at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1988)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1816)

17:45DateTimeFormatterBuilder.appendLiteral 之间的冒号似乎失败了,没有提供任何线索。

如果我将字面量更改为另一个字符,比方说 m,那么它工作正常:

new DateTimeFormatterBuilder()
    .append(DateTimeFormatter.BASIC_ISO_DATE)
    .appendLiteral('m')
    .append(DateTimeFormatter.ISO_LOCAL_TIME)
    .toFormatter()
    .parse("20180302m17:45:21");

这是怎么回事?假设我无法更改格式,我该如何修复它?

评论表明这可能与版本有关。我正在使用 JDK 9.0.1,它是 .

这与 DateTimeFormatter.BASIC_ISO_DATE 包含一个可选的偏移量 ID 这一事实有关。显然,您的格式化程序将 -17 解析为偏移量,然后解析为对象,因为在格式需要连字符的地方有一个冒号。

当您改用 m 时,它不能被解析为偏移量,因此与格式中的文字 m 相匹配,并且一切正常。

我尝试使用大写 ZZ 也可以是偏移量 ID。

new DateTimeFormatterBuilder()
    .append(DateTimeFormatter.BASIC_ISO_DATE)
    .appendLiteral('Z')
    .append(DateTimeFormatter.ISO_LOCAL_TIME)
    .toFormatter()
    .parse("20180302Z17:45:21");

现在我得到了 java.time.format.DateTimeParseException: Text '20180302Z17:45:21' could not be parsed at index 9。在 Z 之后索引 9 us,因此格式化程序似乎解析了偏移量,然后尝试在 17 所在的位置找到文字 Z

编辑:解决方案呢?而不是使用 BASIC_ISO_DATE 附加模式:

.appendPattern("uuuuMMdd")

现在解析也适用于 Java 9.0.4.

编辑:进一步说明偏移量的可选性:

System.out.println(
    LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE)
);
System.out.println(
    OffsetDateTime.now().format(DateTimeFormatter.BASIC_ISO_DATE)
);

此打印

20180305
20180305+0100

所以在第一种情况下,没有可用的偏移量,它只是将其排除在外。在第二种情况下,如果有的话,也会打印出来(不带冒号)。

未决问题:为什么它在 Java 8 中有效?这真的是一个错误吗?

引用:

  • If the offset is not available to format or parse then the format is complete.
  • The offset ID without colons. If the offset has seconds then they will be handled even though this is not part of the ISO-8601 standard. The offset parsing is lenient, which allows the minutes and seconds to be optional. Parsing is case insensitive.

来自 the documentation of BASIC_ISO_DATE

我将其作为错误提出,并已在 JDK-8199412 中得到确认。