这是 Java 8 中的一个已知错误:JDK-8066982
我相信您在 Java 8 中遇到的确实是这个错误:ZonedDateTime.parse() 在 DST fall transition 周围返回错误的 ZoneOffset。错误标题并不能说明整个故事。真正的问题是,在 Java 8 中DateTimeFormatter.ISO_ZONED_DATE_TIME
(由您使用的 one-arg 隐式ZonedDateTime.parse
使用)如果解析字符串中包含时区 ID,则会忽略偏移量。这与时区数据库与您的字符串不同意关于 2037 年 10 月在巴黎使用的偏移量相结合会导致解析的某个时刻与字符串中的偏移量冲突。
该错误在 Java 9 中已修复。因此在 Java 9、10 和 11 中,由于仍然存在关于偏移量的相同分歧,因此解析的时刻是基于字符串的偏移量。然后使用时区数据库中的规则将其从字符串转换为时区。这会导致偏移量从 +01:00 更改为 +02:00,并且一天中的小时相应地从 19:15 更改为 20:15。我同意 Java 9+ 这是正确的行为。
不要将 ZonedDateTime 用于遥远的未来日期
您的问题也部分是由使用ZonedDateTime
未来日期引起的。这只建议在不久的将来使用,我们假设没有更改区域规则。Instant
对于 2037 年的日期和时间,如果您知道具体时间,则应使用 a,如果您只知道日期和时间,则应使用 a LocalDateTime
。只有当时间临近并且您相信您的 Java 安装已获得最后一次时区更新时,才转换为ZonedDateTime
.
正如评论中所讨论的,我们可能还不知道 2037 年 10 月巴黎的正确 UTC 偏移量。看来欧盟很有可能从2021年起放弃夏令时(DST),据我所知,法国政界人士还没有决定在那之后的法国时间。
如果我们想要字符串中的时间怎么办?
要从字符串 (19:15) 中获取时间,请解析为LocalDateTime
:
String zdtString = "2037-05-10T19:15:00.000+01:00[Europe/Paris]";
LocalDateTime dateTime
= LocalDateTime.parse(zdtString, DateTimeFormatter.ISO_ZONED_DATE_TIME);
System.out.println("Date and time from string: " + dateTime);
输出是(在 Java 11 上运行):
字符串中的日期和时间:2037-05-10T19:15
如果您希望在更高版本的 Java 中使用完整的 Java 8 行为——正如我所提到的,不推荐这样做,您不应该ZonedDateTime
在这里使用:
TemporalAccessor parsed = DateTimeFormatter.ISO_ZONED_DATE_TIME.parse(zdtString);
LocalDateTime dateTime = LocalDateTime.from(parsed);
ZoneId zone = ZoneId.from(parsed);
ZonedDateTime java8Zdt = dateTime.atZone(zone);
System.out.println("Time from string in zone from string: " + java8Zdt);
来自字符串的区域中的字符串时间:2037-05-10T19:15+02:00[Europe/Paris]