将java.util.Date转换为"java.time"类型?

Bas*_*que 34 java datetime date java-time

我有一个java.util.Date对象或一个java.util.Calendar对象.如何在java.time框架中将其转换为正确的类型?

我听说我们现在应该使用java.time类型来处理大部分业务逻辑.使用尚未针对java.time更新的旧代码时,我需要能够来回转换.什么类型映射到java.util.Datejava.util.Calendar

Bas*_*que 100

是的,你肯定应该尽可能使用java.time框架.

避免旧的日期时间类

旧的日期时间类,包括java.util.Date,java.util.Calendar等等java.text.SimpleTextFormat,已被证明设计不当,令人困惑和麻烦.尽可能避免使用它们.但是当您必须与这些旧类型进行互操作时,您可以在旧的和新的之间进行转换.

继续阅读一些基本的介绍,有些过于简化,以指导您在旧的和新的日期时间类之间来回移动.

java.time

java.time框架由定义JSR 310,由大获成功的启发乔达时库,并通过扩展ThreeTen-EXTRA项目.的功能的大部分被后移植到Java 6和7在ThreeTen-反向移植项目,在为Android进一步适配ThreeTenABP项目.

什么java.time类型匹配java.util.Date?好吧,一个java.util.Date对象基本上代表UTC时间轴上的一个时刻,它是日期和时间的组合.我们可以将它转换为java.time中的几种类型中的任何一种.每个都在下面讨论.请注意,旧的日期时间类中添加了一些新方法以方便转换.

从java.util.Date/.Calendar通过ZonedDateTime(或OffsetDateTime)转换为三个<code>Instant</code></h2>

<p>java.time中的构建块是<a rel=UTCInstant时间轴上的一个时刻,分辨率为纳秒.

通常,您应该在UTC中执行大部分业务逻辑.在这样的工作中,Instant会经常使用.传递Instant对象,仅应用时区以呈现给用户.如果确实需要应用偏移或时区,请使用下面进一步介绍的类型.

java.util.DateInstant

鉴于这两个Instantjava.util.Date在UTC时间线上的时刻,我们可以很容易地从一个移动java.util.DateInstant.老班学获得了一种新方法java.util.Date::toInstant.

Instant instant = myUtilDate.toInstant();
Run Code Online (Sandbox Code Playgroud)

你可以走另一个方向,从一个Instant到一个java.util.Date.但是你可能会失去关于小数秒的信息.一个Instant跟踪纳秒,小数点后最多九位数,如2016-01-23T12:34:56.123456789Z.java.util.Date和.Calendar都限制为毫秒,小数点后最多三位数,例如2016-01-23T12:34:56.123Z.在这个例子中,从Instant去往Date意味着截断456789.

java.util.Date myUtilDate = java.util.Date.from(instant);
Run Code Online (Sandbox Code Playgroud)

java.util.CalendarInstant

怎么样java.util.Calendar而不是java.util.Date?在Calendar对象内部,日期时间被跟踪为UTC()中1970年第一时刻的历元参考日期时间毫秒数.所以这个值可以很容易地转换为.1970-01-01T00:00:00.0ZInstant

Instant instant = myUtilCalendar.toInstant() ;
Run Code Online (Sandbox Code Playgroud)

java.util.GregorianCalendarZonedDateTime

更好的是,如果您的java.util.Calendar对象实际上是一个,java.util.GregorianCalendar您可以轻松地直接进入ZonedDateTime.该方法具有保留嵌入时区信息的优点.

垂头丧气接口Calendar具体类GregorianCalendar.然后调用toZonedDateTimefrom方法来回走动.

if (myUtilCalendar instanceof GregorianCalendar) {
    GregorianCalendar gregCal = (GregorianCalendar) myUtilCalendar; // Downcasting from the interface to the concrete class.
    ZonedDateTime zdt = gregCal.toZonedDateTime();  // Create `ZonedDateTime` with same time zone info found in the `GregorianCalendar`
}
Run Code Online (Sandbox Code Playgroud)

走向另一个方向......

java.util.Calendar myUtilCalendar = java.util.GregorianCalendar.from(zdt); // Produces an instant of `GregorianCalendar` which implements `Calendar` interface.
Run Code Online (Sandbox Code Playgroud)

如上所述,请注意您可能会丢失有关秒数的信息.在纳秒在java.time类型(ZonedDateTime)被截断,以毫秒为单位.Calendar/ .GregorianCalendar.

OffsetDateTime

Instant我们可以应用一个从UTC偏移量进入某个地方的挂钟时间.偏移量是UTC(向东)之前或UTC之后(向西)之前的小时数,可能是分钟数和秒数.这个ZoneOffset类代表了这个想法.结果是一个OffsetDateTime对象.

ZoneOffset offset = ZoneOffset.of("-04:00"); 
OffsetDateTime odt = OffsetDateTime.ofInstant(instant, zoneOffset);
Run Code Online (Sandbox Code Playgroud)

你可以走另一个方向,从一个OffsetDateTime到一个java.util.Date.提取一个Instant然后按照我们在上面的代码中看到的那样继续.如上所述,任何纳秒都会被截断为毫秒(数据丢失).

java.util.Date myUtilDate = java.util.Date.from(odt.toInstant());
Run Code Online (Sandbox Code Playgroud)

ZonedDateTime

更好的是,应用一个全时区.时区是偏移加上用于处理诸如夏令时(DST)之类的异常的规则.

应用a ZoneId可以获得一个ZonedDateTime对象.使用适当的时区名称(大陆/地区).不要使用常见的如3-4个字母的缩写ESTIST因为它们既不规范,也不是唯一的.

ZoneId zoneId = ZoneId.of("America/Montreal");
ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, zoneId);
Run Code Online (Sandbox Code Playgroud)

你可以走另一个方向,从一个ZonedDateTime到一个java.util.Date.提取一个Instant然后按照我们在上面的代码中看到的那样继续.如上所述,任何纳秒都会被截断为毫秒(数据丢失).

java.util.Date myUtilDate = java.util.Date.from( zdt.toInstant() );
Run Code Online (Sandbox Code Playgroud)

我们进一步看到a ZonedDateTime可以转换为a GregorianCalendar.

LocalDate

有时您可能需要一个仅限日期的值,没有时间和没有时区.为此,请使用java.time.LocalDate对象.

有关更多讨论,请参阅此问题,将java.util.Date转换为java.time.LocalDate,尤其是此答案由Joda-Time和java.time发明背后的主要人员撰写.

关键是要经过一个ZonedDateTime(如上面代码中生成的那样).我们需要一个时区来确定日期.世界各地的日期各不相同,东部早些时候开始新的一天.例如,在巴黎的午夜之后是新的一天,而在蒙特利尔仍然是"昨天".因此,虽然a LocalDate包含时区,但需要时区来确定 a LocalDate.

LocalDate localDate = zdt.toLocalDate();
Run Code Online (Sandbox Code Playgroud)

从另一个方向转换LocalDate到日期时间意味着发明一个时间.您可以选择在您的业务场景中有意义的任何时间.对于大多数人来说,一天的第一时刻是有道理的.您可能很想将第一时刻的硬编码作为时间00:00:00.0.在某些时区,由于夏令时(DST)或其他异常,该时间可能无效作为第一时刻.所以让java.time通过调用来确定正确的时间atStartOfDay.

ZonedDateTime zdt = localDate.atStartOfDay(zoneId);
Run Code Online (Sandbox Code Playgroud)

LocalTime

在极少数情况下,您可能只需要一个没有日期且没有时区的时间.这个概念由LocalTime班级代表.如上所述LocalDate,我们需要一个时区来确定LocalTime即使LocalTime对象不包含(不"记住")该时区.因此,我们再次通过ZonedDateTimeInstant上面看到的对象获得.

LocalTime localTime = zdt.toLocalTime();
Run Code Online (Sandbox Code Playgroud)

LocalDateTime

与其他两种Local…类型一样,a LocalDateTime没有分配时区和偏移量.因此,您可能很少使用它.它可以让您大致了解日期时间,但不是时间轴上的一个点.如果您指的是某个通用日期和可能应用于时区的某个时间,请使用此选项.

例如,"今年圣诞节开始"将是2016-12-25T00:00:00.0.请注意,在该文本表示中缺少任何偏移或时区LocalDateTime.印度德里的圣诞节比法国巴黎的圣诞节开始得更早,后来仍然在蒙特利尔魁北克加拿大.应用这些区域的每个时区将在时间轴上产生不同的时刻.

LocalDateTime ldt = zdt.toLocalDateTime();
Run Code Online (Sandbox Code Playgroud)

  • @ chrisl08:不,他们真的*真的*.他们是*糟糕的API.看看有多少`java.util.Date`已被弃用.只需看看Stack Overflow上有多少用户被`Date.toString()`混淆,显示系统本地时区的值,并推断'Date`有关于时区的信息.只需看看`java.sql.Date`和`java.util.Date`之间的继承关系,其中`java.sql.Date`只是表示一个日期,但是甚至没有指定所谓的时区.在,当`java.util.Date`是一个时间点时.关于这个我可以咆哮*小时* (6认同)
  • @ chrisl08任何快速搜索Stack Overflow都会让你几乎无休止地引起那些古老的日期时间类引起的混乱和痛苦.许多地方已经多次解释了它们的许多缺陷.在历史的这一点上甚至没有争议 - 当然让我们**不要进入这里**.虽然这些旧课程是一个勇敢的第一次尝试(由Taligent和IBM)在一个长期忽视这个困难但却至关重要的日期时间处理主题的行业,但最终它们远远不够好.因此,Joda-Time,现在是它的继承者,java.time. (3认同)
  • 好问答.值得注意的是`Calendar`上的新方法:toInstant():http://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#toInstant--.`GregorianCalendar`有两个新方法:toZonedDateTime()和from(ZonedDateTime) (3认同)
  • 也许我是守旧派,但你从哪里得出这样的说法:“旧的日期时间类,包括 java.util.Date、java.util.Calendar 和 java.text.SimpleTextFormat 等已被证明设计不佳,令人困惑,而且很麻烦。”我已经编写了超过我应得的 Java 应用程序,以及好的旧 java.util.date 和 java.sql.date,但它们从来没有“设计不佳、令人困惑和麻烦”,至少对我来说是这样。 (2认同)