11 CompletableFuture: 组合式异步编程
12 新的日期和时间API
- Date 和 Calendar这两个类增加了程序员的困惑,到底该使用哪一个类?此外,有的特性只在某一个类有提供,比如用于以语言无关方式格式化和解析日期或时间的 DateFormat 方法就只在 Date 类里有。
- DateFormat 不是线程安全的。这意味着两个线程如果尝试使用同一个 formatter 解析日期,可能会得到无法预期的结果。
- Date 和 Calendar 类都是可以变的。这种设计会将难以维护。
- 为了解决这些问题,Oracle 决定在原生的 Java API 中提供高质量的日期和时间支持。Java 8 在
java.time
包中整合了很多Joda-Time
的特性。
12.1 主要类
LocalDate、LocalTime、Instant、Duration *以及 *Period
12.1.1 使用LocalDate和LocalTime
创建一个 LocalDate 对象并读取其值。
1 | LocalDate date = LocalDate.of(2021, 2, 5); |
也可以通过传递一个 TemporalField
参数给 get
方法拿到同样的信息。ChronoField
枚举实现了 TemporalField
接口。
1 | int year1 = date.get(ChronoField.YEAR); |
类似地,一天中的时间,比如 13:45:20,可以使用 LocalTime
类表示。
1 | LocalTime time = LocalTime.of(13, 45, 20); |
LocalDate
和 LocalTime
都可以通过静态方法 parse
解析代表字符串创建。可以向 parse
方法传递一个 DateTimeFormatter
。
1 | LocalDate date = LocalDate.parse("2014-03-18"); |
12.1.2 合并日期和时间
LocalDateTime,是 LocalDate 和 LocalTime 的合体,不带有时区信息,可以直接创建,也可以通过合并日期和时间对象创建。
1 | LocalDateTime dt1 = LocalDateTime.of(2021, Month.FEBRUARY, 5, 17, 21, 20); |
12.1.3 机器日期和时间
可以通过向静态工厂方法 ofEpochSecond
传递一个代表秒数的值创建一个该类的实例。 ofEpochSecond
的重载版本,接收第二个以纳秒为单位的参数值,对传入作为秒数的参数进行调整。重载的版本会调整纳秒参数,确保保存的纳秒分片在 0 到 999 999 999 之间。下面几个方法都会返回几乎同样的 Instant
对象。
1 | Instant.ofEpochSecond(3); |
Instant 设计初衷是便于机器使用。它包含的是由秒及纳秒所构成的数字。所以,它无法处理常见的的时间单位,日月年等。
12.1.4 定义Duration或Period
在本节之前出现的所有类都实现了 Temporal
接口。Duration
类的静态工厂方法 between
可以创建两个 Temporal
对象之间的间隔。
如果需要以年、月或者日的方式对多个时间单位建模,可以使用 Period
类。使用该类的工厂方法 between
,可以使用得到两个 LocalDate
之间的时间间隔。
1 | Period tenDays = Period.between(LocalDate.of(2021, 2, 5), LocalDate.of(2021, 2, 15)); |
Duration 与 Period 类有许多的共享方法,如果用到的话当然还是去看源码,在这列表格也没什么用。
上面的这些日期和时间对象都是不可修改的,这是为了更好地支持函数式编 程,确保线程安全。
12.2 操纵、解析和格式化日期
withAttribute
方法会创建对象的一个副本,并按照需要修改它的属性。下面的代码都返回一个修改了属性的对象,它们都不会修改原来的对象。
1 | LocalDate date1 = LocalDate.of(2021, 2, 5); |
使用 get
和 with
方法,可以将 Temporal
对象值的读取和修改区分开。还能以声明的方式操纵LocalDate对象。
1 | LocalDate date1 = LocalDate.of(2021, 2, 5); |
表示时间点的日期-时间类的通用方法,用到的时候当然还是 Ctrl + Click
。
12.2.1 使用TemporalAdjuster
有时候需要进行一些复杂操作,比如,将日期调整到下个周日、下个工作日,或者是本月的最后一天。可以使用重载版本的 with
方法,向其传递一个提供了更多定制化选择的 TemporalAdjuster
对象。
1 | // 周五 |
容易发现 TemporalAdjuster
是一个函数式接口。
1 |
|
这意味着 TemporalAdjuster
接口的实现需要定义如何将一个 Temporal
对象转换为另一 个 Temporal
对象。可以看成一个 UnaryOperator<Temporal>
。
设计一个
NextWorkingDay
类,该类实现了TemporalAdjuster
接口,能够计算明天的日期,同时过滤掉周六和周日这些节假日。
12.2.2 打印输出及解析日期-时间对象
1 | LocalDate date = LocalDate.of(2021, 2, 5); |
按照某个格式创建 DateTimeFormatter
。
1 | DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy"); |
12.3 处理不同的时区和历法
没什么重要的内容