詳解Java的日期時(shí)間新特性
一. 新特性概述
在JDK 8之前,其實(shí)有不少的API都存在著一些問(wèn)題,日期時(shí)間等相關(guān)類同樣如此。所以從JDK 8開(kāi)始,Java做了較大的改動(dòng),出現(xiàn)了很多新特性。其中,java.time包中了就提供了不少新的日期和時(shí)間API,主要如下:
- 本地日期和時(shí)間類:LocalDateTime,LocalDate,LocalTime;
- 帶時(shí)區(qū)的日期和時(shí)間類:ZonedDateTime;
- 時(shí)刻類:Instant;
- 時(shí)區(qū):ZoneId,ZoneOffset;
- 時(shí)間間隔:Duration。
在格式化操作方面,也推出了一個(gè)新的格式化類DateTimeFormatter。
和之前那些舊的API相比,新的時(shí)間API嚴(yán)格區(qū)分了時(shí)刻、本地日期、本地時(shí)間和帶時(shí)區(qū)的日期時(shí)間,日期和時(shí)間的運(yùn)算更加方便。這些新的API類型幾乎全都是final不變類型,我們不必?fù)?dān)心其會(huì)被修改。并且新的API還修正了舊API中一些不合理的常量設(shè)計(jì):
- Month的范圍采用1~12,分別表示1月到12月;
- Week的范圍采用1~7,分別表示周一到周日。
二. LocalDateTime
1. 簡(jiǎn)介
LocalDateTime是JDK 8之后出現(xiàn)的,用來(lái)表示本地日期和時(shí)間的類。我們可以通過(guò)now()方法,默認(rèn)獲取到本地時(shí)區(qū)的日期和時(shí)間。與之前的舊API不同,LocalDateTime、LocalDate和LocalTime默認(rèn)會(huì)嚴(yán)格按照ISO 8601規(guī)定的日期和時(shí)間格式進(jìn)行打印。
2. 獲取當(dāng)前日期和時(shí)間
我們可以通過(guò)now()方法來(lái)獲取到當(dāng)前的日期和時(shí)間。我們?cè)趫?zhí)行一行代碼時(shí),多少也會(huì)消耗一點(diǎn)時(shí)間,日期和時(shí)間的值可能會(huì)對(duì)不上,尤其是時(shí)間的毫秒數(shù)可能會(huì)有差異。所以為了保證獲取到的日期和時(shí)間值差異減少,我們的代碼盡量要編寫如下:
import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; /** * @author */ public class Demo10 { public static void main(String[] args) { //獲取當(dāng)前日期和時(shí)間 LocalDateTime dt = LocalDateTime.now(); System.out.println("dt="+dt); // 轉(zhuǎn)換到當(dāng)前日期 LocalDate d = dt.toLocalDate(); System.out.println("date="+d); // 轉(zhuǎn)換到當(dāng)前時(shí)間 LocalTime t = dt.toLocalTime(); System.out.println("time="+t); } }
3. of()方法的作用
我們可以通過(guò)of()方法,根據(jù)指定的日期和時(shí)間來(lái)創(chuàng)建一個(gè)LocalDateTime對(duì)象,用法如下:
import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; /** * @author */ public class Demo11 { public static void main(String[] args) { // 根據(jù)指定的日期和時(shí)間創(chuàng)建LocalDateTime對(duì)象 // 2023-01-25, 月份與之前不同:1-12分別表示1-12個(gè)月 LocalDate date = LocalDate.of(2023, 1, 25); // 20:35:48 LocalTime time = LocalTime.of(20, 35, 48); LocalDateTime dt1 = LocalDateTime.of(date, time); System.out.println("dt1=" + dt1); LocalDateTime dt2 = LocalDateTime.of(2023, 1, 23, 20, 35, 48); System.out.println("dt2=" + dt2); } }
4. parse()方法的作用
我們可以使用parse()方法,將一個(gè)時(shí)間格式的字符串解析為L(zhǎng)ocalDateTime,用法如下:
import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; /** * @author */ public class Demo11 { public static void main(String[] args) { // 解析時(shí)間字符串。T是日期和時(shí)間的分隔符 LocalDateTime dt = LocalDateTime.parse("2023-02-22T20:18:15"); System.out.println("dt="+dt); LocalDate date = LocalDate.parse("2012-10-15"); System.out.println("date="+date); LocalTime time = LocalTime.parse("16:15:20"); System.out.println("time="+time); } }
我們要注意,根據(jù)ISO 8601規(guī)定,日期和時(shí)間的分隔符是T,標(biāo)準(zhǔn)格式如下:
- 日期:yyyy-MM-dd
- 時(shí)間:HH:mm:ss
- 帶毫秒的時(shí)間:HH:mm:ss.SSS
- 日期和時(shí)間:yyyy-MM-dd'T'HH:mm:ss
- 帶毫秒的日期和時(shí)間:yyyy-MM-dd'T'HH:mm:ss.SSS
5. 時(shí)間加減方法
在LocalDateTime中,給我們提供了一系列的加減操作方法,使得我們可以很輕易的實(shí)現(xiàn)時(shí)間的加減操作,比如給某個(gè)日期或時(shí)間進(jìn)行加或減的操作。plusXxx()增加方法如下圖所示:
minusXxx()減少方法如下圖所示:
import java.time.LocalDateTime; /** * @author */ public class Demo13 { public static void main(String[] args) { // 對(duì)日期進(jìn)行加減操作 LocalDateTime dt1 = LocalDateTime.now(); System.out.println("dt1=" + dt1); // 加3天減6小時(shí): LocalDateTime dt2 = dt1.plusDays(3).minusHours(6); // 2019-10-31T17:30:59 System.out.println("dt2=" +dt2); // 減1月: LocalDateTime dt3 = dt2.minusMonths(1); // 2019-09-30T17:30:59 System.out.println("dt3=" +dt3); //加兩周 LocalDateTime dt4 = dt3.plusWeeks(2); System.out.println("dt4=" +dt4); } }
6. 時(shí)間調(diào)整方法
我們除了可以進(jìn)行日期和時(shí)間的增加、減少操作之外,還可以利用withXxx()方法對(duì)日期和時(shí)間進(jìn)行調(diào)整,這些方法如下圖所示:
從上圖可以看出,我們可以對(duì)年、月、日、時(shí)、分、秒等進(jìn)行調(diào)整,具體含義如下:
- 調(diào)整年:withYear()
- 調(diào)整月:withMonth()
- 調(diào)整日:withDayOfMonth()
- 調(diào)整時(shí):withHour()
- 調(diào)整分:withMinute()
- 調(diào)整秒:withSecond()
import java.time.LocalDateTime; /** * @author */ public class Demo14 { public static void main(String[] args) { // 對(duì)日期進(jìn)行加減操作 LocalDateTime dt1 = LocalDateTime.now(); System.out.println("dt1=" + dt1); // 注意:如果某個(gè)月中沒(méi)有29、30、31等日期,會(huì)出現(xiàn)java.time.DateTimeException: Invalid date 'FEBRUARY 31'類似的異常。 //LocalDateTime dt2 = dt1.withDayOfMonth(31); //日期變?yōu)?5日 LocalDateTime dt2 = dt1.withDayOfMonth(25); System.out.println("dt2="+dt2); // 2019-10-31T20:30:59 //月份變成5月 LocalDateTime dt3 = dt2.withMonth(5); //2023-05-25T20:06:06.768 System.out.println("dt3="+dt3); //年份變成2024年 LocalDateTime dt4 = dt3.withYear(2024); //2024-05-25T20:06:06.768 System.out.println("dt4=" +dt4); } }
我們?cè)诶脀ithXxx()方法調(diào)整月份時(shí),會(huì)相應(yīng)地調(diào)整日期。并且要注意,如果某個(gè)月中沒(méi)有29、30、31等日期,則會(huì)出現(xiàn)java.time.DateTimeException: Invalid date 'FEBRUARY 31'類似的異常,如下圖所示:
7. with()方法
LocalDateTime中有一個(gè)通用的with()方法,允許我們進(jìn)行更復(fù)雜的運(yùn)算,比如獲取當(dāng)月或下個(gè)月的第一天、最后一天、第一個(gè)周一等操作。
import java.time.DayOfWeek; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.temporal.TemporalAdjusters; public class Demo14 { public static void main(String[] args) { //獲取本月的第一天0:00時(shí)刻: LocalDateTime firstDay = LocalDate.now().withDayOfMonth(1).atStartOfDay(); System.out.println("firstDay="+firstDay); //獲取本月的最后1天: LocalDate lastDay = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()); System.out.println("lastDay="+lastDay); //獲取下個(gè)月的第1天: LocalDate nextMonthFirstDay = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth()); System.out.println("nextMonthFirstDay="+nextMonthFirstDay); //獲取本月的第1個(gè)周一 LocalDate firstWeekday = LocalDate.now().with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)); System.out.println("firstWeekday="+firstWeekday); } }
8. isBefore()與isAfter()方法
如果我們判斷兩個(gè)LocalDateTime的先后順序,可以使用isBefore()、isAfter()方法。
import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; /** * @author */ public class Demo16 { public static void main(String[] args) { LocalDateTime now = LocalDateTime.now(); LocalDateTime target = LocalDateTime.of(2022, 12, 25, 10, 15, 10); //判斷A日期在B日期之前 System.out.println("before?="+now.isBefore(target)); //判斷A日期在B日期之前 System.out.println(LocalDate.now().isBefore(LocalDate.of(2023, 12, 19))); //判斷A時(shí)間在B時(shí)間之后 System.out.println(LocalTime.now().isAfter(LocalTime.parse("10:15:20"))); } }
9. Duration時(shí)間間隔和Period間隔天數(shù)
我們可以使用Duration來(lái) 表示兩個(gè)時(shí)刻之間的持續(xù)時(shí)間, 計(jì)算兩個(gè)時(shí)間之間的間隔 ;用 Period來(lái) 表示兩個(gè)日期之間的間隔天數(shù), 以年、月、日的形式表示,用于計(jì)算兩個(gè)日期之間的間隔。
Duration和Period的表示方法也符合ISO-8601的格式,它以 P...T... 的形式表示。P...T之間表示日期間隔,T后面表示時(shí)間間隔,如果是PT...的格式,則只表示時(shí)間間隔。
所以如果是兩個(gè)LocalDateTime之間的差值,使用Duration表示的形式就是PT12H10M30S,意思是間隔12小時(shí)10分鐘30秒。而兩個(gè)LocalDate之間的差值,用Period表示的形式則是P1M21D,表示間隔1個(gè)月21天。
import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.Period; /** * @author */ public class Demo17 { public static void main(String[] args) { LocalDateTime start = LocalDateTime.of(2023, 05, 24, 13, 15, 20); LocalDateTime end = LocalDateTime.of(2025, 11, 8, 19, 25, 30); //計(jì)算兩個(gè)時(shí)間的間隔 Duration d = Duration.between(start, end); //PT21582H10M10S,間隔21582小時(shí)10分10秒 System.out.println("duration="+d); //計(jì)算兩個(gè)日期的間隔 Period p = LocalDate.of(2022, 12, 11).until(LocalDate.of(2025, 2, 22)); //P2Y2M11D,間隔2個(gè)月11天 System.out.println("period="+p); //我們也可以使用ofXxx()或parse()方法直接創(chuàng)建Duration //10hours Duration d1 = Duration.ofHours(10); //2day,4hours,5minutes Duration d2 = Duration.parse("P2DT4H5M"); System.out.println("d1="+d1); System.out.println("d2="+d2); } }
三. ZonedDateTime
1. 簡(jiǎn)介
我們知道,LocalDateTime表示本地日期和時(shí)間,如果我們要表示一個(gè)帶時(shí)區(qū)的日期和時(shí)間,就需要使用ZonedDateTime了。ZonedDateTime相當(dāng)于是LocalDateTime + ZoneId,其中ZoneId是java.time引入的新的時(shí)區(qū)類,它與舊的java.util.TimeZone是有區(qū)別的。在ZonedDateTime中也提供了plusDays()等加減操作,使用起來(lái)也非常地方便。
2. 創(chuàng)建方式
如果我們想要?jiǎng)?chuàng)建一個(gè)ZonedDateTime對(duì)象,可以有以下幾種方法:
- 通過(guò)now()方法返回ZonedDateTime對(duì)象;
- 通過(guò)給LocalDateTime附加ZoneId獲取。
接下來(lái)就通過(guò)一些具體的案例來(lái)給大家展示一下ZonedDateTime到底是怎么創(chuàng)建的。
2.1 now()方法
我們先來(lái)看看通過(guò)now()方法如何創(chuàng)建一個(gè)ZonedDateTime對(duì)象。
import java.time.ZoneId; import java.time.ZonedDateTime; /** * @author */ public class Demo18 { public static void main(String[] args) { //獲取默認(rèn)時(shí)區(qū)的時(shí)間對(duì)象 ZonedDateTime zdt1 = ZonedDateTime.now(); System.out.println("zdt1="+zdt1); //獲取指定時(shí)區(qū)的時(shí)間對(duì)象 ZonedDateTime zdt2 = ZonedDateTime.now(ZoneId.of("America/New_York")); System.out.println("zdt2="+zdt2); } }
在這個(gè)案例中,我們獲得的兩個(gè)ZonedDateTime對(duì)象,它們的時(shí)區(qū)雖然不同,但時(shí)間都是同一時(shí)刻的,如果毫秒數(shù)不同是在執(zhí)行語(yǔ)句時(shí)有一點(diǎn)時(shí)間差。
2.2 附加ZoneId
我們?cè)賮?lái)通過(guò)給LocalDateTime附加ZoneId的方式來(lái)獲取一個(gè)ZonedDateTime對(duì)象。
import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; /** * @author */ public class Demo19 { public static void main(String[] args) { LocalDateTime ldt = LocalDateTime.of(2023, 1, 25, 10, 15, 11); //獲取默認(rèn)時(shí)區(qū)的時(shí)間對(duì)象 ZonedDateTime zdt1 = ldt.atZone(ZoneId.systemDefault()); System.out.println("zdt1=" + zdt1); //獲取指定時(shí)區(qū)的時(shí)間對(duì)象 ZonedDateTime zdt2 = ldt.atZone(ZoneId.of("America/New_York")); System.out.println("zdt2=" + zdt2); } }
這種方式創(chuàng)建的ZonedDateTime對(duì)象,其日期和時(shí)間與LocalDateTime相同,但附加的時(shí)區(qū)不同,因此是兩個(gè)不同的時(shí)刻。
3. 時(shí)區(qū)轉(zhuǎn)換
有時(shí)我們?cè)陧?xiàng)目中需要將A時(shí)區(qū)的時(shí)間轉(zhuǎn)換成B時(shí)區(qū)的時(shí)間,這就需要在兩個(gè)時(shí)區(qū)之間進(jìn)行切換。以前如果我們想實(shí)現(xiàn)這種功能,就需要我們自己計(jì)算,而且非常的麻煩且復(fù)雜,現(xiàn)在新的Java API中直接給我們帶來(lái)了負(fù)責(zé)時(shí)區(qū)轉(zhuǎn)換的方法,這就非常方便了。利用withZoneSameInstant()方法,我們就可以將一個(gè)關(guān)聯(lián)的時(shí)區(qū)轉(zhuǎn)換到另一個(gè)時(shí)區(qū),轉(zhuǎn)換后的日期和時(shí)間也會(huì)相應(yīng)調(diào)整。
import java.time.ZoneId; import java.time.ZonedDateTime; /** * @author */ public class Demo20 { public static void main(String[] args) { //將北京時(shí)間轉(zhuǎn)為紐約時(shí)間 //獲取北京時(shí)區(qū)的當(dāng)前時(shí)間 //注意:這里的時(shí)區(qū)名字不能隨便瞎寫,否則會(huì)產(chǎn)生java.time.zone.ZoneRulesException: Unknown time-zone ID: Asia/Beijing ZonedDateTime zdt1 = ZonedDateTime.now(ZoneId.of("Asia/Shanghai")); System.out.println("zdt1=" + zdt1); //將當(dāng)前時(shí)區(qū)的時(shí)間,轉(zhuǎn)換為紐約時(shí)間 ZonedDateTime zdt2 = zdt1.withZoneSameInstant(ZoneId.of("America/New_York")); System.out.println("zdt2=" + zdt2); //轉(zhuǎn)換為本地時(shí)間 LocalDateTime ldt = zdt2.toLocalDateTime(); System.out.println("ldt=" + ldt); } }
注意:
時(shí)區(qū)的名字不能隨便瞎寫,否則會(huì)產(chǎn)生java.time.zone.ZoneRulesException: Unknown time-zone ID: Asia/Beijing,如下圖所示:
另外在時(shí)區(qū)轉(zhuǎn)換時(shí),由于夏令時(shí)的存在,不同的日期轉(zhuǎn)換結(jié)果有可能是不同的,有可能會(huì)出現(xiàn)兩次轉(zhuǎn)換后有1小時(shí)的夏令時(shí)時(shí)差。所以如果我們以后涉及到時(shí)區(qū)轉(zhuǎn)換時(shí),盡量不要自己計(jì)算時(shí)差,否則難以正確地處理夏令時(shí)。
四. DateTimeFormatter
1. 簡(jiǎn)介
我們?cè)谇懊鎸W(xué)習(xí)Date日期時(shí)間對(duì)象時(shí),知道該對(duì)象默認(rèn)輸出的時(shí)間格式其實(shí)是不符合大多數(shù)的使用場(chǎng)景的,所以一般都需要我們對(duì)其進(jìn)行格式化設(shè)置,比如通過(guò)printf()方法或SimpleDateFormat類來(lái)實(shí)現(xiàn)。但是當(dāng)我們使用新的LocalDateTime或ZonedDateTime進(jìn)行格式化顯示時(shí),就需要使用新的DateTimeFormatter類了。
和SimpleDateFormat不同的是,DateTimeFormatter不但是不可變的對(duì)象,且還是線程安全的。編程時(shí)我們可以先創(chuàng)建出一個(gè)DateTimeFormatter實(shí)例對(duì)象,然后根據(jù)需要引用即可。而之前的SimpleDateFormat則是線程不安全的,使用時(shí)只能在方法內(nèi)部創(chuàng)建出一個(gè)新的局部變量。
2. 創(chuàng)建方式
我們要想使用DateTimeFormatter,首先得創(chuàng)建出一個(gè)DateTimeFormatter對(duì)象,一般有如下兩種方式:
- DateTimeFormatter.ofPattern(String pattern):pattern是待傳入的格式化字符串;
- DateTimeFormatter.ofPattern(String pattern,Locale locale):locale是所采用的本地化設(shè)置。
3. 基本使用
了解了創(chuàng)建方式之后,我們就可以來(lái)看看該如何進(jìn)行時(shí)間格式化了。
import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Locale; /** * @author */ public class Demo21 { public static void main(String[] args) { //獲取默認(rèn)的本地時(shí)間 ZonedDateTime zdt = ZonedDateTime.now(); //獲取一個(gè)DateTimeFormatter對(duì)象,如果需要輸出固定字符,可以用'xxx'表示,如'中國(guó)時(shí)間' var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss ZZZZ '中國(guó)時(shí)間'"); System.out.println(formatter.format(zdt)); //獲取一個(gè)DateTimeFormatter對(duì)象,中國(guó)時(shí)區(qū) var zhFormatter = DateTimeFormatter.ofPattern("yyyy MMM dd EE HH:mm:ss", Locale.CHINA); System.out.println(zhFormatter.format(zdt)); //改變默認(rèn)的顯示格式,用指定的格式顯示 //System.out.println(DateTimeFormatter.ISO_DATE.format(zdt)); //獲取一個(gè)DateTimeFormatter對(duì)象,美國(guó)時(shí)區(qū) var usFormatter = DateTimeFormatter.ofPattern("E, MMMM/dd/yyyy HH:mm:ss", Locale.US); //System.out.println(usFormatter.format(zdt)); //改變默認(rèn)的顯示格式 System.out.println(DateTimeFormatter.ISO_DATE_TIME.format(zdt)); } }
當(dāng)我們?cè)诟袷交址畷r(shí),如果需要輸出一些固定的字符,可以用'xxx'的形式來(lái)表示。另外我們?cè)谡{(diào)用System.out.println()方法對(duì)一個(gè)ZonedDateTime,或者LocalDateTime實(shí)例進(jìn)行打印時(shí),實(shí)際上調(diào)用的是它們的toString()方法。默認(rèn)的toString()方法顯示的字符串是按照ISO 8601格式顯示的,我們可以通過(guò)DateTimeFormatter預(yù)定義的幾個(gè)靜態(tài)變量來(lái)引用。
五. Instant
1. 簡(jiǎn)介
在之前給大家講過(guò),計(jì)算機(jī)中存儲(chǔ)的當(dāng)前時(shí)間,其實(shí)就是一個(gè)不斷遞增的整數(shù)值,即從1970年1月1日0分0秒開(kāi)始以來(lái)不斷遞增的一個(gè)整數(shù)值。如果我們想要獲取這個(gè)整數(shù)值,可以使用System.currentTimeMillis()方法,該方法會(huì)返回一個(gè)long型的毫秒值,這就是當(dāng)前時(shí)間的時(shí)間戳!
而現(xiàn)在我們其實(shí)還可以使用Instant.now()來(lái) 獲取當(dāng)前的時(shí)間戳,效果和 System.currentTimeMillis() 類似。但I(xiàn)nstant獲取的時(shí)間戳更為精確,其內(nèi)部采用了兩種時(shí)間精度,分別是秒和納秒。
另外Instant還提供了plusXxx和minusXxx增減方法,方便我們進(jìn)行時(shí)間的操作。且Instant作為時(shí)間戳對(duì)象,我們還可以給它附加上一個(gè)時(shí)區(qū),創(chuàng)建出對(duì)應(yīng)的ZonedDateTime對(duì)象。我們也可以給它關(guān)聯(lián)上指定的ZoneId,得到對(duì)應(yīng)的ZonedDateTime,進(jìn)而獲得對(duì)應(yīng)時(shí)區(qū)的LocalDateTime,所以我們可以在LocalDateTime、ZoneId、Instant、ZonedDateTime之間互相轉(zhuǎn)換。
2. 使用方法
接下來(lái)我們看看Instant的用法。
import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; /** * @author */ public class Demo22 { public static void main(String[] args) { //獲取當(dāng)前時(shí)間的時(shí)間戳 long currentTimeMillis = System.currentTimeMillis(); System.out.println("currentTimeMillis毫秒級(jí)時(shí)間戳="+currentTimeMillis); //獲取當(dāng)前時(shí)間的時(shí)間戳 Instant now = Instant.now(); System.out.println("now時(shí)刻="+now); // 秒 System.out.println("秒級(jí)時(shí)間戳="+now.getEpochSecond()); // 毫秒 System.out.println("毫秒級(jí)時(shí)間戳="+now.toEpochMilli()); //用指定的時(shí)間戳創(chuàng)建Instant對(duì)象 Instant ins = Instant.ofEpochSecond(1676262418); //獲取所在時(shí)區(qū)的ZonedDateTime對(duì)象 ZonedDateTime zdt = ins.atZone(ZoneId.systemDefault()); System.out.println("zdt="+zdt); } }
六. 新舊時(shí)間API的轉(zhuǎn)換
1. 簡(jiǎn)介
現(xiàn)在我們知道,在Java中,關(guān)于日期和時(shí)間的API其實(shí)有兩套,一套舊的,一套新的。這兩套API以JDK 8為分割線,此前的屬于舊版API,此后的屬于新版API。
- 舊版API:定義在java.util包中,主要包括Date、Calendar和TimeZone幾個(gè)類;
- 新版API:JDK 8之后引入,定義在java.time包中,主要包括LocalDateTime、ZonedDateTime、ZoneId、DateTimeFormatter、Instant等。
這時(shí)有些小白就會(huì)好奇,這兩套時(shí)間API我們?cè)陂_(kāi)發(fā)時(shí)該怎么選擇?其實(shí),如果大家的項(xiàng)目屬于是一個(gè)新開(kāi)發(fā)的項(xiàng)目,且你們的Java版本在JDK 8以上,那就采用新版的API吧。但是如果你們的項(xiàng)目涉及到遺留代碼,是對(duì)舊的項(xiàng)目進(jìn)行維護(hù)或改造,很多遺留代碼仍然在使用舊的API,建議大家還是不要改變?cè)械倪x擇,請(qǐng)繼續(xù)使用舊版API。
但是如果你們的項(xiàng)目環(huán)境已經(jīng)從低版本的JDK切換到了高本版的JDK,且你們公司要求對(duì)項(xiàng)目進(jìn)行升級(jí)改造,我們能不能在新舊兩種API之間進(jìn)行轉(zhuǎn)換呢?其實(shí)這也是可以的,今天就給大家講一下如何在新舊兩套API之間互相轉(zhuǎn)換。
2. 舊轉(zhuǎn)新
首先我們來(lái)看看舊的API是如何轉(zhuǎn)成新的API的。比如我們想把舊式的 Date 或 Calendar 轉(zhuǎn)換為新的API對(duì)象,可以通過(guò) toInstant() 方法轉(zhuǎn)換為 Instant 對(duì)象,然后再繼續(xù)轉(zhuǎn)換為 ZonedDateTime,實(shí)現(xiàn)代碼如下:
import java.time.Instant; import java.time.ZonedDateTime; import java.util.Calendar; import java.util.Date; /** * @author */ public class Demo24 { public static void main(String[] args) { //將舊版API中的Date,轉(zhuǎn)為新版API中的Instant對(duì)象 Instant instant = new Date().toInstant(); System.out.println("instant="+instant); //將舊版API中的Calendar,轉(zhuǎn)為新版API中的Instant,然后再進(jìn)一步轉(zhuǎn)為新版的ZonedDateTime: Calendar calendar = Calendar.getInstance(); Instant instant2 = calendar.toInstant(); ZonedDateTime zdt = instant2.atZone(calendar.getTimeZone().toZoneId()); System.out.println("zdt="+zdt); } }
3. 新轉(zhuǎn)舊
舊版API可以轉(zhuǎn)換成新版API,同時(shí)我們也可以將新班API轉(zhuǎn)成舊版的API,已實(shí)現(xiàn)與原有系統(tǒng)的兼容。如果要實(shí)現(xiàn)這一目標(biāo),我們需要借助 long 型的時(shí)間戳做一個(gè)“中轉(zhuǎn)” ,具體實(shí)現(xiàn)如下:
import java.time.ZonedDateTime; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; public class Demo25 { public static void main(String[] args) { //新版API中的ZonedDateTime,先轉(zhuǎn)為long類型 ZonedDateTime zdt = ZonedDateTime.now(); //獲取秒級(jí)時(shí)間戳,轉(zhuǎn)為long類型 long ts = zdt.toEpochSecond() * 1000; System.out.println("ts=" + ts); //然后將long類型,轉(zhuǎn)為舊版API中的Date Date date = new Date(ts); System.out.println("date=" + date); //將long類型轉(zhuǎn)為舊版API中的Calendar對(duì)象 Calendar calendar = Calendar.getInstance(); calendar.clear(); calendar.setTimeZone(TimeZone.getTimeZone(zdt.getZone().getId())); calendar.setTimeInMillis(zdt.toEpochSecond() * 1000); System.out.println("calendar=" + calendar); } }
七. 結(jié)語(yǔ)
至此,就把日期的格式化操作給大家講解完畢了,今天的內(nèi)容比較多且很實(shí)用。接下來(lái)就把今日重點(diǎn)給大家總結(jié)一下:
- JDK 8中引入了新的日期和時(shí)間API,它們是不變類,默認(rèn)按ISO 8601標(biāo)準(zhǔn)格式化和解析;
- 使用LocalDateTime可以非常方便地對(duì)日期和時(shí)間進(jìn)行加減、調(diào)整日期和時(shí)間,且總是返回新對(duì)象;
- 使用isBefore()和isAfter()可以判斷日期和時(shí)間的先后;
- 使用Duration和Period可以表示兩個(gè)日期和時(shí)間的“區(qū)間間隔”;
- ZonedDateTime是帶時(shí)區(qū)的日期和時(shí)間,可用于時(shí)區(qū)轉(zhuǎn)換;
- ZonedDateTime和LocalDateTime可以相互轉(zhuǎn)換;
- 對(duì)ZonedDateTime或LocalDateTime進(jìn)行格式化,需要使用DateTimeFormatter類;
- DateTimeFormatter可以通過(guò)格式化字符串和Locale對(duì)日期和時(shí)間進(jìn)行定制輸出;
- Instant表示高精度時(shí)間戳,它可以和ZonedDateTime以及l(fā)ong互相轉(zhuǎn)換;
- 處理日期和時(shí)間時(shí),盡量使用新的java.time包;
- 新舊兩種API之間可以進(jìn)行轉(zhuǎn)換,舊版轉(zhuǎn)新版可以通過(guò)toInstant()方法轉(zhuǎn)換為Instant對(duì)象,然后再繼續(xù)轉(zhuǎn)換為ZonedDateTime;新版轉(zhuǎn)舊版需要借助long型的時(shí)間戳做一個(gè)“中轉(zhuǎn)”。
以上就是詳解Java的日期時(shí)間新特性的詳細(xì)內(nèi)容,更多關(guān)于Java 日期時(shí)間新特性的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解jdbc實(shí)現(xiàn)對(duì)CLOB和BLOB數(shù)據(jù)類型的操作
這篇文章主要介紹了詳解jdbc實(shí)現(xiàn)對(duì)CLOB和BLOB數(shù)據(jù)類型的操作的相關(guān)資料,這里實(shí)現(xiàn)寫入操作與讀寫操作,需要的朋友可以參考下2017-08-08IntelliJ?IDEA?2022安裝注冊(cè)永久激活
java開(kāi)發(fā)工具IntelliJ?IDEA深受用戶喜愛(ài),很多朋友對(duì)這個(gè)idea開(kāi)發(fā)工具比較忠心,一旦有新版本發(fā)出,很多小伙伴就迫不及待的想更新,今天小編給大家?guī)?lái)了idea2022.1最新永久激活碼,親測(cè)有效,喜歡的朋友快來(lái)下載體驗(yàn)吧2022-08-08Java如何實(shí)現(xiàn)通過(guò)證書(shū)訪問(wèn)Https請(qǐng)求
這篇文章主要介紹了Java如何實(shí)現(xiàn)通過(guò)證書(shū)訪問(wèn)Https請(qǐng)求,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01Java常用數(shù)字工具類 大數(shù)乘法、加法、減法運(yùn)算(2)
這篇文章主要為大家詳細(xì)介紹了Java常用數(shù)字工具類,大數(shù)乘法、加法、減法運(yùn)算,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05IDEA中配置多個(gè)版本的JDK的實(shí)現(xiàn)示例
IDEA可以配置多個(gè)JDK,根據(jù)需要使用不同版本的,本文就來(lái)介紹一下IDEA中配置多個(gè)版本的JDK的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03Java遞歸運(yùn)行的機(jī)制:遞歸的微觀解讀圖文分析
這篇文章主要介紹了Java遞歸運(yùn)行的機(jī)制:遞歸的微觀解讀,結(jié)合圖文形式詳細(xì)分析了java遞歸運(yùn)行的原理、機(jī)制與相關(guān)注意事項(xiàng),需要的朋友可以參考下2020-03-03js+java實(shí)現(xiàn)登錄滑動(dòng)圖片驗(yàn)證
這篇文章主要為大家詳細(xì)介紹了js+java實(shí)現(xiàn)登錄滑動(dòng)圖片驗(yàn)證,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03