Migrating to the New Java 8 Date Time API – 迁移到新的Java 8日期时间API

最后修改: 2016年 6月 25日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial you will learn how to refactor your code in order to leverage the new Date Time API introduced in Java 8.

在本教程中,你将学习如何重构你的代码,以利用Java 8中引入的新的日期时间API。

2. New API at a Glance

2.新API一瞥

Working with dates in Java used to be hard. The old date library provided by JDK included only three classes: java.util.Date, java.util.Calendar and java.util.Timezone.

在Java中处理日期曾经是很困难的。JDK提供的旧日期库只包括三个类。java.util.Date, java.util.Calendarjava.util.Timezone

These were only suitable for the most basic tasks. For anything even remotely complex, the developers had to either use third-party libraries or write tons of custom code.

这些只适用于最基本的任务。对于任何复杂的事情,开发人员不得不使用第三方库或编写大量的自定义代码。

Java 8 introduced a completely new Date Time API (java.util.time.*) that is loosely based on the popular Java library called JodaTime. This new API dramatically simplified date and time processing and fixed many shortcomings of the old date library.

Java 8引入了一个全新的日期时间APIjava.util.time.*),它松散地基于流行的Java库JodaTime。这个新的API极大地简化了日期和时间的处理,并修正了旧的日期库的许多缺点。

1.1. API Clarity

1.1.API的明确性

A first advantage of the new API is clarity – the API is very clear, concise and easy to understand. It does not have a lot of inconsistencies found in the old library such as the field numbering (in Calendar months are zero-based, but days of week are one-based).

新的API的第一个优点是清晰性–API非常清晰、简明和容易理解。它没有旧库中的很多不一致之处,比如字段编号(在日历中,月是以0为基础的,但星期是以1为基础的)。

1.2. API Flexibility

1.2.API的灵活性

Another advantage is flexibility – working with multiple representations of time. The old date library included only a single time representation class – java.util.Date, which despite its name, is actually a timestamp. It only stores the number of milliseconds elapsed since the Unix epoch.

另一个优势是灵活性–使用多种时间表示方法。旧的日期库只包括一个单一的时间表示类– java.util.Date,尽管它的名字是这样,但实际上是一个时间戳。它只存储自Unix纪元以来所经过的毫秒数。

The new API has many different time representations, each suitable for different use cases:

新的API有许多不同的时间表示,每个都适合不同的使用情况。

  • Instant – represents a point in time (timestamp)
  • LocalDate – represents a date (year, month, day)
  • LocalDateTime – same as LocalDate, but includes time with nanosecond precision
  • OffsetDateTime – same as LocalDateTime, but with time zone offset
  • LocalTime – time with nanosecond precision and without date information
  • ZonedDateTime – same as OffsetDateTime, but includes a time zone ID
  • OffsetLocalTime – same as LocalTime, but with time zone offset
  • MonthDay – month and day, without year or time
  • YearMonth – month and year, without day or time
  • Duration – amount of time represented in seconds, minutes and hours. Has nanosecond precision
  • Period – amount of time represented in days, months and years

1.3. Immutability and Thread-Safety

1.3.不变性和线程安全

Another advantage is that all time representations in Java 8 Date Time API are immutable and thus thread-safe.

另一个优点是,Java 8 Date Time API中的所有时间表示都是不可变的,因此是线程安全的。

All mutating methods return a new copy instead of modifying state of the original object.

所有的变异方法都返回一个新的副本,而不是修改原始对象的状态。

Old classes such as java.util.Date were not thread-safe and could introduce very subtle concurrency bugs.

旧的类,如java.util.Date,不是线程安全的,可能会引入非常微妙的并发错误。

1.4. Method Chaining

1.4.方法链

All mutating methods can be chained together, allowing to implement complex transformations in a single line of code.

所有的变异方法都可以被串联起来,允许在一行代码中实现复杂的转换。

ZonedDateTime nextFriday = LocalDateTime.now()
  .plusHours(1)
  .with(TemporalAdjusters.next(DayOfWeek.FRIDAY))
  .atZone(ZoneId.of("PST"));

2. Examples

2.例子

The examples below will demonstrate how to perform common tasks with both old and new API.

下面的例子将演示如何使用新旧API执行常见任务。

Getting current time

获取当前的时间

// Old
Date now = new Date();

// New
ZonedDateTime now = ZonedDateTime.now();

Representing specific time

代表特定的时间

// Old
Date birthDay = new GregorianCalendar(1990, Calendar.DECEMBER, 15).getTime();

// New
LocalDate birthDay = LocalDate.of(1990, Month.DECEMBER, 15);

Extracting specific fields

提取特定的字段

// Old
int month = new GregorianCalendar().get(Calendar.MONTH);

// New
Month month = LocalDateTime.now().getMonth();

Adding and subtracting time

增加和减少时间

// Old
GregorianCalendar calendar = new GregorianCalendar();
calendar.add(Calendar.HOUR_OF_DAY, -5);
Date fiveHoursBefore = calendar.getTime();

// New
LocalDateTime fiveHoursBefore = LocalDateTime.now().minusHours(5);

Altering specific fields

修改特定的字段

// Old
GregorianCalendar calendar = new GregorianCalendar();
calendar.set(Calendar.MONTH, Calendar.JUNE);
Date inJune = calendar.getTime();

// New
LocalDateTime inJune = LocalDateTime.now().withMonth(Month.JUNE.getValue());

Truncating

截断>

Truncating resets all time fields smaller than the specified field. In the example below minutes and everything below will be set to zero

截断会重置所有小于指定字段的时间字段。在下面的例子中,分钟和下面的一切将被设置为零

// Old
Calendar now = Calendar.getInstance();
now.set(Calendar.MINUTE, 0);
now.set(Calendar.SECOND, 0);
now.set(Calendar.MILLISECOND, 0);
Date truncated = now.getTime();

// New
LocalTime truncated = LocalTime.now().truncatedTo(ChronoUnit.HOURS);

Time zone conversion

时区转换

// Old
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTimeZone(TimeZone.getTimeZone("CET"));
Date centralEastern = calendar.getTime();

// New
ZonedDateTime centralEastern = LocalDateTime.now().atZone(ZoneId.of("CET"));

Getting time span between two points in time

获得两个时间点之间的时间跨度

// Old
GregorianCalendar calendar = new GregorianCalendar();
Date now = new Date();
calendar.add(Calendar.HOUR, 1);
Date hourLater = calendar.getTime();
long elapsed = hourLater.getTime() - now.getTime();

// New
LocalDateTime now = LocalDateTime.now();
LocalDateTime hourLater = LocalDateTime.now().plusHours(1);
Duration span = Duration.between(now, hourLater);

Time formatting and parsing

时间格式化和解析

DateTimeFormatter is a replacement for the old SimpleDateFormat that is thread-safe and provides additional functionality.

DateTimeFormatter是旧的SimpleDateFormat的替代品,它是线程安全的并提供额外的功能。

// Old
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date now = new Date();
String formattedDate = dateFormat.format(now);
Date parsedDate = dateFormat.parse(formattedDate);

// New
LocalDate now = LocalDate.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String formattedDate = now.format(formatter);
LocalDate parsedDate = LocalDate.parse(formattedDate, formatter);

Number of days in a month

一个月内的天数

// Old
Calendar calendar = new GregorianCalendar(1990, Calendar.FEBRUARY, 20);
int daysInMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);

// New
int daysInMonth = YearMonth.of(1990, 2).lengthOfMonth();

3. Interacting With Legacy Code

3.与遗留代码互动

In many cases a user might need to ensure interoperability with third-party libraries that rely on the old date library.

在许多情况下,用户可能需要确保与依赖旧日期库的第三方库的互操作性。

In Java 8 old date library classes have been extended with methods that convert them to corresponding objects from new Date API.
New classes provide similar functionalities.

在Java 8中,旧的日期库类已被扩展,其方法可将它们转换为新的Date API中的相应对象。
新的类提供了类似的功能。

Instant instantFromCalendar = GregorianCalendar.getInstance().toInstant();
ZonedDateTime zonedDateTimeFromCalendar = new GregorianCalendar().toZonedDateTime();
Date dateFromInstant = Date.from(Instant.now());
GregorianCalendar calendarFromZonedDateTime = GregorianCalendar.from(ZonedDateTime.now());
Instant instantFromDate = new Date().toInstant();
ZoneId zoneIdFromTimeZone = TimeZone.getTimeZone("PST").toZoneId();

4. Conclusion

4.结论

In this article we explored the new Date Time API available in Java 8. We took a look at its advantages, compared to the deprecated API and pointed out differences using multiple examples.

在这篇文章中,我们探讨了Java 8中可用的新日期时间API。我们看了看它的优点,与被废弃的API相比,并通过多个例子指出了不同之处。

Note that we barely scratched surface of the capabilities of the new Date Time API. Make sure to read through the official documentation to discover full range of tools offered by the new API.

请注意,我们几乎没有触及新的日期时间API的能力的表面。请确保阅读官方文档以发现新的API所提供的全部工具。

Code examples can be found in the GitHub project.

代码实例可以在GitHub项目中找到。