DateTimeFormatter 不解析自定义日期格式

end*_*yha 5 java datetime datetime-parsing java-time

我有一个关于 java 的问题DataTimeFormmater。我觉得我错过了一些东西,但又不知道到底错过了什么。

String format = "yyyy-MM-dd'T'HH:mm:ss[.S]'T'zxxx";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);

String date = "2017-07-05T12:28:36.4TGMT+03:00";

System.out.println(formatter.format(ZonedDateTime.now()));
System.out.println(formatter.parse(date));
Run Code Online (Sandbox Code Playgroud)

上面的代码生成当前的字符串ZonedDateTime,并尝试使用相同的日期格式化程序解析日期时间字符串。2017-07-05T06:07:51.0TCDT-05:00成功生成但无法解析的结果2017-07-05T12:28:36.4TGMT+03:00

我的目标是解析2017-07-05T12:28:36.4TGMT+03:00并提出适当的DateTimeFormatter.

小智 6

您必须将格式更改为:

String format = "yyyy-MM-dd'T'HH:mm:ss[.S]'T'[zzz][xxx]";
Run Code Online (Sandbox Code Playgroud)

[zzz][xxx]在可选部分中,因为zzz可以解析整个GMT+03:00部分或仅解析区域短名称(例如CDT),并且xxx仅解析偏移部分(例如- 因此如果找到-05:00a 则不需要)。GMT+03:00

只是提醒一下,formatter.parse(date)返回一个TemporalAccessor对象。如果你想创建一个特定的类型,最好使用该类各自的parse方法:

System.out.println(ZonedDateTime.parse(date, formatter)); // 2017-07-05T12:28:36.400+03:00[GMT+03:00]
Run Code Online (Sandbox Code Playgroud)

PS:此格式化程序的唯一问题是,格式化时,它会打印所有可选部分。所以,如果你这样做:

String date = "2017-07-05T12:28:36.4TGMT+03:00";
ZonedDateTime z  = ZonedDateTime.parse(date, formatter);
System.out.println(formatter.format(z));
Run Code Online (Sandbox Code Playgroud)

输出将是:

2017-07-05T12:28:36.4TGMT+03:00+03:00

这是因为GMT+03:00是 的结果zzz,第二个+03:00是 的结果xxx。如果您不想要这个,我建议使用 2 个不同的DateTimeFormatter'(一个用于解析,另一个用于格式化)。

或者(一种“丑陋”的方法),使用两种不同的格式化程序:

DateTimeFormatter noGMT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.S]'T'zzzxxx");
DateTimeFormatter gmt = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.S]'TGMT'xxx");
Run Code Online (Sandbox Code Playgroud)

然后,您尝试使用第一个进行解析 - 如果出现异常,请尝试使用第二个(或者检查您的输入是否包含GMT以了解要使用哪个)。

我个人不喜欢它,因为它GMT是区域名称的一部分,不应被视为文字。但最终,你得到了一个ZonedDateTime具有正确偏移量的 a ,所以我不确定这种方法有多么错误。


时区缩写

请注意,您应该(尽可能)避免使用 3 个字母的缩写(例如CDTPST),因为它们不明确且不标准CDT可以是Central Daylight Time(UTC-05:00)、Cuba Daylight Time(UTC-04:00) 甚至China Daylight Time(UTC+09:00)。

如果可能,最好使用IANA 时区名称Continent/City(始终采用America/Sao_Paulo或 等格式Europe/Berlin)。根据该列表,有超过 40 个时区使用(或过去曾在某个地方使用过)该CDT缩写。

CDT适用于这种情况,因为某些缩写配置了默认值,可能是由于向后兼容的原因,但您不应该在所有情况下都依赖它们。

为了确保您的时区缩写始终有效(如果您无法避免使用它们),您可以创建一个使用一组首选区域的格式化程序。在本例中,我正在使用America/Chicago(因此,CSTCDT将被解析为芝加哥的时区):

Set<ZoneId> preferedZones = new HashSet<>();
preferedZones.add(ZoneId.of("America/Chicago"));
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    // append first part of pattern (before timezone)
    .appendPattern("yyyy-MM-dd'T'HH:mm:ss[.S]'T'")
    // append zone name, use prefered zones (optional)
    .optionalStart().appendZoneText(TextStyle.SHORT, preferedZones).optionalEnd()
    // offset (optional)
    .appendPattern("[xxx]")
    // create formatter
    .toFormatter();
Run Code Online (Sandbox Code Playgroud)

对于您的输入(带和不带 ),此格式化程序的工作方式与上面相同GMT,并America/Chicago在以下情况下用作默认时区:CDT并且在输入中您可以根据您的用例在集合中添加任意数量的区域。

只是提醒一下,该格式化程序在输出方面存在相同的问题(它打印所有可选部分),如上所述。

  • 也可以使用“OOOO”作为时区,但在 Java 8 中解析存在错误,在 Java 9 中已更正:[JDK-8154050](http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8154050) (2认同)