【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/CityCountry/City 以及其它的定义方式,如 Asia/Shanghai, Asia/Taipei, Asia/Chongqing, Etc/GMT+8,共计 602 个时区。

静态方法

静态方法 描述
Set<String> getAvailableZoneIds() 获取 Java 中所有支持的时区 ID 集合
ZoneId systemDefault() 获取系统默认时区
ZoneId of(String zoneId) 获取一个指定时区

注意

  1. getAvailableZoneIds() 方法返回的是一个集合,集合没有索引,直接打印可查看其中元素,使用 size() 方法可查看其长度;

    Set<String> zoneIds = ZoneId.getAvailableZoneIds();
    System.out.println(zoneIds.size()); //获取长度
    System.out.println(zoneIds);        //集合没有索引,此处仅直接打印
    
  2. systemDefault() 方法返回的是系统的默认时区,这个可在操作系统设置中更改,一般而言,使用的是 UTC+08:00,返回 Asia/Shanghai

  3. 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, LocalTimeLocalDateTime 参数;

常用方法

常用方法 描述
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)
基于所给字符串,使用指定格式化器解析得到指定对象

注意

  1. of() 有许多重载,并不一定需要全部的参数,具体详见官方文档;
  2. parse() 方法解析出错时,会抛出异常 DateTimeParseException

实例方法

方法 描述
getXxx() 获得指定字段的数据
boolean isAfter()/isBefore()/isEqual() 判断先后顺序
minusXxx(xxx)/plusXxx(xxx) 针对时间日期的加法、减法
withXxx(xxx) 返回修改后的新对象

1-9.4.3 LocalDateTime 转换至 LocalDateLocalTime

转换方法

方法 描述
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 个月;
posted @ 2023-07-22 21:21  Zebt  阅读(85)  评论(0)    收藏  举报