【问题标题】:How convert LocalDateTime to Date in Java 8如何在 Java 8 中将 LocalDateTime 转换为 Date
【发布时间】:2016-09-22 01:30:00
【问题描述】:

我默认使用巴西时区,但是当捕捉到纽约的一个 LocalDateTime 并转换为 java.tim.Instant 时,instant 被正确填充。问题是,当我尝试使用 Date.from (instantValue) 生成 Date,而不是生成纽约的 date 时,我最终从巴西获得当前日期。

ZoneId nyZone = ZoneId.of("America/New_York");
ZoneId brazilZone = ZoneId.of("America/Recife");

LocalDateTime ldtBrazil = LocalDateTime.now(brazilZone);
LocalDateTime ldtNY = LocalDateTime.now(nyZone);

Instant instantBrazil = ldtBrazil.toInstant(ZoneOffset.UTC);
Instant instantNY = ldtNY.toInstant(ZoneOffset.UTC);

System.out.println("-------LocalDateTime-------");
System.out.println("ldtBrazil    : "+ldtBrazil);
System.out.println("ldtNY        : "+ldtNY);

System.out.println("\n-------Instant-------");
System.out.println("instantBrazil: "+instantBrazil);
System.out.println("instantNY    : "+instantNY);

long milliBrazil = instantBrazil.toEpochMilli();
long milliNY = instantNY.toEpochMilli();

System.out.println("\n----------Milli----------");
System.out.println("miliBrazil : "+milliBrazil);
System.out.println("miliNY     : "+milliNY);

Date dateBrazil = Date.from(instantBrazil);
Date dateNY = Date.from(instantNY);

System.out.println("\n---------Date From Instant---------");
System.out.println("dateBrazil: "+dateBrazil);
System.out.println("dateNY    : "+dateNY);

System.out.println("\n---------Date From Milli---------");
System.out.println("dateBrazil: "+new Date(milliBrazil));
System.out.println("dateNY    : "+new Date(milliNY));

结果

-------LocalDateTime-------
ldtBrazil    : 2016-09-21T22:11:52.118
ldtNY        : 2016-09-21T21:11:52.118

-------Instant-------
instantBrazil: 2016-09-21T22:11:52.118Z
instantNY    : 2016-09-21T21:11:52.118Z

----------Milli----------
miliBrazil : 1474495912118
miliNY     : 1474492312118

---------Date From Instant---------
dateBrazil: Wed Sep 21 19:11:52 BRT 2016
dateNY    : Wed Sep 21 18:11:52 BRT 2016 //this data must be related to   NY LocalDateTime, but reiceved a same date of Brazil.

---------Date From Milli---------
dateBrazil: Wed Sep 21 19:11:52 BRT 2016
dateNY    : Wed Sep 21 18:11:52 BRT 2016

【问题讨论】:

  • 这是因为Date 真的只是一瞬间。它不存储您所在的时区。当您打印Date 对象时,它们将使用您当前的时间区域设置格式化,其中包括您的时区。因此,每当您使用 System.out.println 直接显示日期时,它总是会显示 BRT 时区,或者您当地的时区。
  • 好的,但是我调用new Date(milli),milli代表一个没有时区的日期。
  • 当然,但是 Date.from() 的工作方式相同。无论您如何制作 Date 对象,它都没有内置时区。
  • 您的 instantBrazilinstantNY不是当前时间,因为您使用 UTC 而不是巴西或纽约的时区转换它们。所以你这样做引入了几个小时的错误。

标签: java date time java-8


【解决方案1】:

LocalDateTime 表示没有区域

你似乎误解了LocalDateTime的目的。

此类没有时区,也没有offset-from-UTC。它不是时间线上的一个点。相反,它代表了一个关于可能时刻的模糊概念。 “Local…”这个名字可能有悖常理,因为它代表任何特定的地方,而是任何地方。

例如,今年的圣诞节是 2016 年 12 月 25 日开始的午夜,或2016-12-25T00:00。除非您在新西兰奥克兰或加尔各答 IN 或巴黎 FR 或蒙特利尔 CA 应用时区来庆祝圣诞节,否则这没有任何意义,每个时间点都是时间线上的不同点,随着您向西移动,时间越来越晚。

永远不要使用LocalDateTime,因为您认为它会为您省去区域和偏移量的麻烦。恰恰相反,您会在日期时间值不明确的情况下自掘坟墓。

关注世界标准时间

您的大部分业务逻辑、日志记录、数据存储和数据交换都应该在UTC 中。将 UTC 视为一个真正的时间;所有其他区域和偏移量都伪装成 UTC 值。

在 java.time 中,这意味着 Instant 类是您的首选类,它是日期时间对象的基本构建块。 Instant 类代表UTC 中时间轴上的一个时刻,分辨率为nanoseconds

Instant now = Instant.now();

ZonedDateTime

仅在需要访问某些地区的wall-clock time 时才调整到时区。应用ZoneId 以获取ZonedDateTime 对象。

ZoneId zNewYork = ZoneId.of("America/New_York");
ZoneId zRecife = ZoneId.of("America/Recife");

ZonedDateTime zdtNewYork = now.atZone( zNewYork );
ZonedDateTime zdtRecife = now.atZone( zRecife );

所有这三个对象,nowzdtNewYorkzdtRecife,都是在某个时刻,即时间线上的同一点。所有三个共享相同的时代计数。唯一的区别是我们看到他们挂钟时间的镜头。

避免使用旧的日期时间类

避免使用与最早版本的 Java 捆绑在一起的麻烦的旧日期时间类。所以,避免java.util.Datejava.util.Calendar。他们真的那么糟糕。坚持使用 java.time 类。

如果您必须与尚未针对 java.time 类型更新的旧代码进行交互,您可以转换为 java.time 类型/从 java.time 类型转换。寻找添加到旧类的新方法。 java.util.Date.from 方法采用 Instant。我们可以从ZoneDateTime(或OffsetDateTime)中提取Instant

java.util.Date utilDate = java.util.Date.from( zdtNewYork.toInstant() );

然后往另一个方向发展。

Instant instant = utilDate.toInstant();

有关转换的更多信息,请参阅my Answer 到问题,Convert java.util.Date to what “java.time” type?

避免从纪元开始计数

避免使用从 1970 年开始的毫秒数等从 UTC 开始计数的数字。计数有多种粒度(毫秒、微秒、纳秒、整秒等)。除了 1970 年之外,各种计算机系统至少使用了几十个 epoch。这些数字在人类阅读时没有任何意义,因此可能无法检测到错误。

您可能会在练习时发现它们很有用。在Instant 上调用getEpochSecondgetNano,或者为截断值调用toEpochMilli


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

从哪里获得 java.time 类?

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

    【解决方案2】:

    在我看来,您对LocalDateTimeInstant(或Date,本质上与Instant 相同)之间的区别感到困惑。这些是完全不同的对象。

    LocalDateTime 是特定的日历日期和特定的时钟时间。你可以把它想象成这张图。

    或者您可以将其视为年、月、日、小时、分钟和秒。但它没有时区。这就是日历所说的和时钟所说的。

    Instant 是一个时刻。例如,尼尔阿姆斯特朗第一次踏上月球的那一刻可以表示为Instant。肯尼迪被枪杀的那一刻也是如此。

    同样,没有时区。但它与LocalDateTime 不同。除非您知道要写下哪个时区,否则您无法写下 Instant 的时间。哦,DateInstant 是一回事。

    因此,要在LocalDateTimeInstant 之间进行转换,您需要引用特定的时区。因此,将尼尔·阿姆斯特朗踏上月球的那一刻表达为年、月、日、时、分和秒;您需要知道使用哪个时区。如果使用 UTC,则为 1969 年 7 月 21 日凌晨 2:56。如果使用太平洋标准时间,则为 1969 年 7 月 20 日下午 6:56。

    有了这些知识,让我们来分析您的代码。您从几个 LocalDateTime 对象开始。

    • ldtBrazil 是巴西的当前时间 - 9 月 21 日 22:11:52。
    • ldtNY 是纽约的当前时间 - 9 月 21 日的 21:11:52。

    现在,您可以使用 UTC 将这些转换为 Instant 对象。

    • instantBrazil 是 Timbuktu(全年使用 UTC)的 22:11:52。
    • instantNY 是廷巴克图的 21:11:52(比 InstantBrazil 早一个小时)。

    然后你把这些打印出来。我们需要知道一个时区才能做到这一点,但这没关系。无论如何,Instant 都会以 UTC 格式打印。这就是Z 的含义。

    现在,您将 Instant 对象转换为毫秒数。美好的。这是自 UTC 时间 1970 年 1 月 1 日午夜以来的毫秒数。 milliNY 显然比 milliBrazil 少 360 万,因为它对应于一个小时前的 Instant

    然后将Instant 对象转换为Date 对象。这并没有真正改变任何东西,因为 DateInstant 代表相同的东西,即使它们的打印方式不同。

    您打印出那些转换后的Date 对象。它们以巴西时间打印,因为那是您的语言环境。碰巧dateNYdateBrazil 早一个小时;但它们仍然以巴西时间打印,比 UTC 晚三个小时。所以你分别得到 19:11:52 和 18:11:52。

    最后,您从毫秒数中创建了更多Date 对象。但是这些新的Date 对象与您已经拥有的dateBrazildateNY 完全相同,因为您使用相同的毫秒数创建了它们。同样,它们是在巴西时间印刷的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-31
      • 1970-01-01
      • 2021-11-26
      • 2016-01-06
      • 2019-01-21
      • 1970-01-01
      • 1970-01-01
      • 2012-12-28
      相关资源
      最近更新 更多