将java.util.Date转换为哪种“java.time”类型?避免旧的日期时间类java.timeOffsetDateTimeZonedDateTimeLocalDateLocalTimeLocalDateTime关于 java.time

2022-08-31 21:10:34

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

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


答案 1

是的,你绝对应该尽可能地使用java.time框架。

避免旧的日期时间类

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

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

java.time

java.time框架由JSR 310定义,灵感来自非常成功的Joda-Time库,并由ThreeTen-Extra项目扩展。在ThreeTen-Backport项目中,大部分功能被移植到Java 6和7,并在ThreeTenABP项目中进一步适应Android。

哪种 java.time 类型与 java.util.Date 匹配?好吧,对象基本上表示UTC时间轴上的一个时刻,即日期和时间的组合。我们可以将其转换为java.time中的几种类型中的任何一种。下面将逐一讨论。请注意,一些新方法已添加到旧的日期时间类中,以便于转换。java.util.Date

diagram of converting from java.util.Date/.Calendar through ZonedDateTime (or OffsetDateTime) to the three Local… types

Instant

java.time中的构建块是即时的,是UTC时间轴上的一个时刻,分辨率为纳秒

通常,您应该在 UTC 中执行大部分业务逻辑。在这样的工作中,会经常使用。传递对象,应用时区仅用于向用户演示。当您确实需要应用偏移量或时区时,请使用下面进一步介绍的类型。InstantInstant

从 到java.util.DateInstant

鉴于两者都是 UTC 时间轴上的一个时刻,我们可以轻松地从 java.util.Date 移动到 .旧类获得了一个新方法,java.util.Date::toInstantInstantjava.util.DateInstant

Instant instant = myUtilDate.toInstant();

您可以转到另一个方向,从 a 到 .但是您可能会丢失有关小数秒的信息。A 跟踪纳秒,在小数点后最多为九位,例如 .java.util.Date & .日历限制为毫秒,小数点后最多为三位,例如 .在此示例中,从 到 表示 截断 .Instantjava.util.DateInstant2016-01-23T12:34:56.123456789Z2016-01-23T12:34:56.123ZInstantDate456789

java.util.Date myUtilDate = java.util.Date.from(instant);

从 到java.util.CalendarInstant

那么 java.util.Calendar 而不是 ?在对象内部,日期时间被跟踪为从 1970 年第一个时刻的 epoch 参考日期时间(UTC)开始的毫秒数()。因此,此值可以轻松转换为 .java.util.DateCalendar1970-01-01T00:00:00.0ZInstant

Instant instant = myUtilCalendar.toInstant() ;

从 到java.util.GregorianCalendarZonedDateTime

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

从 的接口向下铸造的具体类 。然后调用 toZonedDateTimefrom 方法来回切换。CalendarGregorianCalendar

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

从 一个,我们可以应用一个偏移量 UTC 来移动到某个位置的挂钟时间。偏移量是 UTC 之前(向东)或 UTC(向西)的小时数,可能为分钟和秒数。ZoneOffset 类表示此思想。结果是一个 OffsetDateTime 对象。Instant

ZoneOffset offset = ZoneOffset.of("-04:00"); 
OffsetDateTime odt = OffsetDateTime.ofInstant(instant, zoneOffset);

您可以转到另一个方向,从 a 到 .提取 一个,然后按照我们在上面的代码中看到的那样继续。如上所述,任何纳秒都会被截断为毫秒数据丢失)。OffsetDateTimejava.util.DateInstant

java.util.Date myUtilDate = java.util.Date.from(odt.toInstant());

ZonedDateTime

更好的是,应用完整的时区。时区是用于处理异常(如夏令时 (DST))的偏移量规则。

应用 ZoneId 会获得一个 ZonedDateTime 对象。使用正确的时区名称(大洲/地区)。切勿使用常见的 3-4 个字母缩写,例如 或 因为它们既不标准化也不唯一。ESTIST

ZoneId zoneId = ZoneId.of("America/Montreal");
ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, zoneId);

您可以转到另一个方向,从 a 到 .提取 一个,然后按照我们在上面的代码中看到的那样继续。如上所述,任何纳秒都会被截断为毫秒数据丢失)。ZonedDateTimejava.util.DateInstant

java.util.Date myUtilDate = java.util.Date.from( zdt.toInstant() );

我们在上面进一步看到,a 可以转换为 .ZonedDateTimeGregorianCalendar

LocalDate

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

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

关键是要通过一个(如上面的代码中生成的那样)。我们需要一个时区来确定日期。世界各地的日期各不相同,东部的新一天提前到来。例如,巴黎午夜过后是新的一天,而蒙特利尔仍然是“昨天”。因此,虽然 a 不包含时区,但需要时区来确定 .ZonedDateTimeLocalDateLocalDate

LocalDate localDate = zdt.toLocalDate();

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

ZonedDateTime zdt = localDate.atStartOfDay(zoneId);

LocalTime

在极少数情况下,您可能只想要一个没有日期和时区的时间。此概念由 LocalTime 类表示。如上文所述,我们需要一个时区来确定一个,即使该对象不包含(不“记住”)该时区。因此,我们再次浏览从上面看到的对象。LocalDateLocalTimeLocalTimeZonedDateTimeInstant

LocalTime localTime = zdt.toLocalTime();

LocalDateTime

与其他两种类型一样,LocalDateTime 没有分配时区或偏移量。因此,您可能很少使用它。它为您提供了日期时间的粗略概念,但不是时间轴上的点。如果您指的是可能应用于某个时区的某个常规日期和某个时间,请使用此选项。Local…

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

LocalDateTime ldt = zdt.toLocalDateTime();

enter image description here


关于 java.time

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

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

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

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

从哪里获取 java.time 类?


答案 2