《Java8实战》笔记(12):新的日期和时间API
新的日期和時間API
相信大多數有經驗的程序員都會贊同Java 8之前的庫對日期和時間的支持就非常不理想。值得高興的是 Java 8中引入全新的日期和時間API就是要解決這一問題。
Java 8之前的日期和時間API
在Java 1.0中,對日期和時間的支持只能依賴java.util.Date類。正如類名所表達的,這個類無法表示日期,只能以毫秒的精度表示時間。
更糟糕的是它的易用性,由于某些原因未知的設計決策,這個類的易用性被深深地損害了,比如:年份的起始選擇是1900年,月份的起始從0開始。這意味著,如果你想要用Date表示Java 8的發布日期,即2014年3月18日,需要創建下面這樣的Date實例:
Date date = new Date(114, 2, 18);它的打印輸出效果為:
Tue Mar 18 00:00:00 CET 2014Date類的toString方法返回的字符串也容易誤導人。以我們的例子而言,它的返回值中甚至還包含了JVM的默認時區CET,即中歐時間(Central Europe Time)。但這并不表示Date類在任何方面支持時區。
隨著Java 1.0退出歷史舞臺,Date類的種種問題和限制幾乎一掃而光,但很明顯,這些歷史舊賬如果不犧牲前向兼容性是無法解決的。
所以,在Java 1.1中,Date類中的很多方法被廢棄了,取而代之的是java.util.Calendar類。很不幸,Calendar類也有類似的問題和設計缺陷,導致使用這些方法寫出的代碼非常容易出錯。
比如,月份依舊是從0開始計算(不過,至少Calendar類拿掉了由1900年開始計算年份這一設計)。更糟的是,同時存在Date和Calendar這兩個類,
也增加了程序員的困惑。到底該使用哪一個類呢?此外,有的特性只在某一個類有提供,比如用于以語言無關方式格式化和解析日期或時間的DateFormat方法就只在Date類里有。
DateFormat方法也有它自己的問題。比如,**它不是線程安全的。**這意味著兩個線程如果嘗試使用同一個formatter解析日期,你可能會得到無法預期的結果。
最后,Date和Calendar類都是可以變的。能把2014年3月18日修改成4月18日意味著什么呢?這種設計會將你拖入維護的噩夢。
所有這些缺陷和不一致導致用戶們轉投第三方的日期和時間庫,比如Joda-Time。為了解決這些問題,Oracle決定在原生的Java API中提供高質量的日期和時間支持。所以,你會看到Java 8在java.time包中整合了很多Joda-Time的特性。
DateTimeExamples
LocalDate、LocalTime、Instant、Duration 以及Period
使用LocalDate 和LocalTime
LocalDate類實例是一個不可變對象,它只提供了簡單的日期,并不含當天的時間信息。另外,它也不附帶任何與時區相關的信息。
創建一個LocalDate對象并讀取其值
LocalDate date = LocalDate.of(2014, 3, 18); int year = date.getYear();//2014 Month month = date.getMonth();//MARCH int day = date.getDayOfMonth();//18 DayOfWeek dow = date.getDayOfWeek();//TUESDAY int len = date.lengthOfMonth();//31 boolean leap = date.isLeapYear();//false可以使用工廠方法從系統時鐘中獲取當前的日期:
LocalDate today = LocalDate.now();使用TemporalField讀取LocalDate的值
int year = date.get(ChronoField.YEAR); int month = date.get(ChronoField.MONTH_OF_YEAR); int day = date.get(ChronoField.DAY_OF_MONTH);一天中的時間,比如13:45:20,可以使用LocalTime類表示
創建LocalTime并讀取其值
LocalTime time = LocalTime.of(13, 45, 20); int hour = time.getHour();//13 int minute = time.getMinute();//45 int second = time.getSecond();//20LocalDate和LocalTime都可以通過解析代表它們的字符串創建。使用靜態方法parse,你可以實現這一目的:
LocalDate date = LocalDate.parse("2014-03-18"); LocalTime time = LocalTime.parse("13:45:20");合并日期和時間
這個復合類名叫LocalDateTime,是LocalDate和LocalTime的合體。它同時表示了日期和時間,但不帶有時區信息,你可以直接創建,也可以通過合并日期和時間對象構造
直接創建LocalDateTime對象,或者通過合并日期和時間的方式創建
// 2014-03-18T13:45:20 LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20); LocalDateTime dt2 = LocalDateTime.of(date, time); LocalDateTime dt3 = date.atTime(13, 45, 20); LocalDateTime dt4 = date.atTime(time); LocalDateTime dt5 = time.atDate(date);也可以使用toLocalDate或者toLocalTime方法,從LocalDateTime中提取LocalDate或者LocalTime組件
LocalDate date1 = dt1.toLocalDate(); LocalTime time1 = dt1.toLocalTime();機器的日期和時間格式
作為人,我們習慣于以星期幾、幾號、幾點、幾分這樣的方式理解日期和時間。毫無疑問,這種方式對于計算機而言并不容易理解。
從計算機的角度來看,建模時間最自然的格式是表示一個持續時間段上某個點的單一大整型數。這也是新的java.time.Instant類對時間建模的方式,基本上它是以Unix元年時間(傳統的設定為UTC時區1970年1月1日午夜時分)開始所經歷的秒數進行計算。
你可以通過向靜態工廠方法ofEpochSecond傳遞一個代表秒數的值創建一個該類的實例。靜態工廠方法ofEpochSecond還有一個增強的重載版本,它接收第二個以納秒為單位的參數值,對傳入作為秒數的參數進行調整。重載的版本會調整納秒參數,確保保存的納秒分片在0到999 999 999之間。
Instant.ofEpochSecond(3); Instant.ofEpochSecond(3, 0); Instant.ofEpochSecond(2, 1_000_000_000); Instant.ofEpochSecond(4, -1_000_000_000);Instant類也支持靜態工廠方法now,它能夠幫你獲取當前時刻的時間戳。我們想要特別強調一點,Instant的設計初衷是為了便于機器使用。它包含的是由秒及納秒所構成的數字。所以,它無法處理那些我們非常容易理解的時間單位。
int day = Instant.now().get(ChronoField.DAY_OF_MONTH);它會拋出下面這樣的異常:
java.time.temporal.UnsupportedTemporalTypeException: Unsupported field:DayOfMonth定義Duration或Period
目前為止,你看到的所有類都實現了Temporal接口,Temporal接口定義了如何讀取和操縱為時間建模的對象的值。
創建兩個Temporal對象之間的duration。Duration類的靜態工廠方法between就是為這個目的而設計的。你可以創建兩個LocalTimes對象、兩個LocalDateTimes對象,或者兩個Instant對象之間的duration,
Duration d1 = Duration.between(time1, time2); Duration d1 = Duration.between(dateTime1, dateTime2); Duration d2 = Duration.between(instant1, instant2);由于LocalDateTime和Instant是為不同的目的而設計的,一個是為了便于人閱讀使用,另一個是為了便于機器處理,所以你不能將二者混用。如果你試圖在這兩類對象之間創建duration,會觸發一個DateTimeException異常。此外,由于Duration類主要用于以秒和納秒衡量時間的長短,你不能僅向between方法傳遞一個LocalDate對象做參數。
如果你需要以年、月或者日的方式對多個時間單位建模,可以使用Period類。使用該類的工廠方法between,你可以使用得到兩個LocalDate之間的時長,如下所示:
Period tenDays = Period.between(LocalDate.of(2014, 3, 8), LocalDate.of(2014, 3, 18));創建Duration和Period對象
Duration threeMinutes = Duration.ofMinutes(3); Duration threeMinutes = Duration.of(3, ChronoUnit.MINUTES);Period tenDays = Period.ofDays(10); Period threeWeeks = Period.ofWeeks(3); Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);Duration類和Period類共享了很多相似的方法
日期-時間類中表示時間間隔的通用方法
| between | 是 | 創建兩個時間點之間的interval |
| from | 是 | 由一個臨時時間點創建interval |
| of | 是 | 由它的組成部分創建interval的實例 |
| parse | 是 | 由字符串創建interval的實例 |
| addTo | 否 | 創建該interval的副本,并將其疊加到某個指定的temporal對象 |
| get | 否 | 讀取該interval的狀態 |
| isNegative | 否 | 檢查該interval是否為負值,不包含零 |
| isZero | 否 | 檢查該interval的時長是否為零 |
| minus | 否 | 通過減去一定的時間創建該interval的副本 |
| multipliedBy | 否 | 將interval的值乘以某個標量創建該interval的副本 |
| negated | 否 | 以忽略某個時長的方式創建該interval的副本 |
| plus | 否 | 以增加某個指定的時長的方式創建該interval的副本 |
| subtractFrom | 否 | 從指定的temporal對象中減去該interval |
截至目前,我們介紹的這些日期-時間對象都是不可修改的,這是為了更好地支持函數式編程,確保線程安全,保持領域模式一致性而做出的重大設計決定。
當然,新的日期和時間API也提供了一些便利的方法來創建這些對象的可變版本。比如,你可能希望在已有的LocalDate實例上增加3天。
操縱、解析和格式化日期
如果你已經有一個LocalDate對象,想要創建它的一個修改版,最直接也最簡單的方法是使用withAttribute方法。
withAttribute方法會創建對象的一個副本,并按照需要修改它的屬性。注意,下面的這段代碼中所有的方法都返回一個修改了屬性的對象。它們都不會修改原來的對象!
LocalDate date1 = LocalDate.of(2014, 3, 18); LocalDate date2 = date1.withYear(2011); LocalDate date3 = date2.withDayOfMonth(25); LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 9);它們都聲明于Temporal接口,所有的日期和時間API類都實現這兩個方法,它們定義了單點的時間,比如LocalDate、LocalTime、LocalDateTime以及Instant。
更確切地說,使用get和with方法,我們可以將Temporal對象值的讀取和修改區分開。如果Temporal對象不支持請求訪問的字段,它會拋出一個UnsupportedTemporalTypeException異常,比如試圖訪問Instant對象的ChronoField.MONTH_OF_YEAR字段,或者LocalDate對象的ChronoField.NANO_OF_SECOND字段時都會拋出這樣的異常。
以相對方式修改LocalDate對象的屬性
LocalDate date1 = LocalDate.of(2014, 3, 18); LocalDate date2 = date1.plusWeeks(1); LocalDate date3 = date2.minusYears(3); LocalDate date4 = date3.plus(6, ChronoUnit.MONTHS);像LocalDate、LocalTime、LocalDateTime以及Instant這樣表示時間點的日期-時間類提供了大量通用的方法
表示時間點的日期-時間類的通用方法
| from | 是 | 依據傳入的Temporal對象創建對象實例 |
| now | 是 | 依據系統時鐘創建Temporal對象 |
| of | 是 | 由Temporal對象的某個部分創建該對象的實例 |
| parse | 是 | 由字符串創建Temporal對象的實例 |
| atOffset | 否 | 將Temporal對象和某個時區偏移相結合 |
| atZone | 否 | 將Temporal對象和某個時區相結合 |
| format | 否 | 使用某個指定的格式器將Temporal對象轉換為字符串(Instant類不提供該方法) |
| get | 否 | 讀取Temporal對象的某一部分的值 |
| minus | 否 | 創建Temporal對象的一個副本,通過將當前Temporal對象的值減去一定的時長創建該副本 |
| plus | 否 | 創建Temporal對象的一個副本,通過將當前Temporal對象的值加上一定的時長創建該副本 |
| with | 否 | 以該Temporal對象為模板,對某些狀態進行修改創建該對象的副本 |
使用TemporalAdjuster
截至目前,你所看到的所有日期操作都是相對比較直接的。有的時候,你需要進行一些更加復雜的操作,比如,將日期調整到下個周日、下個工作日,或者是本月的最后一天。
這時,你可以使用重載版本的with方法,向其傳遞一個提供了更多定制化選擇的TemporalAdjuster對象,更加靈活地處理日期。對于最常見的用例, 日期和時間API已經提供了大量預定義的TemporalAdjuster。你可以通過TemporalAdjuster類的靜態工廠方法訪問它們,
import static java.time.temporal.TemporalAdjusters.*; LocalDate date1 = LocalDate.of(2014, 3, 18); LocalDate date2 = date1.with(nextOrSame(DayOfWeek.SUNDAY));//2014-03-23 LocalDate date3 = date2.with(lastDayOfMonth());//2014-03-31TemporalAdjuster中包含的工廠方法列表
| dayOfWeekInMonth | 創建一個新的日期,它的值為同一個月中每一周的第幾天 |
| firstDayOfMonth | 創建一個新的日期,它的值為當月的第一天 |
| firstDayOfNextMonth | 創建一個新的日期,它的值為下月的第一天 |
| firstDayOfNextYear | 創建一個新的日期,它的值為明年的第一天 |
| firstDayOfYear | 創建一個新的日期,它的值為當年的第一天 |
| firstInMonth | 創建一個新的日期,它的值為同一個月中,第一個符合星期幾要求的值 |
| lastDayOfMonth | 創建一個新的日期,它的值為當月的最后一天 |
| lastDayOfNextMonth | 創建一個新的日期,它的值為下月的最后一天 |
| lastDayOfNextYear | 創建一個新的日期,它的值為明年的最后一天 |
| lastDayOfYear | 創建一個新的日期,它的值為今年的最后一天 |
| lastInMonth | 創建一個新的日期,它的值為同一個月中,最后一個符合星期幾要求的值 |
| next/previous | 創建一個新的日期,并將其值設定為日期調整后或者調整前,第一個符合指定星期幾要求的日期 |
| nextOrSame/previousOrSame | 創建一個新的日期,并將其值設定為日期調整后或者調整前,第一個符合指定星期幾要求的日期,如果該日期已經符合要求,直接返回該對象 |
自定義TemporalAdjuster
使用TemporalAdjuster我們可以進行更加復雜的日期操作,而且這些方法的名稱也非常直觀,方法名基本就是問題陳述。此外,即使你沒有找到符合你要求的預定義的TemporalAdjuster,創建你自己的TemporalAdjuster也并非難事。實際上,TemporalAdjuster接口只聲明了單一的一個方法(這使得它成為了一個函數式接口)。
@FunctionalInterface public interface TemporalAdjuster {Temporal adjustInto(Temporal temporal); }這意味著TemporalAdjuster接口的實現需要定義如何將一個Temporal對象轉換為另一個Temporal對象。你可以把它看成一個UnaryOperator<Temporal>。
實現一個定制的TemporalAdjuster
請設計一個NextWorkingDay類,該類實現了TemporalAdjuster接口,能夠計算明天的日期,同時過濾掉周六和周日這些節假日。格式如下所示:
date = date.with(new NextWorkingDay());如果當天的星期介于周一至周五之間,日期向后移動一天;如果當天是周六或者周日,則返回下一個周一。
public class NextWorkingDay implements TemporalAdjuster {@Overridepublic Temporal adjustInto(Temporal temporal) {DayOfWeek dow = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));int dayToAdd = 1;if (dow == DayOfWeek.FRIDAY) dayToAdd = 3;else if (dow == DayOfWeek.SATURDAY) dayToAdd = 2;return temporal.plus(dayToAdd, ChronoUnit.DAYS);} }該TemporalAdjuster通常情況下將日期往后順延一天,如果當天是周六或者周日,則依據情況分別將日期順延3天或者2天。
注意,由于TemporalAdjuster是一個函數式接口,你只能以Lambda表達式的方式向該adjuster接口傳遞行為:
date = date.with(temporal -> {DayOfWeek dow = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));//... });你大概會希望在你代碼的多個地方使用同樣的方式去操作日期,為了達到這一目的,我們建議你像我們的示例那樣將它的邏輯封裝到一個類中。對于你經常使用的操作,都應該采用類似的方式,進行封裝。最終,你會創建自己的類庫,讓你和你的團隊能輕松地實現代碼復用。
如果你想要使用Lambda表達式定義TemporalAdjuster對象,推薦使用TemporalAdjusters類的靜態工廠方法ofDateAdjuster,它接受一個UnaryOperator<LocalDate>類型的參數,代碼如下:
TemporalAdjuster nextWorkingDay = TemporalAdjusters.ofDateAdjuster(temporal -> {DayOfWeek dow = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));//... }); date = date.with(nextWorkingDay);打印輸出及解析日期-時間對象
格式化以及解析日期?時間對象是另一個非常重要的功能。新的java.time.format包就是特別為這個目的而設計的。這個包中,最重要的類是DateTimeFormatter。創建格式器最簡單的方法是通過它的靜態工廠方法以及常量。
LocalDate date = LocalDate.of(2014, 3, 18); String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE); String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE);也可以通過解析代表日期或時間的字符串重新創建該日期對象。
LocalDate date1 = LocalDate.parse("20140318",DateTimeFormatter.BASIC_ISO_DATE); LocalDate date2 = LocalDate.parse("2014-03-18",DateTimeFormatter.ISO_LOCAL_DATE);和老的java.util.DateFormat相比較,所有的DateTimeFormatter實例都是線程安全的。所以,你能夠以單例模式創建格式器實例,就像DateTimeFormatter所定義的那些常量,并能在多個線程間共享這些實例。
按照某個模式創建DateTimeFormatter
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");LocalDate date1 = LocalDate.of(2014, 3, 18);String formattedDate = date1.format(formatter);LocalDate date2 = LocalDate.parse(formattedDate, formatter);創建一個本地化的DateTimeFormatter
DateTimeFormatter italianFormatter = DateTimeFormatter.ofPattern("d. MMMM yyyy", Locale.ITALIAN); LocalDate date1 = LocalDate.of(2014, 3, 18); String formattedDate = date.format(italianFormatter); // 18. marzo 2014 LocalDate date2 = LocalDate.parse(formattedDate, italianFormatter);你還需要更加細粒度的控制,DateTimeFormatterBuilder類還提供了更復雜的格式器,你可以選擇恰當的方法,一步一步地構造自己的格式器。另外,它還提供了非常強大的解析功能,比如區分大小寫的解析、柔性解析(允許解析器使用啟發式的機制去解析輸入,不精確地匹配指定的模式)、填充, 以及在格式器中指定可選節。比如,
你可以通過DateTimeFormatterBuilder自己編程實現上面代碼中使用的italianFormatter
DateTimeFormatter italianFormatter = new DateTimeFormatterBuilder().appendText(ChronoField.DAY_OF_MONTH).appendLiteral(". ").appendText(ChronoField.MONTH_OF_YEAR).appendLiteral(" ").appendText(ChronoField.YEAR).parseCaseInsensitive().toFormatter(Locale.ITALIAN);處理不同的時區和歷法
之前你看到的日期和時間的種類都不包含時區信息。時區的處理是新版日期和時間API新增加的重要功能,使用新版日期和時間API時區的處理被極大地簡化了。新的java.time.ZoneId類是老版java.util.TimeZone的替代品。它的設計目標就是要讓你無需為時區處理的復雜和繁瑣而操心,比如處理日光時(Daylight Saving Time,DST)這種問題。跟其他日期和時間類一樣,ZoneId類也是無法修改的。
時區是按照一定的規則將區域劃分成的標準時間相同的區間。在ZoneRules這個類中包含了40個這樣的實例。你可以簡單地通過調用ZoneId的getRules()得到指定時區的規則。每個特定的ZoneId對象都由一個地區ID標識,比如:
ZoneId romeZone = ZoneId.of("Europe/Rome");地區ID都為“{區域}/{城市}”的格式,這些地區集合的設定都由英特網編號分配機構(IANA)的時區數據庫提供。你可以通過Java 8的新方法toZoneId將一個老的時區對象轉換為ZoneId:
ZoneId zoneId = TimeZone.getDefault().toZoneId();一旦得到一個ZoneId對象,你就可以將它與LocalDate、LocalDateTime或者是Instant對象整合起來,構造為一個ZonedDateTime實例,它代表了相對于指定時區的時間點,
LocalDate date = LocalDate.of(2014, Month.MARCH, 18); ZonedDateTime zdt1 = date.atStartOfDay(romeZone); LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45); ZonedDateTime zdt2 = dateTime.atZone(romeZone); Instant instant = Instant.now(); ZonedDateTime zdt3 = instant.atZone(romeZone);下圖對ZonedDateTime的組成部分進行了說明,相信能夠幫助你理解LocaleDate、LocalTime、LocalDateTime以及ZoneId之間的差異。
通過ZoneId,你還可以將LocalDateTime轉換為Instant:
LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45); Instant instantFromDateTime = dateTime.toInstant(romeZone);你也可以通過反向的方式得到LocalDateTime對象:
Instant instant = Instant.now(); LocalDateTime timeFromInstant = LocalDateTime.ofInstant(instant, romeZone);利用和UTC/格林尼治時間的固定偏差計算時區
另一種比較通用的表達時區的方式是利用當前時區和UTC/格林尼治的固定偏差。比如,基于這個理論,你可以說“紐約落后于倫敦5小時”。這種情況下,你可以使用ZoneOffset類,它是ZoneId的一個子類,表示的是當前時間和倫敦格林尼治子午線時間的差異:
ZoneOffset newYorkOffset = ZoneOffset.of("-05:00");“-05:00”的偏差實際上對應的是美國東部標準時間。注意,使用這種方式定義的ZoneOffset并未考慮任何日光時的影響,所以在大多數情況下,不推薦使用。由于ZoneOffset也是ZoneId,所以你可以像上面代碼那樣使用它。你甚至還可以創建這樣的OffsetDateTime,它使用ISO-8601的歷法系統,以相對于UTC/格林尼治時間的偏差方式表示日期時間。
LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45); OffsetDateTime dateTimeInNewYork = OffsetDateTime.of(date, newYorkOffset);使用別的日歷系統
ISO-8601日歷系統是世界文明日歷系統的事實標準。但是,Java 8中另外還提供了4種其他的日歷系統。這些日歷系統中的每一個都有一個對應的日志類,分別是ThaiBuddhistDate、MinguoDate 、JapaneseDate 以及HijrahDate 。所有這些類以及LocalDate 都實現了ChronoLocalDate接口,能夠對公歷的日期進行建模。利用LocalDate對象,你可以創建這些類的實例。更通用地說,使用它們提供的靜態工廠方法,你可以創建任何一個Temporal對象的實例,如下所示:
LocalDate date = LocalDate.of(2014, Month.MARCH, 18); JapaneseDate japaneseDate = JapaneseDate.from(date);或者,你還可以為某個Locale顯式地創建日歷系統,接著創建該Locale對應的日期的實例。新的日期和時間API中,Chronology接口建模了一個日歷系統,使用它的靜態工廠方法ofLocale,可以得到它的一個實例,代碼如下:
Chronology japaneseChronology = Chronology.ofLocale(Locale.JAPAN); ChronoLocalDate now = japaneseChronology.dateNow();日期及時間API的設計者建議我們使用LocalDate,盡量避免使用ChronoLocalDate,原因是開發者在他們的代碼中可能會做一些假設,而這些假設在不同的日歷系統中,有可能不成立。比如,有人可能會做這樣的假設,即一個月天數不會超過31天,一年包括12個月,或者一年中包含的月份數目是固定的。由于這些原因,我們建議你盡量在你的應用中使用LocalDate,包括存儲、操作、業務規則的解讀;不過如果你需要將程序的輸入或者輸出本地化,這時你應該使用ChronoLocalDate類。
伊斯蘭教日歷
在Java 8新添加的幾種日歷類型中,HijrahDate(伊斯蘭教日歷)是最復雜一個,因為它會發生各種變化。Hijrah日歷系統構建于農歷月份繼承之上。Java 8提供了多種方法判斷一個月份,比如新月,在世界的哪些地方可見,或者說它只能首先可見于沙特阿拉伯。withVariant方法可以用于選擇期望的變化。為了支持HijrahDate這一標準,Java 8中還包括了烏姆庫拉(Umm Al-Qura)變量。下面這段代碼作為一個例子說明了如何在ISO日歷中計算當前伊斯蘭年中齋月的起始和終止日期:
HijrahDate ramadanDate = HijrahDate.now().with(ChronoField.DAY_OF_MONTH, 1).with(ChronoField.MONTH_OF_YEAR, 9);System.out.println("Ramadan starts on " +IsoChronology.INSTANCE.date(ramadanDate) +" and ends on " +IsoChronology.INSTANCE.date(ramadanDate.with(TemporalAdjusters.lastDayOfMonth())));小結
- Java 8之前老版的java.util.Date類以及其他用于建模日期時間的類有很多不一致及設計上的缺陷,包括易變性以及糟糕的偏移值、默認值和命名。
- 新版的日期和時間API中,日期-時間對象是不可變的。
- 新的API提供了兩種不同的時間表示方式,有效地區分了運行時人和機器的不同需求。
- 你可以用絕對或者相對的方式操縱日期和時間,操作的結果總是返回一個新的實例,老的日期時間對象不會發生變化。
- TemporalAdjuster讓你能夠用更精細的方式操縱日期,不再局限于一次只能改變它的一個值,并且你還可按照需求定義自己的日期轉換器。
- 你現在可以按照特定的格式需求,定義自己的格式器,打印輸出或者解析日期-時間對象。這些格式器可以通過模板創建,也可以自己編程創建,并且它們都是線程安全的。
- 你可以用相對于某個地區/位置的方式,或者以與UTC/格林尼治時間的絕對偏差的方式表示時區,并將其應用到日期-時間對象上,對其進行本地化。
- 你現在可以使用不同于ISO-8601標準系統的其他日歷系統了。
總結
以上是生活随笔為你收集整理的《Java8实战》笔记(12):新的日期和时间API的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python中[:]与[::]的用法
- 下一篇: 《Java8实战》笔记(11):Comp