ZonedDateTime 和 LocalDateTime 与 100 年前不同
ZonedDateTime & LocalDateTime are different for 100 years ago
我看到的行为非常奇怪 - 有时 LocalDateTime
等于 ZonedDateTime
,其他时候相差 1 或 2 小时,有时相差 30 分钟。所有这些奇怪的差异都取决于我减去的年份。有人可以解释发生了什么吗?尝试了 jdk1.8.0_65
和 jdk1.8.0_91
、MacOS 10.11.5
。我使用 UTC:
ZoneOffset offset = ZoneOffset.UTC;
这里有一些实验。对于 1919 值可能会相差纳秒或毫秒,这是预期的:
assertEquals(
LocalDateTime.now(offset).minusYears(85).toInstant(offset),
ZonedDateTime.now().minusYears(85).withZoneSameInstant(offset).toInstant());
1919 年相差 1 小时:
assertEquals(
LocalDateTime.now(offset).minusYears(86).toInstant(offset),
ZonedDateTime.now().minusYears(86).withZoneSameInstant(offset).toInstant());
Expected :<1930-05-28T20:19:10.383Z>
Actual :<1930-05-28T21:19:10.383Z>
对于 1920 时差 2 小时:
assertEquals(
LocalDateTime.now(offset).minusYears(95).toInstant(offset),
ZonedDateTime.now().minusYears(95).withZoneSameInstant(offset).toInstant());
Expected :<1921-05-28T20:21:45.094Z>
Actual :<1921-05-28T18:21:45.094Z>
对于 1921 再次毫秒或纳秒差异:
assertEquals(
LocalDateTime.now(offset).minusYears(96).toInstant(offset),
ZonedDateTime.now().minusYears(96).withZoneSameInstant(offset).toInstant());
最奇怪的是 - 1930 年 30 分钟的差异:
assertEquals(
LocalDateTime.now(offset).minusYears(97).toInstant(offset),
ZonedDateTime.now().minusYears(97).withZoneSameInstant(offset).toInstant());
Expected :<1919-05-28T20:24:27.345Z>
Actual :<1919-05-28T19:53:08.346Z>
更新
正如@Tunaki 指出的那样,我必须指定 ZonedDateTime
:
的偏移量
assertEquals(
LocalDateTime.now(offset).minusYears(95).toInstant(offset),
ZonedDateTime.now(offset).minusYears(95).withZoneSameInstant(offset).toInstant());
因为没有人回答这个...
创建 ZonedDateTime 时也必须发送偏移量。否则它将使用 Clock.systemDefaultZone()
并且时区会有所不同。
ZonedDateTime.now(offset).minusYears(95).withZoneSameInstant(offset).toInstant()
问题是它不知道时区:LocalDateTime.now(offset).minusYears(97).toInstant(offset)
。目前只有 offset。但这知道时区:ZonedDateTime.now().minusYears(97).toInstant()
ZoneId
包含地点信息和那个地方的时差。它知道 N 年前在那个特定时区的偏移量是 2 小时,而不是现在的 3 小时。
ZoneOffset
仅跟踪 hours/minutes 班次。它不知道特定国家的时间变化历史。它只是 "adds hours".
建议的(有点不正确)解决方案是让 ZonedDateTime
忘记区域并使用偏移量代替:ZonedDateTime.now(offset).minusYears(97)
。现在这将与具有相同偏移量的 LocalDateTime
一致 - 两者将显示相同的 不正确 信息。但他们会同意,因为 "just add hours" 而不是了解那个地方在那个历史点的时差:
ZoneOffset offset = ZoneId.of("Europe/Moscow").getRules().getOffset(Instant.now());
assertEquals(
LocalDateTime.now(offset).minusYears(97).toInstant(offset),
ZonedDateTime.now(offset).minusYears(97).toInstant());
或者我们可以设置 LocalDateTime
以显示那个时间那个地方的正确值:
ZonedDateTime zoned = ZonedDateTime.now(ZoneId.of("Europe/Moscow")).minusYears(97);
ZoneOffset offset = zoned.getOffset();//agrees with history
assertEquals(
LocalDateTime.now().minusYears(97).toInstant(offset),
zoned.toInstant());
PS:这一切都表明 ZonedDateTime
在不同情况下的工作方式不同 - 有时它知道时区,其他时候它只是 "adds hours" 就像你对 LocalDateTime 所做的那样手动设置偏移量。对我来说这是一个奇怪的实现。 JodaTime 可能仍然是最好的 Java 实现。至少不用学几天就能看懂。
我看到的行为非常奇怪 - 有时 LocalDateTime
等于 ZonedDateTime
,其他时候相差 1 或 2 小时,有时相差 30 分钟。所有这些奇怪的差异都取决于我减去的年份。有人可以解释发生了什么吗?尝试了 jdk1.8.0_65
和 jdk1.8.0_91
、MacOS 10.11.5
。我使用 UTC:
ZoneOffset offset = ZoneOffset.UTC;
这里有一些实验。对于 1919 值可能会相差纳秒或毫秒,这是预期的:
assertEquals(
LocalDateTime.now(offset).minusYears(85).toInstant(offset),
ZonedDateTime.now().minusYears(85).withZoneSameInstant(offset).toInstant());
1919 年相差 1 小时:
assertEquals(
LocalDateTime.now(offset).minusYears(86).toInstant(offset),
ZonedDateTime.now().minusYears(86).withZoneSameInstant(offset).toInstant());
Expected :<1930-05-28T20:19:10.383Z>
Actual :<1930-05-28T21:19:10.383Z>
对于 1920 时差 2 小时:
assertEquals(
LocalDateTime.now(offset).minusYears(95).toInstant(offset),
ZonedDateTime.now().minusYears(95).withZoneSameInstant(offset).toInstant());
Expected :<1921-05-28T20:21:45.094Z>
Actual :<1921-05-28T18:21:45.094Z>
对于 1921 再次毫秒或纳秒差异:
assertEquals(
LocalDateTime.now(offset).minusYears(96).toInstant(offset),
ZonedDateTime.now().minusYears(96).withZoneSameInstant(offset).toInstant());
最奇怪的是 - 1930 年 30 分钟的差异:
assertEquals(
LocalDateTime.now(offset).minusYears(97).toInstant(offset),
ZonedDateTime.now().minusYears(97).withZoneSameInstant(offset).toInstant());
Expected :<1919-05-28T20:24:27.345Z>
Actual :<1919-05-28T19:53:08.346Z>
更新
正如@Tunaki 指出的那样,我必须指定 ZonedDateTime
:
assertEquals(
LocalDateTime.now(offset).minusYears(95).toInstant(offset),
ZonedDateTime.now(offset).minusYears(95).withZoneSameInstant(offset).toInstant());
因为没有人回答这个...
创建 ZonedDateTime 时也必须发送偏移量。否则它将使用 Clock.systemDefaultZone()
并且时区会有所不同。
ZonedDateTime.now(offset).minusYears(95).withZoneSameInstant(offset).toInstant()
问题是它不知道时区:LocalDateTime.now(offset).minusYears(97).toInstant(offset)
。目前只有 offset。但这知道时区:ZonedDateTime.now().minusYears(97).toInstant()
ZoneId
包含地点信息和那个地方的时差。它知道 N 年前在那个特定时区的偏移量是 2 小时,而不是现在的 3 小时。ZoneOffset
仅跟踪 hours/minutes 班次。它不知道特定国家的时间变化历史。它只是 "adds hours".
建议的(有点不正确)解决方案是让 ZonedDateTime
忘记区域并使用偏移量代替:ZonedDateTime.now(offset).minusYears(97)
。现在这将与具有相同偏移量的 LocalDateTime
一致 - 两者将显示相同的 不正确 信息。但他们会同意,因为 "just add hours" 而不是了解那个地方在那个历史点的时差:
ZoneOffset offset = ZoneId.of("Europe/Moscow").getRules().getOffset(Instant.now());
assertEquals(
LocalDateTime.now(offset).minusYears(97).toInstant(offset),
ZonedDateTime.now(offset).minusYears(97).toInstant());
或者我们可以设置 LocalDateTime
以显示那个时间那个地方的正确值:
ZonedDateTime zoned = ZonedDateTime.now(ZoneId.of("Europe/Moscow")).minusYears(97);
ZoneOffset offset = zoned.getOffset();//agrees with history
assertEquals(
LocalDateTime.now().minusYears(97).toInstant(offset),
zoned.toInstant());
PS:这一切都表明 ZonedDateTime
在不同情况下的工作方式不同 - 有时它知道时区,其他时候它只是 "adds hours" 就像你对 LocalDateTime 所做的那样手动设置偏移量。对我来说这是一个奇怪的实现。 JodaTime 可能仍然是最好的 Java 实现。至少不用学几天就能看懂。