将 java.util.Date 转换为什么“java.time” 类型?

Convert java.util.Date to what “java.time” type?

我有一个java.util.Date object, or a java.util.Calendar object. How do I convert that to the right type in java.time框架?

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

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

避免使用旧的日期时间 classes

旧日期时间 classes 包括 java.util.Date, java.util.Calendar, and java.text.SimpleDateFormat and such have proven to be poorly designed, confusing, and troublesome。尽可能避开它们。但是当你必须与这些旧类型进行互操作时,你可以在新旧类型之间进行转换。

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

java.time

java.time 框架由 JSR 310 定义,受到非常成功的 Joda-Time 库的启发,并且由 ThreeTen-Extra 项目扩展。大部分功能被反向移植到 ThreeTen-Backport 项目中的 Java 6 & 7,并在 [=250] 中进一步适应 Android =]ThreeTenABP 项目.

什么 java.time 类型匹配 java.util.Date?嗯,一个 java.util.Date 对象基本上代表 UTC 时间轴上的一个时刻,是日期和时间的组合。我们可以将其转换为 java.time 中的几种类型中的任何一种。下面分别讨论。请注意,一些新方法已添加到旧日期时间 classes 以促进转换。

Instant

java.time 中的积木是 Instant, a moment on the timeline in UTC with a resolution of nanoseconds

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

java.util.DateInstant

鉴于 Instantjava.util.Date 都是 UTC 时间轴上的一个时刻,我们可以轻松地从 java.util.Date 移动到 Instant。老class有了新方法,java.util.Date::toInstant.

Instant instant = myUtilDate.toInstant();

你可以换个方向,从 Instantjava.util.Date。但是您可能会丢失有关小数秒的信息。 Instant 跟踪 nanoseconds, for up to nine digits after the decimal place such as 2016-01-23T12:34:56.123456789Z. Both java.util.Date & .Calendar are limited to milliseconds,小数点后最多三位数字,例如 2016-01-23T12:34:56.123Z。在此示例中,从 InstantDate 表示截断 456789

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

java.util.CalendarInstant

UTC (1970-01-01T00:00:00.0Z) 中 1970 年第一时刻的 java.util.Calendar instead of a java.util.Date? Internal to the Calendar object, the date-time is tracked as a count of milliseconds from the epoch reference date-time 呢?所以这个值可以很容易地转换为 Instant.

Instant instant = myUtilCalendar.toInstant() ;

java.util.GregorianCalendarZonedDateTime

更好的是,如果你 java.util.Calendar object is actually a java.util.GregorianCalendar you can easily go directly to a ZonedDateTime。这种方法的好处是保留了嵌入的时区信息。

Downcast from the interface of Calendar to the concrete class of GregorianCalendar. Then call the toZonedDateTime and 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.

如上所述,请注意您可能会丢失有关秒的小数部分的信息.Calendar/.GregorianCalendar中的nanoseconds in the java.time type (ZonedDateTime) gets truncated to milliseconds.

OffsetDateTime

我们可以从 Instant 应用一个 offset-from-UTC to move into a wall-clock time for some locality. An offset is a number of hours, and possibly minutes and seconds, ahead of UTC (eastward) or behind UTC (westward). The ZoneOffset class represents this idea. The result is an OffsetDateTime 对象。

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

你可以换个方向,从 OffsetDateTimejava.util.Date。提取一个 Instant 然后按照我们在上面的代码中看到的那样继续。如上所述,任何nanoseconds get truncated to milliseconds数据丢失)。

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

ZonedDateTime

更好的是,应用完整的 time zone. A time zone is an offset plus rules for handling anomalies such as Daylight Saving Time (DST)

正在应用 ZoneId gets you a ZonedDateTime object. Use a proper time zone name (continent/region)。切勿使用常见的 3-4 个字母缩写,例如 ESTIST,因为它们既不标准化也不唯一。

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

你可以换个方向,从 ZonedDateTimejava.util.Date。提取一个 Instant 然后按照我们在上面的代码中看到的那样继续。如上所述,任何nanoseconds get truncated to milliseconds数据丢失)。

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

而且我们在上面进一步看到 ZonedDateTime 可以转换为 GregorianCalendar

LocalDate

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

有关更多讨论,请参阅此问题,Convert java.util.Date to java.time.LocalDate, especially this Answer 由发明 Joda-Time 和 java.time 的主要人物撰写。

关键是通过一个ZonedDateTime(如上面的代码生成的)。我们需要一个时区来确定日期。世界各地的日期各不相同,东方的新一天开始得更早。例如,在巴黎午夜过后是新的一天,而在蒙特利尔仍然是“昨天”。因此,虽然 LocalDate 包含 时区,但需要时区才能 确定 LocalDate

LocalDate localDate = zdt.toLocalDate();

LocalDate 向另一个方向转换为日期时间意味着发明一个时间。您可以选择在您的业务场景中有意义的任何一天中的时间。对于大多数人来说,一天的第一刻是有意义的。您可能想将第一个时刻硬编码为时间 00:00:00.0。在某些时区,由于 Daylight Saving Time (DST) or other anomalies. So let java.time determine the correct time with a call to atStartOfDay.

,该时间可能不适用于第一时间
ZonedDateTime zdt = localDate.atStartOfDay(zoneId);

LocalTime

在极少数情况下,您可能只需要一个没有日期和时区的时间。这个概念由 LocalTime class 表示。正如上面讨论的 LocalDate,我们需要一个时区来确定 LocalTime,即使 LocalTime 对象不包含(不“记住”)那个时区。因此,我们再次遍历从 Instant 获得的 ZonedDateTime 对象,如上所示。

LocalTime localTime = zdt.toLocalTime();

LocalDateTime

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

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

LocalDateTime ldt = zdt.toLocalDateTime();


关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

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

从哪里获得 java.time classes?