Java 8 日期/时间 API 简介

Java 8 为Date和Time引入了新的 API,以解决旧 java.util.Date 和 java.util.Calendar 的缺点。

现有日期/时间API的问题

  • 线程安全 – Date和Calendar类不是线程安全的,这让开发人员不得不处理难以调试的并发问题,并编写额外的代码来处理线程安全。相反,Java 8 中引入的新日期和时间API 是不可变的和线程安全的,从而使开发人员摆脱了并发问题。
  • API 设计和易于理解 – 日期和日历API 设计不佳,执行日常操作的方法不足。新的日期/时间API 以 ISO 为中心,并遵循日期、时间、持续时间和期间的一致性模型。有各种各样的实用程序方法支持最常见的操作。
  • ZonedDate和Time – 使用旧 API,开发人员必须编写额外的逻辑来处理时区,而使用新 API,可以使用Local和ZonedDate / Time API 来处理时区。

本地日期/时间

JAVA8 中最常用的日期类是 LocalDate、LocalTime和LocalDateTime。正如它们的名字所表明的,它们代表了观察者上下文中的本地日期/时间。

LocalDate

LocalDate表示不带时间的 ISO 格式 (yyyy-MM-dd) 的日期。我们可以用它来存储生日和入职日期等。

可以从系统时钟创建当前日期的实例:

LocalDate localDate = LocalDate.now();

我们可以通过 of() 方法或parse()方法获取表示特定日、月和年的 LocalDate。

LocalDate.of(2022, 04, 20);

LocalDate.parse("2022-04-20");

还可以通过一些 API 来进行时间计算:

明天

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

上个月

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

字符串时间星期几

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

字符串时间几号

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

是否闰年

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

LocalTime

LocalTime表示没有日期的时间。

可以从系统时钟创建当前时间的实例:

LocalTime now = LocalTime.now();

我们可以通过 of() 方法或parse()方法获取表示特定时间的 LocalTime。

LocalTime.of(10, 28);

LocalTime.parse("06:30");

还可以通过一些 API 来进行时间计算:

一个小时后

LocalTime nextHour = LocalTime.now().plus(1, ChronoUnit.HOURS);

几点

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

一天的最大、最小和中午时间

LocalTime maxTime = LocalTime.MAX;  // 23:59:59.99

LocalDateTime

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

可以从系统时钟创建当前时间的实例:

LocalDateTime now = LocalDateTime.now();

我们可以通过 of() 方法或parse()方法获取表示特定时间的 LocalDateTime。

LocalDateTime.of(2022, Month.FEBRUARY, 22, 06, 30);

LocalDateTime.parse("2022-02-22T06:30:00");

LocalDateTime 的 API 与 LocalDate 和 LocalTime 中的 API 重合度非常高。

ZonedDateTime

ZonedDateTime 主要处理与时区有关的时间,其中 ZoneId 指代了近 40 个不同的时区。

对于法国巴黎的时区:

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

中国的时区:

ZoneId zoneId = ZoneId.of("Asia/Shanghai");

获取所有可用的时区:

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

将 LocalDateTime 转换为特定区域:

private static void printZoneDate(){
    LocalDateTime dateTime = LocalDateTime.now();
    ZonedDateTime zonedDateTime = ZonedDateTime.of(dateTime, ZoneId.of("Europe/Paris"));
    System.out.println("原始 " + dateTime);
    System.out.println("时区 " + zonedDateTime);
}
原始 2022-04-04T21:50:59.063
时区 2022-04-04T21:50:59.063+02:00[Europe/Paris]

ZonedDateTime提供parse方法来获取特定于时区的日期时间:

ZonedDateTime.parse("2022-04-04T21:50:59.063+02:00[Europe/Paris]");

使用时区的另一种方法是使用 OffsetDateTime。OffsetDateTime 是具有偏移量的日期时间的不可变类。此类存储所有日期和时间字段,精度为纳秒,以及与 UTC/格林威治的偏移量。

使用 ZoneOffset 给 LocalDateTime 添加 8个小时的偏移量:

ZoneOffset offset = ZoneOffset.of("+08:00");
LocalDateTime dateTime = LocalDateTime.now();
OffsetDateTime offSetByTwo = OffsetDateTime.of(dateTime, offset);

期间与持续时间

Period 类表示以年、月和日表示的时间量,而 Duration 类表示以秒和纳秒为单位的时间量。

期间 Period

使用 Period 来操作日期 Date:

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

Period类有各种 getter 方法,例如getYears、getMonths和getDays来从Period对象中获取值。

例如,当我们尝试获取天数的差异时,这将返回一个int值:

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

我们可以使用ChronoUnit.between以特定单位(例如天、月或年)获取两个日期之间的 Period:

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

持续时间 Duration

Duration 类用于处理时间 Time。

给一个上午 9:20 的时间,添加 30 秒:

LocalTime time = LocalTime.of(9, 20, 0);

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

使用Duration类的 between() 方法计算两个时间的时间差:

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

上面的场景,也可以改写为:

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

旧时间类转为新时间类

Java 8 添加了toInstant()方法,该方法有助于将现有的Date和Calendar实例转换为新的 Date 和 Time API:

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

日期和时间格式化

Java 8 提供了用于轻松格式化Date和Time的 API。

把时间格式化为 ISO 日期格式:

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

格式化的结果为 2022-04-05

也可以自定义返回格式:

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

格式化的结果为 2022/04/05

临时方案

Java 8日期/时间 API 由 Joda-Time 库的作者(Stephen Colebourne)和 Oracle 共同领导,Joda-Time 库几乎支持 Java 8 中新加 API 的所有能力。如果你的项目还不能迁移到 Java8 以上的版本,那么才用 Joda-Time 库作为临时方案会是一个不错的选择。

要想在项目中使用 Joda-Time,只需要添加依赖到 pom.xml 中即可:

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

当然,如果你不想为了使用新的时间 API 而学习 Joda-Time 的 API,还有一个库可以尝试,即 ThreeTen。该项目采用了与 Java8 相同的 API,如果在低版本的 JDK 中使用它,当项目迁移到 JDK8 以上后,只需要修改包路径,即可迁移完成。

要想在项目中使用 ThreeTen,只需要添加依赖到 pom.xml 中即可:

<dependency>
    <groupId>org.threeten</groupId>
    <artifactId>threetenbp</artifactId>
    <version>1.3.1</version>
</dependency>
转载请注明出处:码谱记录 » Java 8 日期/时间 API 简介
标签: