Java时间处理3---Java8中Instant、Duration、Period、Clock介绍

前言

前面文章对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:00Z1000000000-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中时间处理比较常用的模块,大家不妨上手敲几段代码试试。

---------- 😏本文结束  感谢您的阅读😏 ----------
评论