【问题标题】:What's the difference between ZonedDateTime and OffsetDateTime?ZonedDateTime 和 OffsetDateTime 有什么区别?
【发布时间】:2015-05-14 10:08:40
【问题描述】:

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

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

【问题讨论】:

  • 基本上ZonedDateTime 还包含有关时区的信息,包括我阅读的夏令时切换等。

标签: java java-8 java-time


【解决方案1】:

问:java 8 ZonedDateTime 和 OffsetDateTime 有什么区别?

javadocs 是这样说的:

"OffsetDateTimeZonedDateTimeInstant 都在时间线上以纳秒精度存储一个瞬间。Instant 是最简单的,只是表示瞬间。OffsetDateTime 添加到瞬间与 UTC/格林威治的偏移量,允许获取本地日期时间。ZonedDateTime 添加完整的时区规则。"

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

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

简单地说:

Time Zone = (Offset-From-UTC + 异常规则)


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

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

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

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

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

但是,如果您正在编写一个手动存储日期/时间值的应用程序,或者依赖于java.sql.DateTime,那么处理 zone-id 的复杂性是......可能需要避免的事情。因此建议。

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


建议的第二个(可能)原因是ZonedDateTime 的构造在某些点上是模棱两可的。例如,在您“拨回时钟”的时间段内,结合本地时间和 zone-id 可以为您提供两个不同的偏移量。 ZonedDateTime 将始终选择一个而不是另一个...但这并不总是正确的选择。

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

【讨论】:

    【解决方案2】:

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

    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 更改为@987654326 @,在大多数情况下,您的业务逻辑可能最终会出现错误。

    (来自https://www.youtube.com/watch?v=nEQhx9hGutQ的代码副本)

    【讨论】:

    • 我会采取相反的方式:正确使用的方法通常是 either InstantZonedDateTime。 IMO OffsetDateTime 之所以存在,是因为许多现有系统(错误地)将“时间偏移”视为有关时区的足够信息(即,它们在应该传达时区名称时仅传达/存储/处理偏移量)。
    • @JoachimSauer 当我们为用户格式化消息或阅读他们的输入时,我们使用ZonedDateTime & ZoneId。当我们将时间事件存储到我们转换为 UTC 的 DB 时,OffsetDateTime 基本上是一个瞬间。 UTC 日期很容易比较(避免 TZ 信息查找)。
    猜你喜欢
    • 2019-07-21
    • 1970-01-01
    • 2010-10-02
    • 2011-12-12
    • 2010-09-16
    • 2012-03-14
    • 2012-02-06
    • 2011-02-25
    • 2011-11-22
    相关资源
    最近更新 更多