Java 8 DateTimeFormatter使用偏移拒绝正确的ISO 8601日期/时间

Vic*_*lov 4 java time date iso8601

我使用DateTimeFormatter的格式字符串:uuuu-MM-dd'T'HH:mm:ssX

必须支持所有可能的时区偏移格式,包括:Z,00,00:00,0000

根据官方的DateTimeFormatter文档,'X'限定符必须与这些格式的偏移匹配:

X区偏移'Z'表示零偏移-X Z; -08; -0830; -08:30; -083015; -08:30:15;

但事实上,事实并非如此

输入字符串:"2014-01-01T00:30:00 + 00:00"

result: java.time.format.DateTimeParseException:无法解析文本'2014-01-01T00:30:00 + 00:00',在索引22处找到未解析的文本

输入字符串:"2014-01-01T00:30:00Z"

结果:正确

代码:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ssX");
OffsetDateTime parsed = OffsetDateTime.parse(dateTimeAsString, formatter);
Run Code Online (Sandbox Code Playgroud)

JDK 1.8.0_192(Oracle,而不是OpenJDK)

Ole*_*.V. 6

这有点复杂.正如jvdmr所说,X的数量很重要.XXXXX会认出来-08:30:15,但不是-083015.XXXX将承认后者,但不承认前者.

为了考虑所有可能的示例格式,我们需要指定不同的可能性.这可以使用方括号在格式模式字符串中完成.这些包含可选部件.一些实验表明,以下模式涵盖了所有示例:

uuuu-MM-dd'T'HH:mm:ss[XXXXX][XXXX][X]

我们来试试吧:

    DateTimeFormatter formatter
            = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss[XXXXX][XXXX][X]");
    for (String dts : new String[] {
            "2014-01-01T00:30:00-08:30:15", "2014-01-01T00:30:00-083015",
            "2014-01-01T00:30:00-08:30", "2014-01-01T00:30:00-0830",
            "2014-01-01T00:30:00-08", "2014-01-01T00:30:00Z",
    }) {
        System.out.println(OffsetDateTime.parse(dts, formatter));
    }
Run Code Online (Sandbox Code Playgroud)

此代码段的输出是:

2014-01-01T00:30-08:30:15
2014-01-01T00:30-08:30:15
2014-01-01T00:30-08:30
2014-01-01T00:30-08:30
2014-01-01T00:30-08:00
2014-01-01T00:30Z
Run Code Online (Sandbox Code Playgroud)

编辑

VelNaga建议不要对ISO日期时间格式进行硬编码.由于编写格式模式字符串容易出错,因此这可能是一个好主意.例如:

    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
            .appendPattern("[XXXXX][XXXX][X]")
            .toFormatter();
Run Code Online (Sandbox Code Playgroud)

使用此格式化程序的输出与使用上述格式化程序的输出相同.它更啰嗦,但我们很容易认为这是值得的,因为它不易出错,可能更清晰,更容易阅读.


jvd*_*dmr 5

文档 (重点是我的)

偏移X和x:这将根据图案字母的数量设置偏移格式除非分钟非零,否则一个字母仅输出小时,例如“ +01”,在这种情况下,分钟也会输出,例如“ +0130”。两个字母输出小时和分钟,不带冒号,例如'+0130'。三个字母输出小时和分钟,并带有冒号,例如“ +01:30”。四个字母输出小时和分钟,可选秒,不带冒号,例如'+013015'。五个字母输出小时和分钟,可选秒,并带有冒号,例如“ +01:30:15”。六个或更多字母会引发IllegalArgumentException。当要输出的偏移量为零时,模式字母“ X”(大写)将输出“ Z”,而模式字母“ x”(小写)将输出“ +00”,“ + 0000”或“ +00” :00'。

这对于解析日期也相反。您要同时分析是否包含冒号,这意味着您必须使用可选部分,因为没有单个模式可以支持。试试这个模式:"uuuu-MM-dd'T'HH:mm:ss[XXX][XXXX]"