我们最近将我们的应用程序从Java 11转换到Java 17.在部署之后,我们注意到某些记录显示日期有一天的差异(+1).这种情况特别出现在1940年以前的记录中.我们的应用程序在CEST时区运行,日期以UTC格式存储在MongoDB中.以下示例突出显示了我们已确定问题的实例.

DB Java 11 Java 17
1934-07-21T22:40:28.000+00:00 1934-07-22 1934-07-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()}")
}

Java11 Output:
Code output showing

Java17 Output:
Code output showing

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

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

推荐答案

您看到的问题是由于在两个版本的Java中使用了time zone database的差异.时区规则会随着时间的推移而更新,而您遇到的旧日期在新规则下会得到不同的处理.

对于我的机器上的Java版本,我得到的Java 11 17的

具体原因是2020D&Amp;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-21).

Time zone database change

我在互联网上做了一些调查,根据time zone database mailing list,这一更改似乎是在时区数据库版本2022b中进行的.因此,2022A和更早版本使用+01:19:32偏移,2022B和更高版本使用+01:00偏移.

2022年合并了阿姆斯特丹和布鲁塞尔的时区,这两个时区在1970年后是相同的,但在足够早的历史日期上有所不同.看起来阿姆斯特丹一度用了local mean time,因此出现了奇怪的偏移量.然而,布鲁塞尔似乎并没有这么做.

Between 2022a an b the timezone info for Europe/Amstedam is removed:
      Z Europe/Amsterdam 0:19:32 - LMT 1835
The timezone info for Brussels around that date differs from Amsterdams timezone info.

在我的Linux系统上,用python确定该时间段附近日期的时区信息,自最近一次更新以来,返回了0分钟的偏移量,而不是之前的19分钟.

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

维护商使用布鲁塞尔针对阿姆斯特丹的规则合并了阿姆斯特丹和布鲁塞尔的时区信息,因为自1970年以来,默认数据包中的规则没有任何不同.这抹go 了1970年之前阿姆斯特丹的规则,包括当地标准时间.

Demonstration

String javaVersion = System.getProperty("java.vendor") + Runtime.version();
String timeZoneDatabase = ZoneRulesProvider.getVersions("UTC")
        .keySet().stream().findFirst().orElseThrow();
Date date = new Date(-1118625572000L);
ZoneId timeZone = ZoneId.of("Europe/Amsterdam");
ZonedDateTime zonedDateTime = date.toInstant().atZone(timeZone);
LocalDate localDate = zonedDateTime.toLocalDate();

System.out.printf("Java: %s%n", javaVersion);
System.out.printf("Time Zone Database: %s%n", timeZoneDatabase);
System.out.printf("Instant (UTC): %s%n", date.toInstant());
System.out.printf("LocalDate (Europe/Amsterdam): %s%n", localDate);
System.out.printf("Time zone offset: %s%n", zonedDateTime.getOffset());

输出

Java: AdoptOpenJDK11.0.10+9
Time Zone Database: 2020d
Instant (UTC): 1934-07-21T22:40:28Z
LocalDate (Europe/Amsterdam): 1934-07-22
Time zone offset: +01:19:32
Java: Eclipse Adoptium17.0.2+8
Time Zone Database: 2021e
Instant (UTC): 1934-07-21T22:40:28Z
LocalDate (Europe/Amsterdam): 1934-07-22
Time zone offset: +01:19:32
Java: Eclipse Adoptium21.0.1+12-LTS
Time Zone Database: 2023c
Instant (UTC): 1934-07-21T22:40:28Z
LocalDate (Europe/Amsterdam): 1934-07-21
Time zone offset: +01:00

Java相关问答推荐

如何从片段请求数据到活动?在主要活动中单击按钮请求数据?

如何使用CSS为选定但未聚焦的表格行设置背景 colored颜色 ?

Javascript更新alert 可扩展内容样式与CSS—按钮更多/更少

空手道比赛条件

使用动态ID从json获取详细信息的Jolt规范

Java .类参数不通过构造函数传递

更新GWT 2.5.1到2.11.0和sencha GXT 3.1.1到4.1时出现错误

当返回Mono<;Something>;时,不会调用Mono<;void>;.flatMap

测试期间未执行开放重写方法

解释左移在Java中的工作原理

格式中的特定回录键-值对

为什么Spring Boot项目无法为基于MySQL的CRUD应用程序找到从JPARepository接口扩展的ProductRepository?

使用Jackson库反序列化json

Android Studio模拟器没有互联网

在JDK Flight Recorder中只记录单个线程

我的代码是线程安全的吗?[Java、CAS、转账]

为什么mvn编译生命周期阶段不只是编译已更改的java文件?

java21预览未命名的符号用于try-with-resources

@此处不能应用可为null的批注

将天数添加到ZonedDateTime不会更改时间