【问题标题】:ZonedDateTime change behavior jdk 8/11ZonedDateTime 更改行为 jdk 8/11
【发布时间】:2019-10-08 20:03:30
【问题描述】:

我正在将应用程序从 jdk 8 迁移到 11,我可以看到 ZonedDateTime 更改是关于夏令时的行为。 JDK8

        ZonedDateTime parse = ZonedDateTime.parse("2037-05-10T19:15:00.000+01:00[Europe/Paris]");
        System.out.println(parse);

输出: 2037-05-10T19:15+02:00[欧洲/巴黎]

JDK11/12

        ZonedDateTime parse = ZonedDateTime.parse("2037-05-10T19:15:00.000+01:00[Europe/Paris]");
        System.out.println(parse);

2037-05-10T20:15+02:00[欧洲/巴黎]

有人可以向我解释为什么他们会改变这种行为吗?

最好的问候,

【问题讨论】:

  • 使用 AdoptOpenJDK 11.0.3 我得到2037-05-10T20:15+02:00[Europe/Paris]
  • 对不起,我弄混了我的 sn-p。因此,由于夏季夏令时,使用 Jdk11/12 确实会增加一小时。在 jdk 8 上它是相同的日期时间 2037-05-10T19:15:00
  • 欧盟似乎在2021年可能会放弃夏令时(DST)。你知道2037年10月巴黎是偏移+01:00还是+02:00?我怀疑它还没有决定。
  • 底层时区数据库在您使用的两个版本之间发生了变化。关注 this question 以查看您使用的数据库版本,然后搜索 the tz-announce archive 以查看这些版本之间的变化。

标签: java java-8 java-11 zoneddatetime java-12


【解决方案1】:

这是 Java 8 中的一个已知错误:JDK-8066982

我相信您在 Java 8 中遇到的确实是这个错误:ZonedDateTime.parse() returns wrong ZoneOffset around DST fall transition。错误标题并不能说明全部情况。真正的问题是,在 Java 8 中,DateTimeFormatter.ISO_ZONED_DATE_TIME(由您使用的单参数 ZonedDateTime.parse 隐式使用)在解析的字符串中包含时区 ID 时会忽略偏移量。这与一个时区数据库不同意您关于 2037 年 10 月在巴黎使用的偏移量的字符串的时区数据库会导致解析的某个时刻与字符串中的偏移量冲突。

这个 bug 在 Java 9 中已经修复。所以在 Java 9、10 和 11 中,由于关于偏移量的相同分歧仍然存在,所以解析的时刻是基于字符串的偏移量。然后使用时区数据库中的规则将其从字符串转换为时区。这会导致偏移量从 +01:00 更改为 +02:00,并且一天中的小时相应地从 19:15 更改为 20:15。我同意 Java 9+,这是正确的行为。

不要将 ZonedDateTime 用于遥远的未来日期

您的问题也部分是由于使用ZonedDateTime 作为未来的日期。这只建议在不久的将来使用,我们假设没有更改区域规则。对于 2037 年的日期和时间,如果您知道具体时间,则应使用 Instant;如果您只知道日期和时间,则应使用 LocalDateTime。只有当时间临近并且您相信您的 Java 安装已获得最后一次时区更新时,才转换为 ZonedDateTime

正如 cmets 中所讨论的,我们可能还不知道 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]

【讨论】:

    猜你喜欢
    • 2020-09-05
    • 1970-01-01
    • 1970-01-01
    • 2022-01-10
    • 1970-01-01
    • 1970-01-01
    • 2020-05-28
    • 2020-06-17
    • 2022-12-05
    相关资源
    最近更新 更多