【问题标题】:How to properly convert new Date(0L) to LocalDate (1970-01-01)?如何正确将 new Date(0L) 转换为 LocalDate (1970-01-01)?
【发布时间】:2019-02-21 02:07:41
【问题描述】:

考虑代码:

import org.junit.Test;

import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * @author nsheremet
 */
public class MyTest {
    private static final Date CREATED_ON = new Date(0L);

    @Test
    public void someTest() {
        LocalDate actualDate = CREATED_ON.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDate();
        assertThat(actualDate).isEqualTo(LocalDate.of(1970, 01, 01));
    }
}

在我的机器上运行良好,但在其他机器上却出现异常:

[ERROR] Failures: 
[ERROR] MyTest.someTest:23 expected: <19[70-01-0]1> but was:<19[69-12-3]>1 

为什么会这样?使用UTC 代替ZoneId.systemDefault() 是否正确?

【问题讨论】:

  • Date(时间线上的瞬间)和LocalDate(在某些特定时区,理论上是 24 小时长的时间段)在类型上存在根本区别。您可以参考特定时区进行转换。
  • @smac89 "我相信 LocalDate 可以扩展的最远距离是 Epoch" you believe wrongly
  • LocalDate.EPOCH?
  • 这是Date的问题之一,尽管类名定义了一个时间点,并且在那个时间点上,不同时区总会有不同的日期。
  • 在 epoch,日期是 1970-1-1 或 1969-12-31,具体取决于您所在的时区。使用 UTC,GMT+0 到 GMT+12 恰好在这种情况下工作,但是,如果 Date 不是在 GMT+0 的午夜,您选择的时区仍然会有所不同。

标签: java java-time localdate


【解决方案1】:

为什么会这样?使用 UTC 代替 ZoneId.systemDefault() 是 对吗?

是的。 java.util.Date 的 EPOCH 是 1970 年 1 月 1 日 00:00:00.000 UTC,但 ZoneId.systemDefault() 将获取当前计算机配置为用作用户时区的时间。这极不可能是 UTC。

所以你需要使用时区 UTC,而不是其他任何东西。

【讨论】:

    【解决方案2】:

    tl;博士

    Instant
    .ofEpochMilli( 0L ) 
    .atOffset( 
        ZoneOffset.UTC
    )
    .toLocalDate()
    .toString()
    

    1970-01-01

    java.util.Date

    java.util.Date 表示 UTC 中的一个时刻,一个带有时间的日期。

    您的代码:

    new Date(0L)
    

    ...生成了一个对象,表示 1970 年 UTC 的第一刻,1970-01-01T00:00:00Z。

    时区

    同时,让印度的某人通过电话告诉你他们在时钟上看到的时间,你会得到“早上 5:30”的答案。当时时区Asia/Kolkata 使用了UTC提前五个半小时的偏移量。

    如果与魁北克省蒙特利尔的某人通电话,您会得到“7 PM……日期为 1969-12-31”的答案。该日期的时区America/Montreal 使用了 UTC 落后 5 小时的偏移量。在 UTC 中比午夜晚几个小时也意味着晚于 1969 年的最后一天而不是 1970 年的第一天。America/New_York 和北美东海岸的大部分地区也是如此。

    因此,您必须了解,对于任何特定时刻,日期在全球各地都不同,日期因地区而异。例如,当日本新的一天破晓时,在巴西仍然是“昨天”。

    java.time

    Date 类很糟糕,应该永远不要使用。与Calendar 一起,它现在是遗留的,多年前被 java.time 类所取代。具体来说,DateInstant 替换,这两个类都代表 UTC 中的时刻。

    Instant instant = Instant.ofEpochMilli( 0L ) ;
    

    instant.toString(): 1970-01-01T00:00Z

    调整到特定地区、时区的人们使用的挂钟时间。应用 ZoneId 以获取 ZonedDateTime 对象。

    ZoneId z = ZoneId.of( "America/Montreal" ) ;
    ZonedDateTime zdt = instant.atZone( z ) ;
    

    提取仅日期部分,不包括时间和区域。

    LocalDate ld = zdt.toLocalDate() ;
    

    ld.toString(): 1969-12-31

    如果您想保持 UTC,请应用 ZoneOffset 常量 ZoneOffset.UTC 而不是 ZoneId。并获得OffsetDateTime 而不是ZonedDateTime

    OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;
    

    提取仅日期值,LocalDate

    LocalDate ld = odt.toLocalDate() ;
    

    ld.toString(): 1970-01-01

    【讨论】:

      【解决方案3】:

      可能的问题是您的两台机器位于不同的时区。 检查两台机器上的 ZoneId(如果它们相同)。

      也许你想把定义好的ZoneId,以确保结果总是一样的?

      【讨论】:

      • 这是正确答案,但您已将其表述为评论。请改写并展开。
      • 实际上,这根本不是不同机器之间不同时区的问题。 java.util.Date 从 Epoch 的 delta 构建时使用 UTC。不涉及机器的偏好。
      猜你喜欢
      • 2014-01-16
      • 1970-01-01
      • 1970-01-01
      • 2018-08-26
      • 2012-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-25
      相关资源
      最近更新 更多