ZonedDateTime& LocalDateTime与100年前不同

时间:2016-05-28 20:29:08

标签: java datetime java-8 java-time

我看到的行为非常奇怪 - 有时候LocalDateTime会等于ZonedDateTime,有时会相差1小时或2,有时则是30分钟。所有这些奇怪的差异取决于我减去的年份。有人能解释一下发生了什么吗?已尝试jdk1.8.0_65jdk1.8.0_91MacOS 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());

2 个答案:

答案 0 :(得分:1)

问题是这不知道时区:LocalDateTime.now(offset).minusYears(97).toInstant(offset)。只有偏移存在。但是这知道时区:ZonedDateTime.now().minusYears(97).toInstant()

  • ZoneId包含有关该地点的地点和时差的信息。它知道N年前在那个特定时区的偏移是2小时,而不是现在的3小时。
  • ZoneOffset仅跟踪小时/分钟班次。它不知道特定国家的时间变化历史。它只是“增加了几个小时”。

建议(并且有点不正确)解决方案是让ZonedDateTime忘记区域并改为使用偏移:ZonedDateTime.now(offset).minusYears(97)。现在这同意LocalDateTime具有相同的偏移量 - 两者都会显示相同的不正确的信息。但是他们会同意,因为两者都“加上时间”,而不是理解那个地方的历史时间差:

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在不同的情况下可以有不同的工作方式 - 有时候它会知道时区,有时它只是“加上小时”,就像使用LocalDateTime手动设置偏移一样。对我来说,这是一个奇怪的实现。 JodaTime可能仍然是最好的Java实现。至少你几天不需要学习它来理解它。

答案 1 :(得分:0)

因为没有人回答这个问题......

在创建ZonedDateTime时也必须发送偏移量。否则它将使用Clock.systemDefaultZone(),你会在时区上有所不同。

ZonedDateTime.now(offset).minusYears(95).withZoneSameInstant(offset).toInstant()