为什么 LocalDateTime 到 java.util.Date 的转换会转换为非常旧的日期?

Nap*_*Nap 5 java java.util.date java-time

我在将旧日期从 java.time.LocalDateTime 转换为 java.util.Date 时遇到问题

我尝试了很多变化,但它仍然具有相同的转移日期。我认为这是执行了一些奇怪的日历,但它失败了。

要解析的日期1800-01-01 00:00:00

我使用了一个非常简单的转换函数。

Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());

时区 a.通过SimpleDateFormat转换 b.通过DateFormatter将LocalDateTime转换为java.util.Date
亚洲/东京 1800-01-01 00:00:00 日本标准时间 1799-12-31 23:41:01 日本标准时间
欧洲/布鲁塞尔 欧洲中部时间 1800-01-01 00:00:00 欧洲中部时间 1800 年 1 月 1 日 00:04:30
澳大利亚/悉尼 1800-01-01 00:00:00 澳大利亚东部时间 1799-12-31 23:55:08 澳大利亚东部时间
世界标准时间 1800-01-01 00:00:00 世界标准时间 1800-01-01 00:00:00 世界标准时间

A。通过 SimpleDateFormat 将 String 转换为 java.util.Date

b. 通过DateFormatter将String转换为java.time.LocalDatetime,然后将其转换为java.util.Date

现在我发现它只适用于 UTC 时区,我不能只更改软件时区,因为它会扰乱其他时区。有人知道将 java.time.LocalDateTime 转换为 java.util.Date 的其他方法吗?

TimeZone.setDefault(TimeZone.getTimeZone("UTC"));

=====以下是在扫地者的回答后添加的,以说明它并不适合所有人====

奇怪的是,它只发生在非常旧的日期,它不会发生在 1900-01-01 00:00:00,但尚未检查问题是在哪一点开始的。我在想,也许是因为18XX年的某个时候进行了调整/改变。

System.out.println(ZoneId.of("Asia/Tokyo").getRules().getOffset(LocalDateTime.parse("1800-01-01T00:00:00")));
SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
System.out.println(TimeZone.getTimeZone("Asia/Tokyo").getOffset(format1.parse("1800-01-01T00:00:00").getTime()));
        
System.out.println(ZoneId.of("Asia/Tokyo").getRules().getOffset(LocalDateTime.parse("1900-01-01T00:00:00")));
SimpleDateFormat format2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
System.out.println(TimeZone.getTimeZone("Asia/Tokyo").getOffset(format2.parse("1900-01-01T00:00:00").getTime()));
Run Code Online (Sandbox Code Playgroud)

结果至

+09:18:59
32400000
+09:00
32400000
Run Code Online (Sandbox Code Playgroud)

Swe*_*per 6

与此问题类似,旧 API 不同意ZoneId.systemDefault()这些位置在 1800 年 1 月 1 日的偏移量。

您可以看到它的实际效果:

System.out.println(ZoneId.of("Asia/Tokyo").getRules().getOffset(LocalDateTime.parse("1800-01-01T00:00:00")));
var format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
System.out.println(TimeZone.getTimeZone("Asia/Tokyo").getOffset(format.parse("1800-01-01T00:00:00").getTime()));
Run Code Online (Sandbox Code Playgroud)

输出:

+09:18:59
32400000
Run Code Online (Sandbox Code Playgroud)

正如链接帖子中所述,+09:18:59 是日本当地时间,32400000ms 正好是 9 小时。这是因为旧的 API 不支持 Local Mean Time。请注意,日本于 1888 年标准化了时区,这解释了为什么两个 API 的输出在 1900-01-01 是一致的。

因此,ZoneId认为亚洲/东京的偏移量比(由等TimeZone使用的)所认为的多 18 分 59 秒。SimpleSateFormatDate.toString

这正是输出中存在“偏移”的原因。ldt.atZone(...)产生

1800-01-01T00:00:00+09:18:59
Run Code Online (Sandbox Code Playgroud)

并将toInstant其变成瞬间。

假设您正在使用Date.toString或其他一些用于TimeZone生成字符串的旧 API,旧 API 会认为该时刻是在 +09:00:00!上述日期 +09:00:00 是哪一天?好吧,只需减去 18 分 59 秒(就像减去 9 小时以将 UTC+9 日期转换为 UTC+0 一样)!

这就是你得到的:

1799-12-31 23:41:01
Run Code Online (Sandbox Code Playgroud)

至于解决方案,Date你得到的“转移”确实没有任何问题。如果您只是将其转换回 aZonedDateTime并将其格式化以DateTimeFormatter供显示,则一切都应该正常工作。事实上,如果可以的话,请考虑Date根本不转换为,而使用Instants 代替。

如果您做不到这一点,我建议您不要混合使用这两个 API。

  • @Nap我添加了一个关于“TimeZone”不支持本地平均时间的错误报告。由于兼容性问题,他们不会修复它。 (2认同)