ZonedDateTime 和 OffsetDateTime 有什么区别?

What's the difference between ZonedDateTime and OffsetDateTime?

我已经阅读了文档,但我仍然不知道什么时候应该使用其中之一:

根据文档,将日期写入数据库时​​应使用 OffsetDateTime,但我不明白为什么。

Q: What's the difference between java 8 ZonedDateTime and OffsetDateTime?

javadocs 是这样说的:

"OffsetDateTime, ZonedDateTime and Instant all store an instant on the time-line to nanosecond precision. Instant is the simplest, simply representing the instant. OffsetDateTime adds to the instant the offset from UTC/Greenwich, which allows the local date-time to be obtained. ZonedDateTime adds full time-zone rules."

来源:https://docs.oracle.com/javase/8/docs/api/java/time/OffsetDateTime.html

因此 OffsetDateTimeZonedDateTime 之间的区别在于后者包括涵盖夏令时调整和各种其他异常的规则。

简单地说:

Time Zone = ( Offset-From-UTC + Rules-For-Anomalies )


Q: According to documentation OffsetDateTime should be used when writing date to database, but I don't get why.

具有本地时间偏移的日期始终代表相同的时刻,因此具有稳定的排序。相比之下,具有完整时区信息的日期的含义在面对各自时区规则的调整时是不稳定的。 (这些确实发生了;例如,对于未来的日期时间值。)因此,如果您存储然后检索 ZonedDateTime,则实现会出现问题:

  • 它可以存储计算出的偏移量...然后检索到的对象可能具有与当前区域 ID 规则不一致的偏移量。

  • 它可以丢弃计算出的偏移量...然后检索到的对象代表绝对/通用时间轴中与存储的时间点不同的点。

如果使用Java对象序列化,Java9实现采用第一种方法。这可以说是 "more correct" 处理此问题的方法,但这似乎没有记录在案。 (JDBC 驱动程序和 ORM 绑定可能会做出类似的决定,并希望能正确处理。)

但是,如果您正在编写手动存储 date/time 值或依赖 java.sql.DateTime 的应用程序,那么处理区域 ID 的复杂性是……可能需要解决的问题避免了。因此建议。

请注意,随着时间的推移,其含义/排序不稳定的日期可能对应用程序有问题。由于更改区域规则是一种边缘情况,因此问题很可能会在意想不到的时候出现。


这个建议的(可能的)第二个原因是 ZonedDateTime 的构造在某些点上是模棱两可的。例如,在您 "putting the clocks back" 的时间段内,结合本地时间和区域 ID 可以为您提供两个不同的偏移量。 ZonedDateTime 将始终选择一个而不是另一个...但这并不总是正确的选择。

现在,对于以这种方式构造 ZonedDateTime 值的任何应用程序来说,这可能是个问题。但是从构建企业应用程序的人的角度来看,当(可能不正确的)ZonedDateTime 值是持久的并在以后使用时,这是一个更大的问题。

接受的答案给出了非常完整的解释,也许下面的代码示例可以为您提供简短清晰的图片:

Instant instant = Instant.now();
Clock clock = Clock.fixed(instant, ZoneId.of("America/New_York"));
OffsetDateTime offsetDateTime = OffsetDateTime.now(clock);
ZonedDateTime zonedDateTime = ZonedDateTime.now(clock);

System.out.println(offsetDateTime); // 2019-01-03T19:10:16.806-05:00
System.out.println(zonedDateTime);  // 2019-01-03T19:10:16.806-05:00[America/New_York]
System.out.println();

OffsetDateTime offsetPlusSixMonths = offsetDateTime.plusMonths(6);
ZonedDateTime zonedDateTimePlusSixMonths = zonedDateTime.plusMonths(6);

System.out.println(offsetPlusSixMonths); // 2019-07-03T19:10:16.806-05:00
System.out.println(zonedDateTimePlusSixMonths); // 2019-07-03T19:10:16.806-04:00[America/New_York]
System.out.println(zonedDateTimePlusSixMonths.toEpochSecond() - offsetPlusSixMonths.toEpochSecond()); // -3600

System.out.println();
System.out.println(zonedDateTimePlusSixMonths.toLocalDateTime()); // 2019-07-03T19:10:16.806
System.out.println(offsetPlusSixMonths.toLocalDateTime()); // 2019-07-03T19:10:16.806

简而言之,仅当您要考虑夏令时时才使用 ZonedDateTime,通常会有一小时的差异,正如您在上面的示例中看到的那样,ZonedDateTime 的偏移量从-5:00-04:00,在大多数情况下,您的业务逻辑最终可能会出现错误。

(代码复制自 https://www.youtube.com/watch?v=nEQhx9hGutQ