您必须将格式更改为:
String format = "yyyy-MM-dd'T'HH:mm:ss[.S]'T'[zzz][xxx]";
[zzz] 和[xxx] 都在可选部分中,因为zzz 可以解析整个GMT+03:00 部分或仅解析区域短名称(例如CDT),而xxx 仅解析偏移量部分(例如-05:00 - 因此如果找到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]
PS:此格式化程序的唯一问题是,在格式化时,它会打印所有可选部分。所以,如果你这样做:
String date = "2017-07-05T12:28:36.4TGMT+03:00";
ZonedDateTime z = ZonedDateTime.parse(date, formatter);
System.out.println(formatter.format(z));
输出将是:
2017-07-05T12:28:36.4TGMT+03:00+03:00
这是因为GMT+03:00 是zzz 的结果,而第二个+03:00 是xxx 的结果。如果您不希望这样,我建议使用 2 个不同的 DateTimeFormatter(一个用于解析,另一个用于格式化)。
或者(一种“更丑”的方法),使用 2 种不同的格式化程序:
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");
然后,您尝试使用第一个进行解析 - 如果遇到异常,请尝试使用第二个(或者检查您的输入是否包含 GMT 以了解要使用哪个)。
我个人不喜欢这样,因为GMT 是区域名称的一部分,不应被视为文字。但最后,你会得到一个带有正确偏移量的ZonedDateTime,所以我不确定这种方法有多错误。
时区缩写
请注意,您应该(尽可能)避免使用 3 个字母的缩写(例如 CDT 或 PST),因为它们是 ambiguous and not standard。 CDT 可以同时是 Central Daylight Time (UTC-05:00)、Cuba Daylight Time (UTC-04:00) 甚至是 China Daylight Time (UTC+09:00)。
如果可能,最好使用IANA timezones names(始终采用Continent/City 的格式,例如America/Sao_Paulo 或Europe/Berlin)。根据该列表,有 40 多个时区使用(或在过去某处使用)CDT 缩写。
CDT 适用于这种情况,因为某些缩写配置了默认值,可能是出于复古兼容性的原因,但您不应该在所有情况下都依赖它们。
为确保您的时区缩写始终有效(以防万一您无法避免使用它们),您可以创建一个使用一组首选时区的格式化程序。在这种情况下,我使用的是 America/Chicago(因此,CST 或 CDT 将被解析为芝加哥的时区):
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();
此格式化程序的工作方式与上述相同,对于您的输入(有和没有GMT),当CDT 在输入中时,使用America/Chicago 作为默认时区。您可以根据自己的用例在集合中添加任意数量的区域。
只是提醒这个格式化程序在输出方面存在相同的问题(它打印所有可选部分),如上所述。