【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 个月;
浙公网安备 33010602011771号