Java 标准库 API 系列之 java.time 包

一位 new Date()大法好的老顽固的真香之旅。

new Date()大法和new SimpleDateFormat()等API是从我2018年学习Java开始就一直在使用的处理日期和格式化日期的神器,直到几个月前我还是喜欢用他们,但是突然遇到一个比较复杂并且涉及用户分布在全球的报表需求时,我开始退坑了。。。

我开始使用java.time包下的API,真香~~~

zs.gif

在 Java 8 之前,java.util.Datejava.util.Calendar是用于表示日期和时间的类。但是这两个工具类 存在很多问题,例如:

  • DateCalender是可变的,这意味着它不是线程安全的。
  • java.util.Date 的时间表示只能精确到毫秒,不能精确到更小的时间单位。
  • API 设计相对复杂且容易出错。
    • Calender使用了许多常量和魔法值,不够直观。例如,月份从 0 开始计数,这与我们通常的习惯不同,容易导致错误。
    • Date 存储的时间是从 1970-01-01T00:00:00Z(即格林威治时间)开始经过的时间,但是它的 API 设计存在很多问题,例如 getYear() 方法返回的是从 1900 年开始经过的年数,导致其易于出现错误。

为了解决这些问题,Java 8 引入了新的日期时间 API(即 java.time 包)也就是这篇文章要介绍和推广的,其中的日期时间类是不可变的、线程安全的,并且支持更精确的时间单位。这些类包括:

  • LocalDate:表示日期(年月日)。
  • LocalTime:表示时间(小时分钟秒)。
  • LocalDateTime:表示日期和时间。
  • ZonedDateTime:表示带有时区信息的日期和时间。
  • Instant:表示从 1970-01-01T00:00:00Z(即格林威治时间)开始经过的时间,精确到纳秒。
  • Duration:表示时间段。
  • Period:表示日期段。

接下来我就一个一个的介绍这些API的使用,觉得有帮助的话可以点个赞哦!

Instant

这个类代表了一个精确的时间点。它的实例可以用于表示一个事件的发生时间,以便于在不同时区之间进行转换。示例代码如下:

Instant instant = Instant.now();

Date也可以转换成Instant,如下:

var instant = new Date().toInstant();

LocalDate

这个类代表了一个日期,不含有具体时间信息。它提供了一系列的静态工厂方法,可以根据年月日的值创建实例。示例代码如下:

LocalDate date = LocalDate.of(2023, 3, 21);

LocalTime

这个类代表了一个时间,不含有具体日期信息。它同样提供了一系列的静态工厂方法,可以根据时分秒的值创建实例。示例代码如下:

LocalTime time = LocalTime.of(12, 30);

LocalDateTime

这个类同时包含了日期和时间信息。可以通过合并 LocalDateLocalTime的实例来创建 LocalDateTime 实例。示例代码如下:

LocalDateTime dateTime = LocalDateTime.of(2023, 3, 21, 12, 30);

也可以从Date转换成LocalDateTime

var instant = new Date().toInstant();
var localDateTime = instant.atZone(ZoneId.systemDefault()).toLocalDateTime();

ZoneDateTime

ZoneDateTimeLocalDateTime的基础上加上了时区信息。也就是说,它可以使用 LocalDateTime 类中所有的方法和属性。这让我们在处理一些全球性的业务是可以如鱼得水。

ZonedDateTime 类中,我们可以通过 ZoneId 类来设置时区。ZoneId是一个表示时区的类,它包含所有的时区信息,并且提供了多种获取时区的方法。以下是一些常用的 ZoneId 的用法:

// 通过时区ID获取一个时区对象
ZoneId zoneId = ZoneId.of("Asia/Shanghai");

// 获取默认时区对象
ZoneId defaultZoneId = ZoneId.systemDefault();

可以看到,我们可以通过指定时区ID来获取对应的时区对象,也可以使用 systemDefault() 方法获取当前默认的时区对象。

然后,我们可以使用 ZonedDateTime 类来表示具有时区信息的日期和时间。下面是一个创建 ZonedDateTime 实例的示例代码:

// 获取当前时间
LocalDateTime localDateTime = LocalDateTime.now();

// 使用指定时区将当前时间转换为带时区的日期时间
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, zoneId);

Duration 和 Period

这两个类分别代表了时间段和日期段。Duration 可以精确表示秒和纳秒之间的时间差,而 Period 可以精确表示年、月、日之间的时间差。示例代码如下:

Duration duration = Duration.between(LocalTime.now(), LocalTime.of(18, 0));
Period period = Period.between(LocalDate.now(), LocalDate.of(2023, 3, 30));

DateTimeFormatter

这个类提供了一系列的静态方法,可以将日期和时间对象格式化为字符串,也可以将字符串解析为日期和时间对象。再也不用new SimpleDateFormat()了。

示例代码如下:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH🇲🇲ss");
String strDateTime = dateTime.format(formatter);
LocalDateTime parsedDateTime = LocalDateTime.parse(strDateTime, formatter);

方便的API

java.time提供的这些工具类,出了明确的职责和优良的设计之外,还提供了非常方便好用的API,简化我们对于日期的计算。

包括但不限于

var today = LocalDate.now();//今天的信息
var yesterday = today.minusDays(1) //昨天的信息
var tomorrow = today.plusDays(1) //明天的信息
var firstDayOfMoth = today.withDayOfMonth(1); //返回这个月的第一天 1-31
var yesterdayStartTime = yesterday.atStartOfDay() //获取昨天零点的LocalDateTime对象
var dayOfYear = today.getDayOfWeek() //获取今天是这周中的第几天
var dayOfYear = today.getDayOfMonth() //获取今天是这个月中的第几天
var dayOfYear = today.getDayOfYear() //获取今天是一年中的第几天