日期和時(shí)間API
因?yàn)闀r(shí)間能夠和朝夕與季節(jié)掛鉤,所以使得事情變得復(fù)雜。
時(shí)間線
時(shí)間單位是以秒為單位,是從地球的自轉(zhuǎn)中推導(dǎo)出來的。地球自轉(zhuǎn)一周需要24個(gè)小時(shí),即24 x 60 x 60 = 86400秒。但是地球有輕微的顫動(dòng),所以需要更加精確的定義。
1976年,人們根據(jù)銫133原子內(nèi)的特性推導(dǎo)出了與其歷史定義相匹配的秒的新的精確定義。
Java Date和Time API 規(guī)范要求Java使用的時(shí)間尺度為:
每天86400秒
每天正午與官方時(shí)間精確匹配
在其他時(shí)間點(diǎn)上,以精確定義的方式與官方時(shí)間接近匹配。
在Java中,Instant表示時(shí)間線上的某個(gè)點(diǎn)。
被稱為“新紀(jì)元”的時(shí)間線原點(diǎn)被設(shè)置為穿過格林威治皇家天文臺(tái)的本初子午線所處時(shí)區(qū)的1970年1月1日的午夜。這與UNIX/POSIX時(shí)間中使用的慣例相同。 從該原點(diǎn)開始,時(shí)間按照每天86400秒向前或向回度量,精確到納秒。
Instance的值可向回追溯到10億年(Instant.MIN)。最大值Instant.MAX是公元1000 000 000年的12月31日。
Instant.now()會(huì)給出當(dāng)前的時(shí)刻。 可以按照常用的方式,用equals和compareTo方法來比較兩個(gè)Instatnt的對(duì)象,因此可以將Instant對(duì)象用作時(shí)間戳。
使用靜態(tài)方法計(jì)算兩個(gè)時(shí)間的時(shí)間差:Duration.between.
Instant start = Instant.now();
runAlgorithm(); // 中間計(jì)算過程
Instant end = Instant.now();
Duration timeElapsed = Duration.between(start, end);
long millis = timeElapsed.toMillis();
Duration是兩個(gè)時(shí)刻之間的時(shí)間量。 通過調(diào)用toNanos、toMIllis、getSeconds、toMinutes、toHours和toDays來獲得Duration按照傳統(tǒng)單位度量的時(shí)間長度。
Duration對(duì)象的內(nèi)部存儲(chǔ)所需的空間超過了一個(gè)long值,因此秒數(shù)存儲(chǔ)在一個(gè)long中,而納秒數(shù)存儲(chǔ)在一個(gè)額外的int中。如果想要讓計(jì)算精確到納秒級(jí),那么就需要整個(gè)Duration的存儲(chǔ)內(nèi)容。 如果不要求那么高的精度,可以用long值來執(zhí)行計(jì)算,然后直接調(diào)用toNanos。
注意:大約300年時(shí)間對(duì)應(yīng)的納秒數(shù)才會(huì)溢出long的范圍。
例如:檢測某個(gè)算法是否比另一個(gè)算法快10倍。
Duration timeElasped2 = Duration.between(start2, end2);
boolean overTenTimesFaster =
? ?timeElasped.multipliedBy(10)*minus(timeElapsed2).isNegative();
// or timeElasped.toNanos() * 10 < timeElapsed2.toNanos();
用于時(shí)間的Instant和Duration的算術(shù)運(yùn)算
方法描述plus、minus在當(dāng)前的Instant或Duration上加上或減去一個(gè)DurationplusNanos、plusMillis、plusSeconds、minusNanos、minusMillis、minusSeconds在當(dāng)前的Instant或Duration上加上或減去給定時(shí)間單位的數(shù)值 ? ?plusMinutes、plusHours、plusDays、minusMinutes、minusHours、minusDays在當(dāng)前Duration上加上或減去給定時(shí)間單位的數(shù)值multipliedBy、dividedBy、negated返回由當(dāng)前Duration乘以或除以給定long或-1而得到的Duration。注意,可以縮放Duration,但是不能縮放InstantisZero、isNegative檢查當(dāng)前的Duration是否是0或負(fù)值
注意:Instant和Duration類都是不可修改的類,所以諸如multipliedBy和minus這樣的方法都會(huì)返回一個(gè)新的實(shí)例。
本地時(shí)間
Java API包含兩種人類時(shí)間, 本地日期/時(shí)間 和時(shí)區(qū)時(shí)間。
本地日期/時(shí)間包含日期和當(dāng)天的時(shí)間,但是與時(shí)區(qū)信息沒有任何關(guān)聯(lián)。
例如:2023年3月13日 就是一個(gè)本地日期。 因?yàn)檫@個(gè)日期既沒有當(dāng)前的時(shí)間,也沒有時(shí)區(qū)信息,因此不對(duì)應(yīng)精確的時(shí)刻。
例如:2023年3月13日 17:09:00 Asia/Shanghai 是一個(gè)時(shí)區(qū)日期/時(shí)間,表示的是時(shí)間線上的一個(gè)精確的時(shí)刻。
某些情況下,時(shí)區(qū)甚至是一個(gè)障礙。例如安排每周10:00開一次會(huì)議。 如果加7天(即7×24×60×60秒)到最后一次會(huì)議的時(shí)區(qū)時(shí)間上,可能會(huì)碰巧跨越夏令時(shí)的時(shí)間調(diào)整邊界,這次會(huì)議可能會(huì)早一個(gè)小時(shí)或晚一個(gè)小時(shí)。
除非確實(shí)想要表示絕對(duì)時(shí)間的實(shí)例,不推薦使用時(shí)區(qū)時(shí)間。 生日、假日、計(jì)劃時(shí)間等通常最好都表示成本地日期和時(shí)間。
LocalDate是帶有年、月、日的日期。
LocalDate today = LocalDate.now();
LocalDate alonzosBirthday = LocalDate.of(1903, 6, 14);
alonzosBirthday = LocalDate.of(1903, Month.JUNE, 14);
// 可以使用Month的枚舉類型
LocalDate的方法
方法描述now, of構(gòu)建一個(gè)LocalDate,要么從當(dāng)前時(shí)間構(gòu)建,要么從給定的年月日構(gòu)建。plusDays,plusWeeks,plusMonths,plusYears在當(dāng)前的LocalDate上加上一定量的天、星期、月或年minusDays,minusWeeks,minusMonths,minusYears在當(dāng)前的LocalDate上減去一定量的天、星期、月或年plus、minus加上或減去一個(gè)Duration或PeriodwithDaysOfMonth,withDayOfYear,withMonth,withYear返回一個(gè)新的LocalDate,其月的日期、年的日期、月或年修改為給定的值getDayOfMonth獲取月的日期(在1到31之間)getDayOfYear獲取年的日期(在1到366之間)getDayOfWeek獲取星期日期,返回DayOfWeek枚舉值getMonth,getMonthValue獲取月份的Month枚舉值,或者是1 ~ 12之間的數(shù)字getYear獲取年份,在-999 999 999到999 999 999之間until獲取Period,或者兩個(gè)日期之間按照給定的ChronoUnits計(jì)算的數(shù)值isBefore,isAfter將當(dāng)前的LocalDate與另一個(gè)LocalDate進(jìn)行比較isLeapYear如果當(dāng)前是閏年,則返回true。即,該年份能夠被4整除,但是不能被100整除,或者能夠被400整除。該算法可以應(yīng)用于已經(jīng)過去的年份,盡管在歷史上并不準(zhǔn)確。
例如每年的第256天使程序員日。
LocalDate day = LocalDate.of(2023, 1, 1).plusDays(255);
兩個(gè)Instant之間的時(shí)長是Duration,用于本地日期的等價(jià)物是Period,它表示的是流逝的年、月或日的數(shù)量??梢哉{(diào)用birthday.plus(Period.ofYears(1))獲取下一年的生日。 也可以直接調(diào)用birthday.plusYears(1)。但是birthday.plus(Duration.ofDays(365))在閏年是錯(cuò)誤的結(jié)果。
util方法:產(chǎn)生兩個(gè)本地日期之間的時(shí)長: independenceDay.util(christmas),可以產(chǎn)生5個(gè)月21天的一段時(shí)長。確定一共有多少天,可以使用:independenceDay.util(christmas, ChronoUnit.DAYS);
警告:上述表中有些方法可能會(huì)創(chuàng)建并不存在的日期。例如:在1月31日加上1個(gè)月,不應(yīng)該產(chǎn)生2月31日。這些方法并不會(huì)拋出異常,而是會(huì)返回該月有效的最后一天。
getDayOfWeek方法:產(chǎn)生星期日期,即DayOfWeek枚舉的某個(gè)值。DayOfWeek.MONDAY的枚舉值是1。
例如:LocalDate.of(1900, 1, 1).getDayOfWeek().getValue()返回1.
DayOfWeek枚舉具有便捷方法plus和minus,以7為模型計(jì)算星期日期。
例如,DayOfWeek.SATURDAY.plus(3)會(huì)產(chǎn)生DayOfWeek.TUESDAY。
注意:周末實(shí)際上在每周的末尾。這與java.util.Calendar有所差異,在后者,星期六的值為1,而星期