迁移到Java 17后,日期显示不准确

Viv*_*dev 4 java timezone mongodb java.util.date java-17

我们最近将应用程序从 Java 11 过渡到 Java 17。部署后,我们注意到某些记录存在差异,其中日期显示有一日差异 (+1)。这种情况尤其发生在 1940 年之前的记录中。我们的应用程序在 CEST 时区运行,日期在 MongoDB 中存储为 UTC。以下示例重点介绍了我们已发现问题的实例。

D B 爪哇11 爪哇17
1934-07-21T22:40:28.000+00:00 1934年7月22日 1934年7月21日
1897-08-06T23:40:28.000+00:00 1897-08-07 1897-08-06

提供的代码片段是用 Kotlin 编写的,在 Java 11 中产生令人满意的结果。但是,当使用 Java 17 执行时,相同的代码无法按预期运行。

fun main() {  // Kotlin code
    val date = Date(-1118625572000) //1934-07-21T22:40:28.000+00:00
    println("Date: ${date.toInstant()?.atZone(ZoneId.of("Europe/Amsterdam"))!!.toLocalDate()}")
}
Run Code Online (Sandbox Code Playgroud)

Java11 输出:
代码输出显示“日期:1934-07-22”

Java17 输出:
代码输出显示“日期:1934-07-21”

Java 17 中是否有可用的解决方案来解决与这些类型的日期相关的问题?

我们尝试直接在数据库中更正这些记录,以确保有效日期的数量。

例如,

1934-07-21T22:40:28.000+00:00 --> 1934-07-22T00:00:00.000+00:00
1897-08-06T23:40:28.000+00:00 --> 1897-08-07T00:00:00.000+00:00
Run Code Online (Sandbox Code Playgroud)

然而,我们需要探索是否有一种方法可以在 Java 17 中专门处理这些类型的日期。

M. *_*tin 7

您看到的问题是由于两个版本的 Java 使用的时区数据库不同造成的。时区规则会随着时间的推移而更新,并且您遇到过在新规则下以不同方式处理的旧日期。

\n

对于我机器上的 Java 版本,Java 11 和 17 的值为 1934-07-22(时区数据库版本分别为 2020d 和 2021e),而 Java 21 的值为 1934-07-21(时区数据库版本) 2023c)。

\n

其具体原因是 2020d 和 2021e 在欧洲/阿姆斯特丹此时的区域偏移量为 +01:19:32。另一方面,2023c 在欧洲/阿姆斯特丹的区域偏移量为 +01:00。将 1:19:32 添加到 1934-07-21T22:40:28Z 会导致第二天的午夜 (1934-07-22),但添加 1:00:00 会导致同一天的 23:40:28 (1934-07-22) 07-21)。

\n

时区数据库更改

\n

我做了一些互联网调查,根据时区数据库邮件列表,看起来这个更改是在时区数据库版本 2022b 中进行的。因此,2022a 及更早版本使用 +01:19:32 偏移量,2022b 及更高版本使用 +01:00 偏移量。

\n

2022b 合并了阿姆斯特丹和布鲁塞尔时区,这两个时区在 1970 年之后是相同的,但在足够早的历史日期上有所不同。看起来阿姆斯特丹曾一度使用当地时间,因此出现了看起来很奇怪的偏移。然而,布鲁塞尔似乎没有。

\n
\n
\n

在 2022a 和 b 之间,删除欧洲/阿姆斯特丹的时区信息:
\n\xe2\x80\x82\xe2\x80\x82\xe2\x80\x82\xe2\x80\x82\xe2\x80\x82\xe2\x80 \x82Z 欧洲/阿姆斯特丹 0:19:32 - LMT 1835
\n该日期前后布鲁塞尔的时区信息与阿姆斯特丹的时区信息不同。

\n

在 python 中确定我的 Linux 系统上该时期周围日期的时区信息,自最新更新以来返回的偏移量为 0 分钟,而不是之前的 19 分钟。

\n

删除此时区信息是有意的还是错误?

\n
\n

维护者通过使用阿姆斯特丹的布鲁塞尔规则合并了阿姆斯特丹和布鲁塞尔时区信息,因为这些规则自 1970 年以来就没有变化,在默认数据包中。这消除了 1970 年之前的阿姆斯特丹规则,包括当地时间。

\n
\n

示范

\n
String javaVersion = System.getProperty("java.vendor") + Runtime.version();\nString timeZoneDatabase = ZoneRulesProvider.getVersions("UTC")\n        .keySet().stream().findFirst().orElseThrow();\nDate date = new Date(-1118625572000L);\nZoneId timeZone = ZoneId.of("Europe/Amsterdam");\nZonedDateTime zonedDateTime = date.toInstant().atZone(timeZone);\nLocalDate localDate = zonedDateTime.toLocalDate();\n\nSystem.out.printf("Java: %s%n", javaVersion);\nSystem.out.printf("Time Zone Database: %s%n", timeZoneDatabase);\nSystem.out.printf("Instant (UTC): %s%n", date.toInstant());\nSystem.out.printf("LocalDate (Europe/Amsterdam): %s%n", localDate);\nSystem.out.printf("Time zone offset: %s%n", zonedDateTime.getOffset());\n
Run Code Online (Sandbox Code Playgroud)\n

输出

\n
Java: AdoptOpenJDK11.0.10+9\nTime Zone Database: 2020d\nInstant (UTC): 1934-07-21T22:40:28Z\nLocalDate (Europe/Amsterdam): 1934-07-22\nTime zone offset: +01:19:32\n
Run Code Online (Sandbox Code Playgroud)\n
Java: Eclipse Adoptium17.0.2+8\nTime Zone Database: 2021e\nInstant (UTC): 1934-07-21T22:40:28Z\nLocalDate (Europe/Amsterdam): 1934-07-22\nTime zone offset: +01:19:32\n
Run Code Online (Sandbox Code Playgroud)\n
Java: Eclipse Adoptium21.0.1+12-LTS\nTime Zone Database: 2023c\nInstant (UTC): 1934-07-21T22:40:28Z\nLocalDate (Europe/Amsterdam): 1934-07-21\nTime zone offset: +01:00\n
Run Code Online (Sandbox Code Playgroud)\n