如何在Java 8中将LocalDateTime转换为Date

时间:2016-09-22 01:30:00

标签: java date time java-8

我默认使用时区巴西,但是当抓到一个纽约的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

2 个答案:

答案 0 :(得分:19)

LocalDateTime表示没有区域

您似乎误解了LocalDateTime的目的。

此课程没有时区,没有offset-from-UTC时间轴上的一个点。相反,它代表了一个关于可能时刻的模糊概念。名称“Local ...”可能是违反直觉的,因为代表任何特定的地点,而是任何地点。

例如,今年圣诞节是2016年12月25日午夜,或2016-12-25T00:00。这是没有意义的,除非您申请时区在新西兰奥克兰或加尔各答IN或巴黎FR或蒙特利尔加利福尼亚州获得圣诞节,每个时间轴都是时间轴上的不同点,越晚越好,然后向西移动。

永远不要使用LocalDateTime,因为您认为这样可以省去区域和偏移的麻烦。恰恰相反,你会把自己挖进一个含糊不清的日期时间值的洞里。

专注于UTC

您的大多数业务逻辑,日志记录,数据存储和数据交换都应该在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.util.Date.from方法需要Instant。我们可以从Instant(或ZoneDateTime)中提取OffsetDateTime

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年以外,各种计算机系统至少使用了十几个时代。这些数字在人类阅读时没有任何意义,因此可能无法检测到错误。

练习时,您可能会觉得它们很有用。在getEpochSecond上致电getNanoInstant,或拨打值toEpochMilli

关于 java.time

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

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

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

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

从哪里获取java.time类?

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

答案 1 :(得分:6)

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

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

LocalDateTime

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

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

同样,没有时区。但它与Instant有所不同。除非您知道要将其写下来的时区,否则您无法记下LocalDateTime的时间。哦,InstantDate是一回事。

因此,要在InstantLocalDateTime之间进行转换,您需要引用特定的时区。所以要表达尼尔阿姆斯特朗作为一年,一个月,一个月,一小时,一分钟和一秒钟登上月球的那一刻;你需要知道使用什么时区。如果你使用UTC,那是1969年7月21日凌晨2点56分。如果你使用太平洋标准时间,那是1969年7月20日下午6点56分。

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

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

现在,您使用UTC将这些转换为LocalDateTime个对象。

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

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

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

然后将Instant个对象转换为Instant个对象。这并没有真正改变任何事情,因为DateDate表示同样的事情,即使它们的打印方式不同。

您打印出转换后的Instant个对象。他们在巴西时间打印,因为这是您的语言环境。恰好DatedateNY提前一小时;但它们仍然在巴西时间打印,比UTC时间晚三个小时。所以你分别得到19:11:52和18:11:52。

最后,您从毫秒数开始生成几个dateBrazil个对象。但是这些新的Date对象与您已经拥有的DatedateBrazil完全相同,因为您使用相同的毫秒数制作它们。再次,他们在巴西时间印刷。