【Java常用类】1-9 JDK 8 新增的时间类
§1-9 JDK 8 新增的时间类
1-9.1 总览
以上一节内容中的 “限时秒杀活动” 案例来看,若使用 JDK 7 的时间类,代码量较大,且操作麻烦,不方便。JDK 8 所新增的时间类可以有效地减少代码量,并提供一些很有用、很方便的方法供我们使用。
与 JDK 7 的时间类相比,JDK 8 新增的时间类:
- 代码层面:JDK 7 代码麻烦,日期对象都需要使用其毫秒值才可用于计算和比较;JDK 8 代码简单,提供了用于判断和计算时间间隔的方法;
- 安全层面:JDK 7 的时间类在多线程环境下会导致数据安全的问题,JDK 8 的时间日期对象不可变,解决了此问题;
JDK 8 新增了许多时间类,这里所介绍的类全部位于 java.time
包下,可按照其功能,将它们简单划分为:
- 日期类:
ZoneId
(时区)、Instant
(基于世界标准时的时间戳)、ZonedDateTime
(带时区的日期时间); - 日期格式化类:
DateTimeFormatter
(用于时间日期的格式化和解析); - 日历类:
LocalDate
(年月日)、LocalTime
(时分秒)、LocalDateTime
(年月日、时分秒);(不带时区) - 工具类:
Duration
(时间间隔,秒或纳秒)、Period
(时间间隔,年月日)、ChronoUnit
(时间间隔,所有单位);
这些类的构造器一般都无法在外部调用,但可以通过它们的静态方法返回对应实例。
1-9.2 日期类
新增的日期类十分类似于 Date
类,可以相互比较学习了解。
1-9.2.1 ZoneId
时区
ZoneId
是一个抽象类,用于表示一个时区。
中国位于东八区,比世界标准时间早 8 小时。但 Java 在定义时区时,并不是按照这种方式定义的。Java 定义时区的方式是 Continent/City
或 Country/City
以及其它的定义方式,如 Asia/Shanghai, Asia/Taipei, Asia/Chongqing, Etc/GMT+8
,共计 602 个时区。
静态方法:
静态方法 | 描述 |
---|---|
Set<String> getAvailableZoneIds() |
获取 Java 中所有支持的时区 ID 集合 |
ZoneId systemDefault() |
获取系统默认时区 |
ZoneId of(String zoneId) |
获取一个指定时区 |
注意:
-
getAvailableZoneIds()
方法返回的是一个集合,集合没有索引,直接打印可查看其中元素,使用size()
方法可查看其长度;Set<String> zoneIds = ZoneId.getAvailableZoneIds(); System.out.println(zoneIds.size()); //获取长度 System.out.println(zoneIds); //集合没有索引,此处仅直接打印
-
systemDefault()
方法返回的是系统的默认时区,这个可在操作系统设置中更改,一般而言,使用的是 UTC+08:00,返回Asia/Shanghai
; -
of()
方法在时区参数具有无效格式的情况下会抛出异常DateTimeException
;
1-9.2.2 Instant
时刻(时间戳)
Instant
类用于表示时间轴上一个时刻。该类中存储了一个 long
(Epoch-seconds)和一个 int
(nanosecond-of-second)。
其中,Epoch-seconds 基于标准 Java 时间原点(Standard Java Epoch)1970.1.1 T00:00:00Z,正值表示其后,负值表示其前;nanosecond-of-second 一定为正。
注意:该类构造器是私有的,无法在外部调用构造器。
静态方法:
静态方法 | 描述 |
---|---|
Instant now() |
获取当前系统时钟表示时间的 Instant 对象(标准时间) |
Instant ofEpochMilli(long epochMilli) Instant ofEpochSecond(long epochSecond) Instant ofEpochSecond(long epochSecond, long nanoAdjustment) |
根据自原点的偏移量,创建 Instant 对象 |
常用方法:
方法 | 描述 |
---|---|
ZonedDateTime atZone(ZoneId zone) |
将时刻结合时区,创建一个 ZonedDateTime 对象 |
boolean isAfter/isBefore(Instant otherInstant) |
判断时刻与给定时刻的先后关系 |
Istant minusXxx/plusXxx(long xxxToSubtract/xxxToAdd) |
时刻相减/相加,有 Nanos/Millis/Seconds |
注意:由于表示的时间不可变,返回 Instant
的方法,应当在调用时用新的变量接收。
详见官方文档。
1-9.2.3 ZonedDateTime
带时区的时间
ZonedDateTime
类用于表示一个带有时区的时间,其不可变,线程安全。该类存储所有日期和时间的字段,精度到纳秒等级。
静态方法:
静态方法 | 描述 |
---|---|
ZonedDateTime now() |
返回使用系统时钟和默认时区的当前日期时间 |
ZonedDateTime now(ZoneId zone) |
使用指定的时区,返回当前系统时钟的日期时间 |
ZonedDateTime of...(..., ZoneId zone) |
使用指定数据返回日期和时间 |
ZonedDateTime ofInstant(Instant instant, ZoneId zone) |
使用 Instant 对象创建实例 |
注意:
of()
方法具有很多重载版本,支持年月日时分秒的传参,也支持传入LocalDate, LocalTime
、LocalDateTime
参数;
常用方法:
常用方法 | 描述 |
---|---|
ZonedDateTime with...(...) |
使用指定的新数据,返回一个新的 ZonedDateTime 实例 |
ZonedDateTime plus...(...) |
日期与时间的加法,返回结果实例 |
ZonedDateTime minus...(...) |
日期与时间的减法,返回结果实例 |
注意:
- 由于其值不可变,修改、加减法都会产生一个新对象,调用者不会发生改变;
1-9.3 日期格式化类 DateTimeFormatter
该日期格式化类十分类似于 SimpleDateFormat
类,可以相互比较学习了解。
DateTimeFormatter
类位于 java.time.format
包下,用于打印(格式化)和解析日期时间对象。
更为复杂的格式化器,请见 DateTimeFormatterBuilder
,位于 java.time.format
包下。
静态方法:
静态方法 | 描述 |
---|---|
DateTimeFormatter ofPattern(String pattern) |
使用指定模式创建格式化器 |
DateTimeFormatter ofPattern(String pattern, Locale locale) |
使用指定的地区和模式创建格式化器 |
注意:
- 模式字符串中所使用的标志字母与
SimpleDateFormat
中所使用的大同小异,具体见官方文档;
常用方法:
方法 | 描述 |
---|---|
String format(TemporalAccessor temporal) |
使用格式化器,格式化一个日期时间对象 |
TemporalAccessor parse(CharSequence text) TemporalAccessor parse(CharSequence text, ParsePosition position) |
使用格式化器解析文本,返回一个时间对象 使用格式化器解析文本,提供文本位置控制,返回一个时间对象 |
注意:
TemporalAccessor
是一个接口,实现了此接口的类(部分)有:LocalDate, LocalTime, LocalDateTime, ZonedDateTime
;
1-9.4 日历类
新增的日历类十分类似于 Calendar
类,可以相互比较学习了解。
主要有以下三个类:LocalDate
, LocalTime
, LocalDateTime
。
1-9.4.1 概览
LocalDate
类用于表示无时区的日期,例如2007-12-03
;LocalTime
类用于表示无时区的时间,例如10:15:30
;LocalDateTime
类用于表示无时区的日期和时间,例如2007-12-03T10:15:30
;
1-9.4.2 具有共同用法的方法
三个类都具有一些方法签名相同的方法。
静态方法:
静态方法 | 描述 |
---|---|
LocalDate/LocalTime/LocalDateTime now() |
基于默认时区和当前系统时钟返回对应对象 |
LocalDate/LocalTime/LocalDateTime now(ZoneId zone) |
基于指定时区和当前系统时钟返回对应对象 |
LocalDate of(int int year, Month/int month, int dayOfMonth) LocalTime of(int hour, int minute, int second, int nanoOfSecond) LocalDateTime of(int year, Month/int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond) |
基于指定数据创建对应对象 |
LocalDate/LocalTime/LocalDateTime ofInstant(Instant instant, ZoneId zone) |
基于指定的时刻和时区创建指定对象 |
LocalDate/LocalTime/LocalDateTime parse(CharSequence text) LocalDate/LocalTime/LocalDateTime parse(CharSequence text, DateTimeFormatter formatter) |
基于所给字符串,使用指定格式化器解析得到指定对象 |
注意:
of()
有许多重载,并不一定需要全部的参数,具体详见官方文档;parse()
方法解析出错时,会抛出异常DateTimeParseException
;
实例方法:
方法 | 描述 |
---|---|
getXxx() |
获得指定字段的数据 |
boolean isAfter()/isBefore()/isEqual() |
判断先后顺序 |
minusXxx(xxx)/plusXxx(xxx) |
针对时间日期的加法、减法 |
withXxx(xxx) |
返回修改后的新对象 |
1-9.4.3 LocalDateTime
转换至 LocalDate
或 LocalTime
转换方法:
方法 | 描述 |
---|---|
LocalDate toLocalDate() |
得到日期部分的 LocalDate 对象 |
LocalTime toLocalTime() |
得到时间部分的 LocalTime 对象 |
1-9.4.4 MonthDay
类
MonthDay
类位于 java.time
包下,用于表示月份和日,例如 12-03
。
静态方法:
静态方法 | 描述 |
---|---|
MonthDay now() |
基于默认时区的当前系统时钟返回实例 |
Monthday now(ZoneId zone) |
基于指定时区的当前系统时钟返回实例 |
MonthDay of(Month/int month, int dayOfMonth) |
基于指定日期构造实例 |
MonthDay parse(CharSequence text) MonthDay parse(CharSequence text, DateTimeFormatter formatter) |
基于给定字符串,使用指定的格式化器解析,返回实例 |
实例方法:
方法 | 描述 |
---|---|
LocalDate atYear(int year) |
结合指定年份,返回一个 LocalDate 实例 |
getXxx(xxx) |
返回指定字段 |
boolean isAfter/isBefore/equals(Monthday other) |
判断先后顺序/是否相等 |
boolean isValidYear(int year) |
判断年份对于月日日期是否有效 |
withXxx(xxx) |
修改指定字段,并返回修改对象 |
minusXxx/plusXxx(xxx) |
时间的加减法,返回结果对象 |
1-9.5 工具类
本节内容所介绍的工具类,主要用于计算时间间隔。
本节介绍三个工具类:Duration
, Period
, ChronoUnit
。
1-9.5.1 概览
Duration
类主要用于计算秒 / 纳秒间隔;Period
类主要用于计算年月日间隔;ChronoUnit
枚举类主要用于检查时间单位、计算时间间隔(带单位);
1-9.5.2 三个类的常用方法
通常来说,我们常用它们的静态方法直接获取它们的实例,得出时间差。
静态方法:
静态方法 | 描述 |
---|---|
Duration between(Temporal startInclusive, Temporal endExclusive) |
返回两个时间对象所示时刻的持续时间(间隔) |
Period between(LocalDate startDateInclusive, LocalDate endDateExclusive) |
返回两个日期之间的年月日间隔实例 |
ChronoUnit between(Temporal temporal1Inclusive, Temporal temporal2Exclusive) |
返回两个时间之间的间隔实例 |
注意:
-
Temporal
是一个接口,其中实现了该接口的部分类有Instant
,ZonedDateTime
,LocalDate
,LocalTime
,LocalDateTime
; -
从方法的形参名字可知,计算结果由后者减去前者得到,也是含头不含尾;
-
ChronoUnit
中包含了许多表示不同时间单位的枚举常量,可以通过这些枚举常量调用between()
计算时间间隔;LocalDateTime now = LocalDateTime.now(); LocalDateTime epoch = LocalDateTime.ofInstant(Instant.EPOCH, ZoneId.systemDefault()); System.out.println("经历纪元数:" + ChronoUnit.ERAS.between(epoch, now)); System.out.println("经历千年数:" + ChronoUnit.MILLENNIA.between(epoch, now)); System.out.println("经历百年数:" + ChronoUnit.CENTURIES.between(epoch, now)); System.out.println("经历十年数:" + ChronoUnit.DECADES.between(epoch, now)); System.out.println("经历年数:" + ChronoUnit.YEARS.between(epoch, now)); System.out.println("经历月数:" + ChronoUnit.MONTHS.between(epoch, now)); System.out.println("经历周数:" + ChronoUnit.WEEKS.between(epoch, now)); System.out.println("经历天数:" + ChronoUnit.DAYS.between(epoch, now)); System.out.println("经历半天数:" + ChronoUnit.HALF_DAYS.between(epoch, now)); System.out.println("经历时数:" + ChronoUnit.HOURS.between(epoch, now)); System.out.println("经历分数:" + ChronoUnit.MINUTES.between(epoch, now)); System.out.println("经历秒数:" + ChronoUnit.SECONDS.between(epoch, now)); System.out.println("经历毫秒数:" + ChronoUnit.MILLIS.between(epoch, now)); System.out.println("经历微秒数:" + ChronoUnit.MICROS.between(epoch, now)); System.out.println("经历纳秒数:" + ChronoUnit.NANOS.between(epoch, now));
Duration
常用方法:
方法 | 描述 |
---|---|
int getNano() |
返回持续时间的纳秒数 |
int getSeconds() |
返回持续时间的秒数 |
long toDays() |
返回持续时间的总天数 |
long toHours() |
返回持续时间的总小时数 |
long toMinutes() |
返回持续时间的总分钟数 |
long toSeconds() |
返回持续时间的总秒数 |
long toMillis() |
返回持续时间的总毫秒数 |
long toNanos() |
返回持续时间的总纳秒数 |
注意:
getXxx()
方法所返回的值只是对应字段的值,并非间隔的总该单位时间;
Period
常用方法:
方法 | 描述 |
---|---|
int getDays() |
返回间隔的天数 |
int getMonths() |
返回间隔的月数 |
int getYears() |
返回间隔的年数 |
long toTotalMonths() |
返回间隔的总月数 |
注意:
Period
类中的getXxx()
方法返回的是对应字段的值,而并非间隔的总该单位时间;equals()
方法将会逐字段比较,只有类型相同不为空且每个字段都匹配的情况下才会返回true
,这意味着 15 个月 并不等于 1 年 3 个月;