如何在Java 8中将LocalDateTime转换为Date LocalDateTime意味着没有区域专注于 UTC 时间ZonedDateTime避免使用旧日期时间类避免从 epoch 计数关于 java.time

2022-09-03 17:49:54

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

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

答案 1

LocalDateTime意味着没有区域

你似乎误解了LocalDateTime的目的。

此类没有时区,也没有与 UTC 的偏移量它不是时间表上的一个点。相反,它代表了一个关于可能时刻的模糊想法。名称“本地...”可能是违反直觉的,因为它代表任何特定的地方,而是代表任何地方。

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

永远不要使用LocalDateTime,因为您认为它可以为您节省区域和偏移量的麻烦。恰恰相反,您将把自己挖入一个日期时间值模糊的洞中。

专注于 UTC 时间

大多数业务逻辑、日志记录、数据存储和数据交换都应采用 UTC 格式。将UTC视为一个真正的时间;所有其他区域和偏移量都伪装成修饰的 UTC 值。

在java.time中,这意味着类是你的首选类,是日期时间对象的基本构建块。Instant 类以 UTC 格式表示时间轴上的某个时刻,分辨率为纳秒Instant

Instant now = Instant.now();

ZonedDateTime

仅当需要访问某些区域的挂钟时间时,才调整到时区。应用 a 以获取对象。ZoneIdZonedDateTime

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.time类。java.util.Datejava.util.Calendar

如果必须与尚未针对 java.time 类型更新的旧代码进行交互,则可以在 java.time 类型之间进行转换。查找添加到旧类的新方法。java.util.Date.from 方法采用 .我们可以从 (或 从 ) 中提取 a。InstantInstantZoneDateTimeOffsetDateTime

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

然后朝另一个方向走。

Instant instant = utilDate.toInstant();

有关转换的更多信息,请参阅我对问题的回答将java.util.Date转换为哪种“java.time”类型?

避免从 epoch 计数

避免使用从 epoch 计数,例如自 UTC 中的 1970 年开始以来的毫秒数。计数有各种粒度(毫秒、微秒、纳秒、整秒等)。除了1970年之外,各种计算机系统至少使用了几十个时代。当人类阅读时,这些数字没有任何意义,因此虫子可能不会被发现。

在练习时,您可能会发现它们很有用。调用 和 打开 ,或用于截断值调用 。getEpochSecondgetNanoInstanttoEpochMilli


关于 java.time

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

Joda-Time 项目现在处于维护模式,建议迁移到 java.time 类。

要了解更多信息,请参阅 Oracle 教程。搜索 Stack Overflow 以获取许多示例和解释。规格是JSR 310

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

从哪里获取 java.time 类?

ThreeTen-Extra 项目通过其他类扩展了 java.time。这个项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuarter


答案 2

在我看来,你对 a 和 an(或 a )之间的区别感到困惑,这本质上与 an 是一回事。这些是完全不同的对象。LocalDateTimeInstantDateInstant

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

LocalDateTime

或者你可以把它想象成年,月,日,小时,分钟和秒。但它没有时区。这只是日历上说的和时钟上说的。

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

同样,没有时区。但它与 .你不能写下a是什么时间,除非你知道要写下哪个时区。哦,a 和 a 是一回事。LocalDateTimeInstantDateInstant

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

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

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

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

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

然后你打印出来。我们需要知道一个时区才能做到这一点,但这没关系。无论如何,A 都会以 UTC 格式打印。这就是意思。InstantZ

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

然后将对象转换为对象。这并没有真正改变任何东西,因为a和a代表同一件事,即使它们以不同的方式打印出来。InstantDateDateInstant

打印出这些转换后的对象。它们在巴西时间打印,因为这是您的区域设置。碰巧比 ;但它们仍然都是在巴西时间打印的,这比UTC晚了三个小时。所以你分别得到19:11:52和18:11:52。DatedateNYdateBrazil

最后,从毫秒数开始,再制作几个对象。但是这些新对象与 您已经拥有的和完全相等,因为您从相同的毫秒数制作了它们。再一次,它们在巴西时间被印刷出来。DateDatedateBrazildateNY