前言
前面文章对Java中的Date和Calendar类进行了介绍,在Java8以前,Javaer处理时间基本都是使用这两个类。
然鹅在使用过程中一个很尴尬的场景就是Date大部分方法废弃,Calendar又有很多不太友好的设计(月份从0开始)
终于,Java8中提供了一套全新的时间处理库,源码中的目录为java.time
,该包中的类都是不可变且线程安全
。
看上图感觉新的time
包下好像有很多都是新的类,感觉看着很头大啊,不过不用担心新提供的处理类中方法设计具有规律性,并且模块清晰,上手较快。
下面对比较常用的类库进行介绍。
本文主要对Instant、Duration、Period、Clock这四个类进行介绍
- Instant:时间线上的某一
时间点
- Duration:两个
时间
之间的持续时间,存储秒和纳秒 - Period:两个
日期
之间的持续时间,存储年,月和日 - Clock:表示真实世界的
时钟
,可通过时钟访问的当前日期和时间
Instant
Instant用于记录时间线上某一瞬间的时间点,顾名思义就是时间戳,但它不同于System.currentTimeMillis();
精度为秒
Instant可以精确到纳秒
,它的取值范围为:-1000000000-01-01T00:00Z
到1000000000-12-31T23:59:59.999999999Z
下面看下他的常用方法示例:
- now(): 获取基于UTC时间的Instant
- ofEpochMilli(long milli):根据时间戳(毫秒)创建一个Instant实例
- ofEpochSecond(long second): 根据时间戳(秒)创建一个Instant实例
- parse(): 根据时间字符串转换为Instant实例
//UTC
System.out.println(Instant.now());
//系统时区
System.out.println(Instant.now(Clock.systemDefaultZone()));
//根据时间字符串转换为Instant实例
System.out.println(Instant.parse("2020-06-06T12:12:12Z"));
Instant instant =Instant.parse("2020-06-06T12:12:12Z");
long milli = instant.toEpochMilli();
long second = instant.getEpochSecond();
//给定时间戳转换为Instant实例
System.out.println(Instant.ofEpochMilli(milli));
//给定时间戳转换为Instant实例
System.out.println(Instant.ofEpochSecond(second));
//给定时间戳和纳秒值转换为Instant实例
System.out.println(Instant.ofEpochSecond(second, 111));
输出结果:
2020-07-10T08:37:52.299Z
2020-07-10T08:37:52.380Z
2020-06-06T12:12:12Z
2020-06-06T12:12:12Z
2020-06-06T12:12:12Z
2020-06-06T12:12:12.000000111Z
Duration
Duration通常用秒或者纳秒相结合来表示一个时间量,最高精度为纳秒
通常用作表示两个时间之间的间隔,也称作持续时间
,例如1s持续时间
表示为PT1S
创建一个Duration实例
- ofXXX()系列方法: 根据纳秒、毫秒、秒、分、时、天等时间来构造持续时间
- from(TemporalAmount amount):根据TemporalAmount实例创建Duration对象
- parse(CharSequence text):根据ISO-8601持续时间格式字符串创建Duration对象
- between(Temporal startInclusive, Temporal endExclusive):获取两个时间对象之间的持续时间
System.out.println(Duration.ofNanos(1000));
System.out.println(Duration.ofMillis(1000));
System.out.println(Duration.ofSeconds(30));
System.out.println(Duration.ofSeconds(30,12345));
System.out.println(Duration.ofMinutes(1));
System.out.println(Duration.ofHours(1));
System.out.println(Duration.ofDays(1));
System.out.println(Duration.of(1000, ChronoUnit.MILLIS));
System.out.println(Duration.from(ChronoUnit.MINUTES.getDuration()));
System.out.println(Duration.parse("PT20.345S"));
System.out.println(Duration.between(Instant.parse("2020-06-23T10:15:30.00Z"), Instant.now()));
输出结果
PT0.000001S
PT1S
PT30S
PT30.000012345S
PT1M
PT1H
PT24H
PT1S
PT1M
PT20.345S
PT406H26M35.814S
Duration常用方法
- getXXX(): 获取持续时间对象具体的秒数或者毫秒数
- plusXXX(): 给Duration对象加上指定精度的值
- minusXXX(): 给Duration对象减去指定精度的值
- withXXX(): 修改Duration对象的秒数or毫秒数
- 其他方法
Duration d = Duration.parse("PT20.345S");
System.out.println(d.getSeconds());
System.out.println(d.getNano());
System.out.println(d.withNanos(3456789));//修改纳秒值,返回一个新的Duration
System.out.println(d.withSeconds(22));//修改秒值,返回一个新的Duration
System.out.println(d.plusNanos(1));//加1纳秒,返回一个新的Duration
System.out.println(d.plusMillis(100));//加100毫秒,返回一个新的Duration
System.out.println(d.plusSeconds(1));
System.out.println(d.minusNanos(1));//减去1纳秒,返回一个新的Duration
System.out.println(d.minusMillis(100));//减去10毫秒,返回一个新的Duration
System.out.println(d.minusSeconds(1));
System.out.println(d.isZero());//是否为0
System.out.println(Duration.ZERO.isZero());//是否为0
System.out.println(d.isNegative());//是否为负
System.out.println(d.negated());//求负
System.out.println(d.negated().abs());//求绝对值
输出结果
20
345000000
PT20.003456789S
PT22.345S
PT20.345000001S
PT20.445S
PT21.345S
PT20.344999999S
PT20.245S
PT19.345S
false
true
false
PT-20.345S
PT20.345S
Period
与Duration类似都是用来表示持续时间
但是Period是由年月日为单位的时间量,例如1年2个月3天
与Duration相比,Period的用法与之基本相同
初始化Period
- ofXXX()系列方法: 根据年月日来构造持续时间
- from(TemporalAmount amount):根据TemporalAmount实例创建Period对象
- parse(CharSequence text):根据ISO-8601持续时间格式字符串创建Period对象
- between(LocalDate startDateInclusive, LocalDate endDateExclusive):获取两个
日期对象
之间的持续时间
System.out.println(Period.of(1, 2, 3));//根据年月日构造Period
System.out.println(Period.ofDays(1));
System.out.println(Period.ofMonths(2));
System.out.println(Period.ofWeeks(3));//根据周数构造
System.out.println(Period.ofYears(1));
System.out.println(Period.from(Period.ofMonths(1)));
System.out.println(Period.parse("P20Y10M5D"));//根据ISO-8601时间格式字符串进行构造
//计算两个日期对象之间的持续时间
System.out.println(Period.between(LocalDate.now().minusYears(1).minusDays(1),LocalDate.now() ));
输出结果
P1Y2M3D
P1D
P2M
P21D
P1Y
P1M
P20Y10M5D
P1Y1D
Period常用方法
常用方法的使用方式与Duration也基本类似
- getXXX(): 获取持续时间对象具体的年、月、日
- plusXXX(): 给Period对象加上指定精度的值
- minusXXX(): 给Period对象减去指定精度的值
- withXXX(): 修改Period对象的某一精度值
- 其他方法
Period p = Period.of(1, 2, 3);
//获取年月日
System.out.println(p.getYears()+"年"+p.getMonths()+"月"+p.getDays()+"日");
//重设Period的年月日
System.out.println(p.withYears(3).withMonths(2).withDays(1));
//加上1天
System.out.println(p.plusDays(1));
//减去1天
System.out.println(p.minusDays(1));
//判断是否为0
System.out.println(p.isZero());
//判断是否为负
System.out.println(p.isNegative());
//取负
System.out.println(p.negated());
输出结果
1年2月3日
P3Y2M1D
P1Y2M4D
P1Y2M2D
false
false
P-1Y-2M-3D
Clock
Clock表示一个时钟,Clock的实例用于查找当前时刻,可以使用存储的时区来解释当前时刻以查找当前日期和时间。某种程度上可以使用时钟代替System.currentTimeMillis()
和TimeZone.getDefault()
。
我们可以自定义创建一个指定滴答间隔的时钟,用来获取需要的时间日期
钟表的滴答间隔(tickDuration):规定了提供下一个读数的时间间隔。比如,滴答间隔为 1 秒的钟表,读数的分辨率就到 1 秒。滴答间隔为 5 秒的钟表,读数的"分辨率" 就到 5 秒。这里,5 秒的"分辨率"是指,当实际时间数据是 0 或 1、2、3、4 秒时,从它那里得到的读数都是 0 秒。当实际时间数据是 5 或 6、7、8、9 秒时,从它那里得到的读数都是 5 秒。
Clock的初始化
Clock clock = Clock.systemUTC();
System.out.println(clock.millis());//打印时钟当前毫秒值
System.out.println(System.currentTimeMillis());//打印当前毫秒值
System.out.println(clock.instant().toEpochMilli());//时钟转换为Instant实例并获取时间戳毫秒值
输出结果
1594371253772
1594371253772
1594371253773
自定义Clock的创建
使用tick()
方法创建一个滴答间隔为3s的时钟,每1s钟查看一下它的时间
//系统默认时区时钟
Clock clock = Clock.systemDefaultZone();
//滴答时间间隔为3秒的时钟
//当实际时间数据是 0 或 1、2秒时,从它那里得到的读数都是 0 秒。当实际时间数据是 3或 4、5秒时,从它那里得到的读数都是 3 秒。
Clock tick = Clock.tick(clock, Duration.ofSeconds(3));
for (int i = 0; i < 10; i++) {
TimeUnit.SECONDS.sleep(1);
System.out.println(clock.instant()+"---> "+tick.instant());
}
输出结果如下,可以看到两个时钟每秒钟的计数是不同的:
2020-07-10T08:55:35.182Z---> 2020-07-10T08:55:33Z
2020-07-10T08:55:36.195Z---> 2020-07-10T08:55:36Z
2020-07-10T08:55:37.195Z---> 2020-07-10T08:55:36Z
2020-07-10T08:55:38.196Z---> 2020-07-10T08:55:36Z
2020-07-10T08:55:39.197Z---> 2020-07-10T08:55:39Z
2020-07-10T08:55:40.198Z---> 2020-07-10T08:55:39Z
2020-07-10T08:55:41.198Z---> 2020-07-10T08:55:39Z
2020-07-10T08:55:42.199Z---> 2020-07-10T08:55:42Z
2020-07-10T08:55:43.199Z---> 2020-07-10T08:55:42Z
2020-07-10T08:55:44.200Z---> 2020-07-10T08:55:42Z
使用tickSeconds()
和tickMinutes()
创建时钟
- tickSeconds(ZoneId zone) : 创建一个滴答间隔为1秒的时钟
- tickMinutes(ZoneId zone) :创建一个滴答间隔为1分钟的时钟
//系统默认时区时钟
Clock clock = Clock.systemDefaultZone();
//获取滴答间隔为1秒的钟表
Clock clock1 = Clock.tickSeconds(ZoneId.systemDefault());
//获取滴答间隔为1分钟的钟表
Clock clock2 = Clock.tickMinutes(ZoneId.systemDefault());
for (int i = 0; i < 10; i++) {
TimeUnit.SECONDS.sleep(1);
System.out.println(clock.instant()+"---> "+clock1.instant()+"---> "+clock2.instant());
}
输出结果,从左到右依次为,系统默认时钟—>滴答间隔1秒的时钟---->滴答间隔1分钟的时钟
2020-07-10T08:58:58.001Z---> 2020-07-10T08:58:58Z---> 2020-07-10T08:58:00Z
2020-07-10T08:58:59.001Z---> 2020-07-10T08:58:59Z---> 2020-07-10T08:58:00Z
2020-07-10T08:59:00.002Z---> 2020-07-10T08:59:00Z---> 2020-07-10T08:59:00Z
2020-07-10T08:59:01.002Z---> 2020-07-10T08:59:01Z---> 2020-07-10T08:59:00Z
2020-07-10T08:59:02.002Z---> 2020-07-10T08:59:02Z---> 2020-07-10T08:59:00Z
2020-07-10T08:59:03.003Z---> 2020-07-10T08:59:03Z---> 2020-07-10T08:59:00Z
2020-07-10T08:59:04.004Z---> 2020-07-10T08:59:04Z---> 2020-07-10T08:59:00Z
2020-07-10T08:59:05.005Z---> 2020-07-10T08:59:05Z---> 2020-07-10T08:59:00Z
2020-07-10T08:59:06.005Z---> 2020-07-10T08:59:06Z---> 2020-07-10T08:59:00Z
2020-07-10T08:59:07.006Z---> 2020-07-10T08:59:07Z---> 2020-07-10T08:59:00Z
总结
以上是Java8中针对瞬时时间、持续时间、时钟加入的新工具类,可以看到对于时间的概念区分更加细化、这四个基础的时间概念也是Java8中时间处理比较常用的模块,大家不妨上手敲几段代码试试。