以下源代码的JDK版本:jdk-13.0.1

一、Duration和Period介绍

Duration类通过秒和纳秒相结合来描述一个时间,最高精度是纳秒。时间量可以为正也可以为负,比如1天(86400秒0纳秒)、-1天(-86400秒0纳秒)、1年(31556952秒0纳秒)、1毫秒(0秒1000000纳秒)等。

Period类通过年、月、日相结合来描述一个时间量,最高精度是日。时间量可以为正也可以为负,例如2年(2年0个月0日)、3个月(0年3个月0日)、4天(0年0月4日)等。

这两个类是不可变的、线程安全的、最终类:

不可变的最终类:类的属性都用final修饰,一旦赋值不可改变;类声明时用final修饰,方法的实现无法被重写(因为final修饰的类是最终类,不可以有子孙类,不能被别的类继承,方法自然就无法被重写)。

Duration类的官方文档:https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/time/Duration.html

Period类的官方文档:https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/time/Period.html

二、Duration类的细节

首先该类import了一堆静态常量:

import static java.time.LocalTime.MINUTES_PER_HOUR;//(每小时的分钟数)60
import static java.time.LocalTime.NANOS_PER_MILLI;//(每毫秒的纳秒数)1000000
import static java.time.LocalTime.NANOS_PER_SECOND;//(每秒的纳秒数)1000000000
import static java.time.LocalTime.SECONDS_PER_DAY;//(一天的秒数)86400
import static java.time.LocalTime.SECONDS_PER_HOUR;//(每小时的秒数)3600
import static java.time.LocalTime.SECONDS_PER_MINUTE;//(每分钟的秒数)60
import static java.time.temporal.ChronoField.NANO_OF_SECOND;//(在数字时钟上可观察到的纳秒数范围)0~999999999
import static java.time.temporal.ChronoUnit.DAYS;//(时间单位天)Duration.ofSeconds(86400)
import static java.time.temporal.ChronoUnit.NANOS;//(时间单位纳秒)Duration.ofNanos(1)
import static java.time.temporal.ChronoUnit.SECONDS;//(时间单位秒)Duration.ofSeconds(1)

(import和import static的区别请百度~)

2.1  属性

该类用两个私有属性来存储时间量:

/**
     * duration中的秒数
     */
    private final long seconds;
    /**
     * duration中的纳秒数,表示为秒数的分数。 这始终是正整数,并且永远不会超过999,999,999。
     */
    private final int nanos;

也就是说,所有的时间量,都换算成long类型的秒数和int类型的纳秒数(1秒=1000000000纳秒),比如1天等于86400秒0纳秒,-1天等于-86400秒0纳秒,1年等于31536952秒0纳秒等。

而且纳秒总是为正数,且约定不超过999999999

官方文档说long类型的seconds能过存储的秒数超过宇宙年龄,所以不用担心时间量不够。

 还有一个特殊的属性:

/**
     * duration为零的常数。
     */
    public static final Duration ZERO = new Duration(0, 0);

Duration.ZERO描述一个0秒0纳秒的时间量(这是什么鬼?)。

其余属性这里不做介绍,感兴趣可以自行阅读源码。

2.2 方法

该类的构造方法只有一个,但是很可惜是private的,不能在外部new一个Duration对象。

/**
     * 用秒数和纳秒数构造一个实例。参数seconds可以是正的也可以是负的,参数nanos值约定从0~999999999
     */
    private Duration(long seconds, int nanos) {
        super();
        this.seconds = seconds;
        this.nanos = nanos;
    }

那么如何创建一个Duration对象呢?用该类的静态方法Duration.of系列:

java 过一段时间执行操作 java时间限制_java 过一段时间执行操作

public static void main(String[] args) {
		//时间量可正可负。
		//获取1天的时间量(86400秒0纳秒)
		Duration d1=Duration.ofDays(1);
		//获取-1小时的时间量(-3600秒0纳秒)
		Duration d2=Duration.ofHours(-1);
		//获取10分钟的时间量
		Duration d3=Duration.ofMinutes(10);
		//获取30秒钟的时间量
		Duration d4=Duration.ofSeconds(30);
		//获取20毫秒的时间量
		Duration d5=Duration.ofMillis(20);
		//获取1微秒的时间量
		Duration d6=Duration.ofNanos(1000);
		//获取100纳秒的时间量
		Duration d7=Duration.ofNanos(100);
		//第一个参数是秒数,第二个参数是纳秒数,而纳秒数可以是任意long类型的值,内部实现会根据传入的秒数和纳秒数的值进行调整,
		//使创建的Duration实例的nanos属性值保持在0~999999999范围内。
		//以下三个时间量等价
		Duration a1=Duration.ofSeconds(3, 1);
		Duration a2=Duration.ofSeconds(4, -999_999_999);
		Duration a3=Duration.ofSeconds(2, 1000_000_001);
		//传一个时间单位,表示获取这个时间单位的n倍的时间量,以下表示获取10天的时间量。
		Duration d8=Duration.of(10, ChronoUnit.DAYS);
		//从另一个时间量对象获取时间量,时间量对象可以是Duration类型也可以是Period类型
	    Duration d9=Duration.from(d8);
	    //看一下时间量对象的toString()方法能打印出什么
	    System.out.println(d9.toString());//PT240H
	    //parse(CharSequence)静态方法是通过解析字符串来创建一个时间量对象,
	    //String/StringBuilder/StringBuffer都实现了CharSequence接口
	    //那么字符串需要什么格式才会被解析成功呢?
	    //"PnDTnHnMn.nS"或"-PnDTnHnMn.nS"格式,字符串前面如果是负号,则整个周期都取反。
	    //"P" "D" "H" "M" "S"可以是大写或小写,这几个字符必须按顺序出现。
	    //"nD" "nH" "nM" "n.nS"这四个部分在字符串中必须有至少一个。
	    //"nD"表示n天,比如"3D","-2D"。一天的时间量被解析成刚好24小时。
	    //"T"是日期和时间的分隔符,必须出现在时间三个部分的第一次出现之前(如果有时间的话)。如果存在"T",则必须在"T"之后至少包含一个时间部分。
	    //"nH"表示n小时,比如"13H","-6H"。
	    //"nM"表示n分钟
	    //"n.nS"表示n.n秒,比如"45.345S",也可以是"45S",小数部分是可选的。小数点可以是点或逗号,小数部分的位数可以为0到9位。
	    Duration d10=Duration.parse("PT20.345S");//被解析为20.345秒
	    Duration d11=Duration.parse("PT15M");//被解析为15分钟(其中一分钟为60秒)
	    Duration d12=Duration.parse("PT10H");//被解析为10小时(其中一小时为3600秒) 
	    Duration d13=Duration.parse("P2D");//被解析为2天(一天为24小时或86400秒)
	    Duration d14=Duration.parse("P2DT3H4M");//被解析为2天3小时4分钟
	    Duration d15=Duration.parse("PT-6H3M");//被解析为负5小时57分钟(-6小时+3分钟)(-21420秒)
	    Duration d16=Duration.parse("-PT6H3M");//被解析为负6小时3分钟(-6小时-3分钟)(-21780秒)
	    Duration d17=Duration.parse("-PT-6H+3M");//被解析为5小时57分钟(+6小时-3分钟)(21420秒)
	}

关于时间单位ChronoUnit的介绍请移步我的另一篇博文>>>Java日期时间主要API:java.time.temporal.TemporalUnit接口及其实现类ChronoUnit 和 Unit

可用静态方法between(Temporal start, Temporal end)来获取两个时间对象之间的时间量。如果两个时间对象属于不同类型,则根据第一个时间对象的类型来计算时间量。例如,如果第一个参数是LocalTime类型那么第二个参数将转换为LocalTime类型。如果结束时间早于开始时间,则此方法的结果可能为负周期。为了保证获得正的时间量,请对结果调用abs()方法。

java 过一段时间执行操作 java时间限制_Duration_02

public static void main(String[] args) {
		LocalTime time1=LocalTime.now();
		LocalTime time2=time1.minusHours(2);//当前时间减去2小时,返回新的时间对象
		System.out.println(Duration.between(time1, time2));//PT-2H
		System.out.println(Duration.between(time1, time2).abs());//PT2H
	}

 Duratin时间对象有两个属性分别是seconds和nanos,如何获取每个字段的值?用get(TemporalUnit)方法

java 过一段时间执行操作 java时间限制_Period_03

public static void main(String[] args) {
		Duration d1=Duration.ofHours(2);
		//参数只能是ChronoUnit.SECONDS或ChronoUnit.NANOS两个时间单元,其余均抛出异常
		System.out.println(d1.get(ChronoUnit.SECONDS));//7200
		System.out.println(d1.get(ChronoUnit.NANOS));//0
	}

查看这个时间量对象所支持的时间单位集合

java 过一段时间执行操作 java时间限制_Duration_04

public static void main(String[] args) {
		Duration d1=Duration.ofHours(2);
		List<TemporalUnit> list=d1.getUnits();
		//获取的集合可以与get(TemporalUnit)访问持续时间的整个状态结合使用。
		//输出:Seconds、Nanos
		for(TemporalUnit u:list) {
			System.out.println(u.toString());
		}
		for(int i=0;i<list.size();i++) {
			d1.get(list.get(i));
		}
	}

下面的几个方法不用介绍了吧~

java 过一段时间执行操作 java时间限制_Duration_05

接下来看看下面两个方法

java 过一段时间执行操作 java时间限制_System_06

public static void main(String[] args) {
		Duration d1=Duration.ofHours(2);
		//返回具有指定秒数的此时间量对象的副本。副本具有指定秒数的时间量,并保留原时间量对象的纳秒部分。
		Duration d2=d1.withSeconds(2000);
		//返回具有指定纳秒数的此时间量对象的副本。副本具有指定纳秒数的时间量,并保留原时间量对象的秒的部分。
		Duration d3=d1.withNanos(1000);
	}

duration.plus系列方法,对时间量进行加法处理

java 过一段时间执行操作 java时间限制_Duration_07

public static void main(String[] args) {
		Duration d1=Duration.ofHours(2);
		//原时间量增加1小时,返回新的时间量对象
		Duration d2=d1.plusHours(1);
	}

duration.minus系列方法,对时间量进行减法处理

java 过一段时间执行操作 java时间限制_System_08

对时间量进行乘法和除法处理

java 过一段时间执行操作 java时间限制_System_09

对时间量取反和取绝对值

java 过一段时间执行操作 java时间限制_System_10

把时间量对象的时间量加到某个时间对象中去和从某个时间对象减去这个时间量

java 过一段时间执行操作 java时间限制_Period_11

public static void main(String[] args) {
		Duration d1=Duration.ofHours(2);
		LocalTime time=LocalTime.now();
		System.out.println(time);//17:55:55.831427900
		System.out.println(d1.addTo(time));//19:55:55.831427900
		System.out.println(d1.subtractFrom(time));//15:55:55.831427900
	}

duration.to系列方法表示把这个时间量转换为以什么时间单位表示的时间量

java 过一段时间执行操作 java时间限制_Duration_12

public static void main(String[] args) {
		Duration d1=Duration.ofHours(2);
		System.out.println(d1.toDays());//0
		System.out.println(d1.toMinutes());//120
	}

 duration.to...part系列方法提取这个时间量转换成标准时间后的各个字段的值

java 过一段时间执行操作 java时间限制_System_13

public static void main(String[] args) {
		Duration d1=Duration.ofSeconds(123456789);
		//1428天21时33分9秒0纳秒
		System.out.println(d1.toDaysPart());//1428
		System.out.println(d1.toHoursPart());//21
		System.out.println(d1.toMinutesPart());//33
		System.out.println(d1.toSecondsPart());//9
		System.out.println(d1.toNanosPart());//0
	}

将时间量截断到指定的时间单元

java 过一段时间执行操作 java时间限制_java 过一段时间执行操作_14

public static void main(String[] args) {
		Duration d1=Duration.ofSeconds(123456789);
		//1428天21时33分9秒0纳秒
		System.out.println(d1);//PT34293H33M9S
		Duration d2=d1.truncatedTo(ChronoUnit.DAYS);
		//1428天
		System.out.println(d2);//PT34272H
	}

 最后这几个方法就不介绍了~

java 过一段时间执行操作 java时间限制_System_15

三、Period类的实现细节

Period只import 了3个静态常量:

import static java.time.temporal.ChronoUnit.DAYS;//时间单位天 Duration.ofSeconds(86400)
import static java.time.temporal.ChronoUnit.MONTHS;//时间单位月 Duration.ofSeconds(31556952L / 12)
import static java.time.temporal.ChronoUnit.YEARS;//时间单位年 Duration.ofSeconds(31556952L)

3.1 Period类的属性

Period用三个私有属性来存储时间量:

/**
     * The number of years.
     */
    private final int years;
    /**
     * The number of months.
     */
    private final int months;
    /**
     * The number of days.
     */
    private final int days;

和Duration一样,也有一个ZERO属性代表0时间量

/**
     * A constant for a period of zero.
     */
    public static final Period ZERO = new Period(0, 0, 0);

3.2 Period类的方法

Period的构造函数也是私有的,只能用Period.of系列方法来创建一个Period对象,这些方法都调用了构造方法。

java 过一段时间执行操作 java时间限制_java_16

private Period(int years, int months, int days) {
        this.years = years;
        this.months = months;
        this.days = days;
    }

from(TemporalAmount)从另一个时间量对象中获取时间量,用法和Duration一样。

然后讲一下parse(CharSequence)方法

public static void main(String[] args) {
		//parse方法传入一个字符串,然后从字符串中解析获取要表达的时间量
		//字符串需要满足一定的格式:"PnYnMnD"或"PnW"。字符串前面如果是负号,则整个周期都取反。
		//"P" "Y" "M" "D" "W"可以是大写也可以是小写,这些后缀必须按顺序出现。
		//"nY"代表n年,例如"3Y";"nM"代表n月,例如"-2M";"nD"代表n天;"nW"代表n个星期。
		//字符串必须至少存在"nY" "nM" "nD" "nW"四个部分中的一个,n的值不能超过int类型,可正可负。
		//ISO-8601不允许在PnYnMnD和PnW格式之间混合使用。任何基于周的输入都将乘以7,并视为天数。
		Period p1=Period.parse("P2Y");//2年
		Period p2=Period.parse("P3M");//3个月
		Period p3=Period.parse("P4W");//4星期(28天)
		Period p4=Period.parse("P5D");//5天
		Period p5=Period.parse("P1Y2M3D");//1年2个月3天
		Period p6=Period.parse("P1Y2M3W4D");//1年2个月25天
		Period p7=Period.parse("P-1Y2M");//负1年正2个月(负10个月的时间量)
		Period p8=Period.parse("-P1Y2M");//-1年-2个月(-14个月)
	}

between(LocalDate, LocalDate)方法和Duration一样都是计算两个时间对象之间的时间量,不同的是本方法只能处理LocalDate类型的时间对象。

java 过一段时间执行操作 java时间限制_System_17

下面这些大部分方法的作用和Duration的一样

java 过一段时间执行操作 java时间限制_java 过一段时间执行操作_18

getChronology()是获取ISO日历系统对象。我们平时用的日历系统就是ISO日历系统。

public static void main(String[] args) {
		Period p1=Period.of(1, 1, 1);
		IsoChronology iso=p1.getChronology();
		//定义日期
		LocalDate ld1=iso.date(1,2,3);
		System.out.println(ld1);//0001-02-03
	}

关于IsoChronology类的细节,请移步到我的另一篇博文>>>Java日期时间主要API:java.time.chrono.Chronology接口及其实现类IsoChronology。