前言
前面文章对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实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| //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));
|
输出结果:
1 2 3 4 5 6
| 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):获取两个时间对象之间的持续时间
1 2 3 4 5 6 7 8 9 10 11
| 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()));
|
输出结果
1 2 3 4 5 6 7 8 9 10 11
| PT0.000001S PT1S PT30S PT30.000012345S PT1M PT1H PT24H PT1S PT1M PT20.345S PT406H26M35.814S
|
Duration常用方法
- getXXX(): 获取持续时间对象具体的秒数或者毫秒数
- plusXXX(): 给Duration对象加上指定精度的值
- minusXXX(): 给Duration对象减去指定精度的值
- withXXX(): 修改Duration对象的秒数or毫秒数
- 其他方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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());//求绝对值
|
输出结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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):获取两个
日期对象
之间的持续时间
1 2 3 4 5 6 7 8 9
| 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() ));
|
输出结果
1 2 3 4 5 6 7 8
| P1Y2M3D P1D P2M P21D P1Y P1M P20Y10M5D P1Y1D
|
Period常用方法
常用方法的使用方式与Duration也基本类似
- getXXX(): 获取持续时间对象具体的年、月、日
- plusXXX(): 给Period对象加上指定精度的值
- minusXXX(): 给Period对象减去指定精度的值
- withXXX(): 修改Period对象的某一精度值
- 其他方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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 4 5 6 7
| 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的初始化
1 2 3 4
| Clock clock = Clock.systemUTC(); System.out.println(clock.millis());//打印时钟当前毫秒值 System.out.println(System.currentTimeMillis());//打印当前毫秒值 System.out.println(clock.instant().toEpochMilli());//时钟转换为Instant实例并获取时间戳毫秒值
|
输出结果
1 2 3
| 1594371253772 1594371253772 1594371253773
|
自定义Clock的创建
使用tick()
方法创建一个滴答间隔为3s的时钟,每1s钟查看一下它的时间
1 2 3 4 5 6 7 8 9 10
| //系统默认时区时钟 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()); }
|
输出结果如下,可以看到两个时钟每秒钟的计数是不同的:
1 2 3 4 5 6 7 8 9 10
| 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分钟的时钟
1 2 3 4 5 6 7 8 9 10 11
| //系统默认时区时钟 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分钟的时钟
1 2 3 4 5 6 7 8 9 10
| 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中时间处理比较常用的模块,大家不妨上手敲几段代码试试。