以下源代碼的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系列:
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()方法。
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)方法
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
}
檢視這個時間量對象所支援的時間機關集合
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));
}
}
下面的幾個方法不用介紹了吧~
接下來看看下面兩個方法
public static void main(String[] args) {
Duration d1=Duration.ofHours(2);
//傳回具有指定秒數的此時間量對象的副本。副本具有指定秒數的時間量,并保留原時間量對象的納秒部分。
Duration d2=d1.withSeconds(2000);
//傳回具有指定納秒數的此時間量對象的副本。副本具有指定納秒數的時間量,并保留原時間量對象的秒的部分。
Duration d3=d1.withNanos(1000);
}
duration.plus系列方法,對時間量進行加法處理
public static void main(String[] args) {
Duration d1=Duration.ofHours(2);
//原時間量增加1小時,傳回新的時間量對象
Duration d2=d1.plusHours(1);
}
duration.minus系列方法,對時間量進行減法處理
對時間量進行乘法和除法處理
對時間量取反和取絕對值
把時間量對象的時間量加到某個時間對象中去和從某個時間對象減去這個時間量
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系列方法表示把這個時間量轉換為以什麼時間機關表示的時間量
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系列方法提取這個時間量轉換成标準時間後的各個字段的值
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
}
将時間量截斷到指定的時間單元
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
}
最後這幾個方法就不介紹了~
三、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對象,這些方法都調用了構造方法。
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類型的時間對象。
下面這些大部分方法的作用和Duration的一樣
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。