Introduction to the Java 8 Date/Time API – Java 8日期/时间API简介

最后修改: 2016年 7月 1日

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

1. Overview

1.概述

Java 8 introduced new APIs for Date and Time to address the shortcomings of the older java.util.Date and java.util.Calendar.

Java 8 为 DateTime 引入了新的 API,以解决旧版 java.util.Datejava.util.Calendar 的不足之处。

In this tutorial, let’s start with the issues in the existing Date and Calendar APIs and discuss how the new Java 8 Date and Time APIs address them.

在本教程中,让我们从现有的DateCalendar API中的问题开始,讨论新的Java 8 DateTime API如何解决这些问题。

We will also look at some of the core classes of the new Java 8 project that are part of the java.time package, such as LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Period, Duration and their supported APIs.

我们还将查看新的Java 8项目中属于java.time包的一些核心类,如LocalDateLocalTimeLocalDateTime ZonedDateTime Period Duration以及它们支持的API。

2. Issues With the Existing Date/Time APIs

2.现有的日期/时间APIs的问题

  • Thread safety – The Date and Calendar classes are not thread safe, leaving developers to deal with the headache of hard-to-debug concurrency issues and to write additional code to handle thread safety. On the contrary, the new Date and Time APIs introduced in Java 8 are immutable and thread safe, thus taking that concurrency headache away from developers.
  • API design and ease of understanding – The Date and Calendar APIs are poorly designed with inadequate methods to perform day-to-day operations. The new Date/Time API is ISO-centric and follows consistent domain models for date, time, duration and periods. There are a wide variety of utility methods that support the most common operations.
  • ZonedDate and Time – Developers had to write additional logic to handle time-zone logic with the old APIs, whereas with the new APIs, handling of time zone can be done with Local and ZonedDate/Time APIs.

3. Using LocalDate, LocalTime and LocalDateTime

3.使用LocalDateLocalTimeLocalDateTime

The most commonly used classes are LocalDate, LocalTime and LocalDateTime. As their names indicate, they represent the local date/time from the context of the observer.

最常用的类是LocalDateLocalTimeLocalDateTime。正如它们的名字所示,它们代表了来自观察者上下文的本地日期/时间。

We mainly use these classes when time zones are not required to be explicitly specified in the context. As part of this section, we will cover the most commonly used APIs.

当不需要在上下文中明确指定时区时,我们主要使用这些类。作为本节的一部分,我们将介绍最常用的API。

3.1. Working With LocalDate

3.1.使用LocalDate

The LocalDate represents a date in ISO format (yyyy-MM-dd) without time. We can use it to store dates like birthdays and paydays.

LocalDate代表ISO格式的日期(yyyy-MM-dd),不含时间。我们可以用它来存储生日和工资日等日期。

An instance of current date can be created from the system clock:

可以从系统时钟中创建一个当前日期的实例。

LocalDate localDate = LocalDate.now();

And we can get the LocalDate representing a specific day, month and year by using the of method or the parse method.

而我们可以通过使用of方法或parse方法获得代表特定日期、月份和年份的LocalDate

For example, these code snippets represent the LocalDate for February 20, 2015:

例如,这些代码片段表示2015年2月20日的LocalDate

LocalDate.of(2015, 02, 20);

LocalDate.parse("2015-02-20");

The LocalDate provides various utility methods to obtain a variety of information. Let’s have a quick peek at some of these API methods.

The LocalDate提供了各种实用方法来获取各种信息。让我们快速浏览一下这些API方法中的一些。

The following code snippet gets the current local date and adds one day:

下面的代码片段获得了当前的本地日期并增加了一天。

LocalDate tomorrow = LocalDate.now().plusDays(1);

This example obtains the current date and subtracts one month. Note how it accepts an enum as the time unit:

这个例子获得了当前日期并减去了一个月。注意它是如何接受一个enum作为时间单位的。

LocalDate previousMonthSameDay = LocalDate.now().minus(1, ChronoUnit.MONTHS);

In the following two code examples, we parse the date “2016-06-12” and get the day of the week and the day of the month respectively. Note the return values — the first is an object representing the DayOfWeek, while the second is an int representing the ordinal value of the month:

在下面两个代码例子中,我们解析了日期 “2016-06-12″,并分别得到星期和月份的日期。注意返回值–第一个是代表DayOfWeek的对象,而第二个是代表月份序数的int

DayOfWeek sunday = LocalDate.parse("2016-06-12").getDayOfWeek();

int twelve = LocalDate.parse("2016-06-12").getDayOfMonth();

We can test if a date occurs in a leap year, for example the current date:

我们可以测试一个日期是否发生在闰年,例如当前日期。

boolean leapYear = LocalDate.now().isLeapYear();

Also, the relationship of a date to another can be determined to occur before or after another date:

另外,一个日期与另一个日期的关系可以被确定为发生在另一个日期之前或之后。

boolean notBefore = LocalDate.parse("2016-06-12")
  .isBefore(LocalDate.parse("2016-06-11"));

boolean isAfter = LocalDate.parse("2016-06-12")
  .isAfter(LocalDate.parse("2016-06-11"));

Finally, date boundaries can be obtained from a given date.

最后,可以从一个给定的日期中获得日期界限。

In the following two examples, we get the LocalDateTime that represents the beginning of the day (2016-06-12T00:00) of the given date and the LocalDate that represents the beginning of the month (2016-06-01) respectively:

在下面两个例子中,我们分别得到了代表给定日期的一天的开始(2016-06-12T00:00)的LocalDateTime和代表一个月的开始(2016-06-01)的LocalDate

LocalDateTime beginningOfDay = LocalDate.parse("2016-06-12").atStartOfDay();
LocalDate firstDayOfMonth = LocalDate.parse("2016-06-12")
  .with(TemporalAdjusters.firstDayOfMonth());

Now let’s have a look at how we work with local time.

现在让我们来看看我们如何使用本地时间。

3.2. Working With LocalTime

3.2.使用LocalTime

The LocalTime represents time without a date.

本地时间代表没有日期的时间

Similar to LocalDate, we can create an instance of LocalTime from the system clock or by using parse and of methods.

LocalDate类似,我们可以从系统时钟或通过使用parseof方法创建一个LocalTime的实例。

We’ll now take a quick look at some of the commonly used APIs.

现在我们将快速浏览一下一些常用的API。

An instance of current LocalTime can be created from the system clock:

可以从系统时钟创建一个当前LocalTime的实例。

LocalTime now = LocalTime.now();

We can create a LocalTime representing 6:30 a.m. by parsing a string representation:

我们可以通过解析一个字符串表示法来创建一个LocalTime 表示上午6:30。

LocalTime sixThirty = LocalTime.parse("06:30");

The factory method of can also be used to create a LocalTime. This code creates LocalTime representing 6:30 a.m. using the factory method:

工厂方法of也可用于创建LocalTime。这段代码使用工厂方法创建了代表上午6:30的LocalTime

LocalTime sixThirty = LocalTime.of(6, 30);

Let’s create a LocalTime by parsing a string and adding an hour to it by using the “plus” API. The result would be LocalTime representing 7:30 a.m.:

让我们创建一个LocalTime,通过解析一个字符串,并通过使用 “加 “API为其添加一个小时。结果将是LocalTime,代表上午7:30。

LocalTime sevenThirty = LocalTime.parse("06:30").plus(1, ChronoUnit.HOURS);

Various getter methods are available that can be used to get specific units of time like hour, min and secs:

有各种getter方法,可以用来获取特定的时间单位,如小时、分钟和秒。

int six = LocalTime.parse("06:30").getHour();

We can also check if a specific time is before or after another specific time. This code sample compares two LocalTime for which the result would be true:

我们还可以检查一个特定的时间是否在另一个特定的时间之前或之后。这个代码样本比较了两个LocalTime,其结果将是真的。

boolean isbefore = LocalTime.parse("06:30").isBefore(LocalTime.parse("07:30"));

Finally, the max, min and noon time of a day can be obtained by constants in LocalTime class. This is very useful when performing database queries to find records within a given span of time.

最后,一天的最大、最小和正午时间可以通过LocalTime类中的常数来获得。这在执行数据库查询以寻找特定时间跨度内的记录时非常有用。

For example, the below code represents 23:59:59.99:

例如,下面的代码代表23:59:59.99。

LocalTime maxTime = LocalTime.MAX

Now let’s dive into LocalDateTime.

现在让我们深入了解一下LocalDateTime

3.3. Working With LocalDateTime

3.3.使用LocalDateTime

LocalDateTime is used to represent a combination of date and time. This is the most commonly used class when we need a combination of date and time.

LocalDateTime用于表示日期和时间的组合。当我们需要一个日期和时间的组合时,这是最常用的类。

The class offers a variety of APIs. Here, we’ll look at some of the most commonly used ones.

该类提供了各种API。在这里,我们将看一下一些最常用的。

An instance of LocalDateTime can be obtained from the system clock similar to LocalDate and LocalTime:

LocalDateTime的实例可以从系统时钟中获得,与LocalDateLocalTime类似。

LocalDateTime.now();

The below code samples explain how to create an instance using the factory “of” and “parse” methods. The result would be a LocalDateTime instance representing February 20, 2015, 6:30 a.m.:

下面的代码示例解释了如何使用工厂的 “of “和 “parse “方法来创建一个实例。其结果将是一个LocalDateTime实例,代表2015年2月20日上午6:30。

LocalDateTime.of(2015, Month.FEBRUARY, 20, 06, 30);
LocalDateTime.parse("2015-02-20T06:30:00");

There are utility APIs to support addition and subtraction of specific units of time like days, months, years and minutes.

有一些实用的API来支持特定时间单位的加减法,如日、月、年和分钟。

The below code demonstrates the “plus” and “minus” methods. These APIs behave exactly like their counterparts in LocalDate and LocalTime:

下面的代码演示了 “加 “和 “减 “方法。这些API的行为与LocalDateLocalTime中的对应内容完全相同。

localDateTime.plusDays(1);
localDateTime.minusHours(2);

Getter methods are also available to extract specific units similar to the date and time classes. Given the above instance of LocalDateTime, this code sample will return the month February:

Getter方法也可以用来提取与日期和时间类类似的特定单位。鉴于上述LocalDateTime的实例,这个代码示例将返回2月份。

localDateTime.getMonth();

4. Using ZonedDateTime API

4.使用ZonedDateTime API

Java 8 provides ZonedDateTime when we need to deal with time-zone-specific date and time. The ZoneId is an identifier used to represent different zones. There are about 40 different time zones, and the ZoneId represents them as follows.

Java 8提供了ZonedDateTime当我们需要处理特定时区的日期和时间时。ZoneId是一个标识符,用于表示不同的区。有大约40个不同的时区,ZoneId表示它们如下。

Here, we create a Zone for Paris:

在这里,我们为巴黎创建一个

ZoneId zoneId = ZoneId.of("Europe/Paris");

And we can get a set of all zone ids:

而我们可以得到一组所有区域的ID。

Set<String> allZoneIds = ZoneId.getAvailableZoneIds();

The LocalDateTime can be converted to a specific zone:

本地日期时间(LocalDateTime)可以被转换为一个特定的区域。

ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, zoneId);

The ZonedDateTime provides the parse method to get time-zone-specific date-time:

The ZonedDateTime提供了parse方法来获取特定时区的日期时间。

ZonedDateTime.parse("2015-05-03T10:15:30+01:00[Europe/Paris]");

Another way to work with time zone is by using OffsetDateTime. The OffsetDateTime is an immutable representation of a date-time with an offset. This class stores all date and time fields, to a precision of nanoseconds, as well as the offset from UTC/Greenwich.

另一种处理时区的方法是通过使用OffsetDateTimeOffsetDateTime是一个带有偏移量的不可变的日期时间表示。这个类存储所有的日期和时间字段,精度为纳秒,以及与UTC/格林威治的偏移。

The OffSetDateTime instance can be created using ZoneOffset. Here, we create a LocalDateTime representing 6:30 a.m. on February 20, 2015:

OffSetDateTime实例可以使用ZoneOffset创建。在这里,我们创建一个LocalDateTime,代表2015年2月20日上午6:30。

LocalDateTime localDateTime = LocalDateTime.of(2015, Month.FEBRUARY, 20, 06, 30);

Then we add two hours to the time by creating a ZoneOffset and setting for the localDateTime instance:

然后,我们通过为localDateTime实例创建一个ZoneOffset和设置,在时间上增加两个小时。

ZoneOffset offset = ZoneOffset.of("+02:00");

OffsetDateTime offSetByTwo = OffsetDateTime
  .of(localDateTime, offset);

We now have a localDateTime of 2015-02-20 06:30 +02:00.

我们现在有一个localDateTime为2015-02-20 06:30 +02:00。

Now let’s move on to how to modify date and time values using the Period and Duration classes.

现在让我们继续讨论如何使用PeriodDuration类来修改日期和时间值。

5. Using Period and Duration

5.使用PeriodDuration

The Period class represents a quantity of time in terms of years, months and days, and the Duration class represents a quantity of time in terms of seconds and nanoseconds.

周期类表示以年、月、日为单位的时间量,期限类表示以秒和纳秒为单位的时间量。

5.1. Working With Period

5.1.使用Period

The Period class is widely used to modify values of given a date or to obtain the difference between two dates:

Period类被广泛用于修改给定日期的值或获得两个日期之间的差异。

LocalDate initialDate = LocalDate.parse("2007-05-10");

We can manipulate the Date by using Period:

我们可以通过使用Period来操作Date

LocalDate finalDate = initialDate.plus(Period.ofDays(5));

The Period class has various getter methods such as getYears, getMonths and getDays to get values from a Period object.

Period类有各种getter方法,如getYears,getMonthsgetDays来从Period对象中获取值。

For example, this returns an int value of 5 as we try to get difference in terms of days:

例如,这将返回一个5的int值,因为我们试图获得天数的差异

int five = Period.between(initialDate, finalDate).getDays();

We can get the Period between two dates in a specific unit such as days or months or years, using ChronoUnit.between:

我们可以使用ChronoUnit.between获得两个日期之间的周期,以一个特定的单位,如天、月或年。

long five = ChronoUnit.DAYS.between(initialDate, finalDate);

This code example returns five days.

这段代码示例返回5天。

Let’s continue by taking a look at the Duration class.

让我们继续看一下Duration类。

5.2. Working With Duration

5.2.使用Duration工作

Similar to Period, the Duration class is used to deal with Time.

Period类似,Duration类被用来处理Time.

Let’s create a LocalTime of 6:30 a.m. and then add a duration of 30 seconds to make a LocalTime of 6:30:30 a.m.:

让我们创建一个LocalTime为6:30 a.m.,然后添加一个30秒的持续时间,使LocalTime为6:30:30 a.m. 。

LocalTime initialTime = LocalTime.of(6, 30, 0);

LocalTime finalTime = initialTime.plus(Duration.ofSeconds(30));

We can get the Duration between two instants as either a Duration or a specific unit.

我们可以得到两个瞬间之间的持续时间,作为持续时间或一个特定的单位。

First, we use the between() method of the Duration class to find the time difference between finalTime and initialTime and return the difference in seconds:

首先,我们使用Duration类的between()方法来查找finalTimeinitialTime之间的时间差,并返回差值(秒)。

long thirty = Duration.between(initialTime, finalTime).getSeconds();

In the second example, we use the between() method of the ChronoUnit class to perform the same operation:

在第二个例子中,我们使用ChronoUnit类的between()方法来执行同样的操作。

long thirty = ChronoUnit.SECONDS.between(initialTime, finalTime);

Now we’ll look at how to convert existing Date and Calendar to new Date/Time.

现在我们来看看如何将现有的DateCalendar转换成新的Date/Time

6. Compatibility With Date and Calendar

6.与DateCalendar兼容

Java 8 has added the toInstant() method, which helps to convert existing Date and Calendar instance to new Date and Time API:

Java 8增加了toInstant()方法,它有助于将现有的DateCalendar实例转换为新的日期和时间API。

LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
LocalDateTime.ofInstant(calendar.toInstant(), ZoneId.systemDefault());

The LocalDateTime can be constructed from epoch seconds. The result of the below code would be a LocalDateTime representing 2016-06-13T11:34:50:

LocalDateTime可以由纪元秒构建。以下代码的结果将是一个代表2016-06-13T11:34:50的LocalDateTime

LocalDateTime.ofEpochSecond(1465817690, 0, ZoneOffset.UTC);

Now let’s move on to Date and Time formatting.

现在让我们继续讨论日期时间格式化。

7. Date and Time Formatting

7.日期时间格式化

Java 8 provides APIs for the easy formatting of Date and Time:

Java 8提供了一些API,使日期时间的格式化变得简单:

LocalDateTime localDateTime = LocalDateTime.of(2015, Month.JANUARY, 25, 6, 30);

This code passes an ISO date format to format the local date, with a result of 2015-01-25:

这段代码通过ISO日期格式来格式化本地日期,其结果是2015-01-25。

String localDateString = localDateTime.format(DateTimeFormatter.ISO_DATE);

The DateTimeFormatter provides various standard formatting options.

DateTimeFormatter提供了各种标准格式化选项。

Custom patterns can be provided to the format method as well, which here returns a LocalDate as 2015/01/25:

自定义模式也可以提供给format方法,这里返回一个LocalDate为2015/01/25。

localDateTime.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));

We can pass in formatting style either as SHORT, LONG or MEDIUM as part of the formatting option.

我们可以将格式化风格作为SHORTLONGMEDIUM的一部分传入格式化选项。

For example, this would give an output representing LocalDateTime in 25-Jan-2015, 06:30:00:

例如,这将给出一个代表LocalDateTime的输出,即2015年1月25日,06:30:00。

localDateTime
  .format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
  .withLocale(Locale.UK));

Let’s take a look at alternatives available to Java 8 Core Date/Time APIs.

让我们来看看Java 8 Core Date/Time API的可用替代品。

8. Backport and Alternate Options

8.倒车和备用方案

8.1. Using the ThreeTen Project

8.1.使用ThreeTen项目

For organizations that are on the path of moving to Java 8 from Java 7 or Java 6 and that want to use date and time API, the ThreeTen project provides the backport capability.

对于那些正处于从 Java 7 或 Java 6 向 Java 8 转移的过程中并且希望使用日期和时间 API 的组织,ThreeTen项目提供了回传功能。

Developers can use classes available in this project to achieve the same functionality as that of new Java 8 Date and Time APIs. And once they move to Java 8, the packages can be switched.

开发人员可以使用这个项目中的类来实现与新的Java 8 DateTime APIs相同的功能。而一旦他们转移到Java 8,这些包就可以被切换。

The artifact for the ThreeTen project can be found in the Maven Central Repository:

ThreeTen项目的工件可以在Maven中央仓库找到。

<dependency>
    <groupId>org.threeten</groupId>
    <artifactId>threetenbp</artifactId>
    <version>1.3.1</version>
</dependency>

8.2. Joda-Time Library

8.2.Joda-时间库

Another alternative for Java 8 Date and Time library is Joda-Time library. In fact, the Java 8 Date/Time API has been led jointly by the author of Joda-Time library (Stephen Colebourne) and Oracle. This library provides pretty much all capabilities that are supported in the Java 8 Date/Time project.

Java 8 DateTime库的另一个选择是Joda-Time库。事实上,Java 8 Date/Time API 是由 Joda-Time 库的作者(Stephen Colebourne)和 Oracle 共同领导的。这个库提供了几乎所有在Java 8 Date/Time项目中支持的功能。

The artifact can be found in Maven Central by including the below pom dependency in our project:

该工件可以在Maven Central中找到,方法是在我们的项目中加入以下pom依赖项。

<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.9.4</version>
</dependency>

9. Conclusion

9.结论

Java 8 provides a rich set of APIs with consistent API design for easier development.

Java 8提供了一套丰富的API,其一致的API设计使开发更加容易。

The code samples for the above article can be found in the Java 8 Date/Time git repository.

上述文章的代码样本可以在Java 8 Date/Time git资源库中找到。