我有一个java.util.Date对象,或一个java.util.Calendar对象。如何在java.time框架中将其转换为正确的类型?
java.util.Date
java.util.Calendar
我听说我们现在应该使用java.time类型来做大部分业务逻辑。当使用尚未为java.time更新的旧代码时,我需要能够来回转换。什么类型映射到java.util.Date或java.util.Calendar?
java.time
避免使用旧的日期时间类
老式的日期时间类(包括java.util.Date,java.util.Calendar和)java.text.SimpleTextFormat已被证明设计不良,混乱且麻烦。尽可能避免它们。但是,当你必须与这些旧类型进行互操作时,可以在新旧之间进行转换。
java.text.SimpleTextFormat
请继续阅读,以获取基本的介绍(有些简化),以使你适应在新旧日期时间类之间来回移动。
该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> Local…</ code>类型的示意图
java.util.Date/.Calendar
ZonedDateTime
<code> Local…</ code>
Instant
java.time中的构造块是UTCInstant时间线上的一个时刻,分辨率为纳秒。
通常,你应该在UTC中执行大部分业务逻辑。在这样的工作中,Instant会经常使用。传递Instant对象,仅应用时区以向用户展示。当确实需要应用偏移量或时区时,请使用下面进一步介绍的类型。
从java.util.Date到Instant 鉴于Instant和java.util.Date都是UTC时间轴上的时刻,我们可以轻松地从java.util.Date移到Instant。旧的班级获得了一种新的方法java.util.Date::toInstant。
java.util.Date::toInstant
Instant instant = myUtilDate.toInstant();
你可以去另一个方向,从一个Instant到java.util.Date。但是你可能会丢失有关小数秒的信息。Instant跟踪纳秒,最长到小数点后一位的9位,例如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);
从java.util.Calendar到Instant 那用a java.util.Calendar代替a java.util.Date呢?在Calendar对象内部,日期时间以UTC()中自1970年第一时刻的纪元参考日期时间开始的毫秒数进行跟踪。因此,该值可以轻松转换为。1970-01-01T00:00:00.0ZInstant
a java.util.Calendar
a java.util.Date
Calendar
Instant instant = myUtilCalendar.toInstant() ;
从java.util.GregorianCalendar到ZonedDateTime 更好的是,如果你的java.util.Calendar对象实际上是java.util.GregorianCalendar,则可以轻松地直接转到ZonedDateTime。这种方法的好处是保留嵌入的时区信息。
垂头丧气从接口的Calendar到具体类的GregorianCalendar。然后调用toZonedDateTime和from方法来回切换。
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` }
往另一个方向…
java.util.Calendar myUtilCalendar = java.util.GregorianCalendar.from(zdt); // Produces an instant of `GregorianCalendar` which implements `Calendar` interface.
如上所述,请注意,你可能会丢失几分之一秒的信息。在纳秒在java.time类型(ZonedDateTime)被截断,以毫秒为单位的.Calendar/ .GregorianCalendar。
OffsetDateTime
从Instant我们可以应用的偏移从-UTC搬进挂钟时间对于一些局部性。偏移量是指UTC之前(向东)或UTC后面(向西)的小时数,甚至可能是数分钟和数秒。这个ZoneOffset班代表了这个想法。结果是一个OffsetDateTime对象。
ZoneOffset offset = ZoneOffset.of("-04:00"); OffsetDateTime odt = OffsetDateTime.ofInstant(instant, zoneOffset);
你可以去另一个方向,从一个OffsetDateTime到java.util.Date。提取一个Instant,然后按照上面的代码中的步骤进行操作。如上所述,任何纳秒都将被截断为毫秒(数据丢失)。
java.util.Date myUtilDate = java.util.Date.from(odt.toInstant()); ZonedDateTime
更好的是,应用一个完整的时区。时区是偏移量以及用于处理异常的规则,例如夏令时(DST)。
应用a ZoneId会得到一个ZonedDateTime对象。使用正确的时区名称(大陆/地区)。切勿使用3-4字母的缩写,例如EST或IST既不是标准化的也不是唯一的。
ZoneId zoneId = ZoneId.of("America/Montreal"); ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, zoneId);
你可以去另一个方向,从一个ZonedDateTime到java.util.Date。提取一个Instant,然后按照上面的代码中的步骤进行操作。如上所述,任何纳秒都将被截断为毫秒(数据丢失)。
java.util.Date myUtilDate = java.util.Date.from( zdt.toInstant() );
而且我们在上面进一步看到了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();
从另一个方向转换LocalDate为日期时间意味着发明一个时间。你可以选择在业务场景中有意义的任何时间。对于大多数人来说,一天的第一刻很有意义。你可能会很想在第一时间将其硬编码为时间00:00:00.0。在某些时区,由于夏时制(DST)或其他异常,该时间可能不是第一时间。因此,让java.time通过调用来确定正确的时间atStartOfDay。
ZonedDateTime zdt = localDate.atStartOfDay(zoneId); LocalTime
在极少数情况下,你可能只需要一天中没有日期和时区的时间。这个概念由LocalTime班代表。如上所述LocalDate,LocalTime即使LocalTime对象不包含(不“记住”)该时区,我们也需要一个时区来确定。因此,同样,我们经历了一个ZonedDateTime从Instant上面获得的对象。
LocalTime localTime = zdt.toLocalTime(); LocalDateTime
与其他两种Local…类型一样,LocalDateTime没有分配时区或偏移量。因此,你可能很少使用它。它为你提供了日期时间的粗略概念,但不是时间轴上的一点。如果你是指可能会应用于某个时区的某个常规日期和某个时间,请使用此选项。
例如,“圣诞节从今年开始”将为2016-12-25T00:00:00.0。请注意,的文本表示形式中缺少任何偏移量或时区LocalDateTime。圣诞节在印度德里的开始要比在法国巴黎的开始快,后来在加拿大的魁北克省开始。应用这些区域的每个时区将在时间轴上产生不同的时刻。
LocalDateTime
LocalDateTime ldt = zdt.toLocalDateTime();