国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

《Java8實(shí)戰(zhàn)》-第十二章筆記(新的日期和時(shí)間API)

khlbat / 1342人閱讀

摘要:合并日期和時(shí)間這個(gè)復(fù)合類(lèi)名叫,是和的合體。截至目前,我們介紹的這些日期時(shí)間對(duì)象都是不可修改的,這是為了更好地支持函數(shù)式編程,確保線程安全,保持領(lǐng)域模式一致性而做出的重大設(shè)計(jì)決定。

新的日期和時(shí)間API

Java的API提供了很多有用的組件,能幫助你構(gòu)建復(fù)雜的應(yīng)用。不過(guò),Java API也不總是完美的。我們相信大多數(shù)有經(jīng)驗(yàn)的程序員都會(huì)贊同Java 8之前的庫(kù)對(duì)日期和時(shí)間的支持就非常不理想。然而,你也不用太擔(dān)心:Java 8中引入全新的日期和時(shí)間API就是要解決這一問(wèn)題。

在Java 1.0中,對(duì)日期和時(shí)間的支持只能依賴(lài)java.util.Date類(lèi)。正如類(lèi)名所表達(dá)的,這個(gè)類(lèi)無(wú)法表示日期,只能以毫秒的精度表示時(shí)間。更糟糕的是它的易用性,由于某些原因未知的設(shè)計(jì)決策,這個(gè)類(lèi)的易用性被深深地?fù)p害了,比如:年份的起始選擇是1900年,月份的起始從0開(kāi)始。這意味著,如果你想要用Date表示Java 8的發(fā)布日期,即2014年3月18日,需要?jiǎng)?chuàng)建下面這樣的Date實(shí)例:

Date date = new Date(114, 2, 18);

它的打印輸出效果為:

Tue Mar 18 00:00:00 CST 2014

看起來(lái)不那么直觀,不是嗎?此外,甚至Date類(lèi)的toString方法返回的字符串也容易誤導(dǎo)人。

隨著Java 1.0退出歷史舞臺(tái),Date類(lèi)的種種問(wèn)題和限制幾乎一掃而光,但很明顯,這些歷史舊賬如果不犧牲前向兼容性是無(wú)法解決的。所以,在Java 1.1中,Date類(lèi)中的很多方法被廢棄了,取而代之的是java.util.Calendar類(lèi)。很不幸,Calendar類(lèi)也有類(lèi)似的問(wèn)題和設(shè)計(jì)缺陷,導(dǎo)致使用這些方法寫(xiě)出的代碼非常容易出錯(cuò)。比如,月份依舊是從0開(kāi)始計(jì)算(不過(guò),至少Calendar類(lèi)拿掉了由1900年開(kāi)始計(jì)算年份這一設(shè)計(jì))。更糟的是,同時(shí)存在Date和Calendar這兩個(gè)類(lèi),也增加了程序員的困惑。到底該使用哪一個(gè)類(lèi)呢?此外,有的特性只在某一個(gè)類(lèi)有提供,比如用于以語(yǔ)言無(wú)關(guān)方式格式化和解析日期或時(shí)間的DateFormat方法就只在Date類(lèi)里有。

DateFormat方法也有它自己的問(wèn)題。比如,它不是線程安全的。這意味著兩個(gè)線程如果嘗試使用同一個(gè)formatter解析日期,你可能會(huì)得到無(wú)法預(yù)期的結(jié)果。

最后,Date和Calendar類(lèi)都是可以變的。能把2014年3月18日修改成4月18日意味著什么呢?這種設(shè)計(jì)會(huì)將你拖入維護(hù)的噩夢(mèng),接下來(lái)的一章,我們會(huì)討論函數(shù)式編程,你在該章中會(huì)了解到更多的細(xì)節(jié)。

這一章中,我們會(huì)一起探索新的日期和時(shí)間API所提供的新特性。我們從最基本的用例入手,比如創(chuàng)建同時(shí)適合人與機(jī)器的日期和時(shí)間,逐漸轉(zhuǎn)入到日期和時(shí)間API更高級(jí)的一些應(yīng)用,比如操縱、解析、打印輸出日期?時(shí)間對(duì)象,使用不同的時(shí)區(qū)和年歷。

LocalDate、LocalTime、Instant、Duration 以及Period

讓我們從探索如何創(chuàng)建簡(jiǎn)單的日期和時(shí)間間隔入手。java.time包中提供了很多新的類(lèi)可以幫你解決問(wèn)題,它們是LocalDate、LocalTime、Instant、Duration和Period。

使用LocalDate 和LocalTime

開(kāi)始使用新的日期和時(shí)間API時(shí),你最先碰到的可能是LocalDate類(lèi)。該類(lèi)的實(shí)例是一個(gè)不可變對(duì)象,它只提供了簡(jiǎn)單的日期,并不含當(dāng)天的時(shí)間信息。另外,它也不附帶任何與時(shí)區(qū)相關(guān)的信息。

你可以通過(guò)靜態(tài)工廠方法of創(chuàng)建一個(gè)LocalDate實(shí)例。LocalDate實(shí)例提供了多種方法來(lái)讀取常用的值,比如年份、月份、星期幾等,如下所示。

LocalDate localDate = LocalDate.of(2014, 3, 18);
int year = localDate.getYear();
Month month = localDate.getMonth();
int day = localDate.getDayOfMonth();
DayOfWeek dow = localDate.getDayOfWeek();
int len = localDate.lengthOfMonth();
boolean leap = localDate.isLeapYear();

System.out.println(String.format("year:%s
month:%s
day:%s
dow:%s
len:%s
leap:%s", year, month, day, dow, len, leap));

打印結(jié)果:

year:2014
month:MARCH
day:18
dow:TUESDAY
len:31
leap:false

你還可以使用工廠方法從系統(tǒng)時(shí)鐘中獲取當(dāng)前的日期:

LocalDate today = LocalDate.now();

接下來(lái)剩余的部分會(huì)探討所有日期-時(shí)間類(lèi),這些類(lèi)都提供了類(lèi)似的工廠方法。你還可以通過(guò)傳遞一個(gè)TemporalField參數(shù)給get方法拿到同樣的信息。TemporalField是一個(gè)接口,它定義了如何訪問(wèn)temporal對(duì)象某個(gè)字段的值。ChronoField枚舉實(shí)現(xiàn)了這一接口,所以你可以很方便地使用get方法得到枚舉元素的值,如下所示。

int year = localDate.get(ChronoField.YEAR);
int month = localDate.get(ChronoField.MONTH_OF_YEAR);
int day = localDate.get(ChronoField.DAY_OF_MONTH);

類(lèi)似地,一天中的時(shí)間,比如13:45:20,可以使用LocalTime類(lèi)表示。你可以使用of重載的兩個(gè)工廠方法創(chuàng)建LocalTime的實(shí)例。第一個(gè)重載函數(shù)接收小時(shí)和分鐘,第二個(gè)重載函數(shù)同時(shí)還接收秒。同LocalDate一樣,LocalTime類(lèi)也提供了一些getter方法訪問(wèn)這些變量的值,如下所示。

LocalTime localTime = LocalTime.of(13, 45, 20);
int hour = localTime.getHour();
int minute = localTime.getMinute();
int second = localTime.getSecond();

System.out.println(String.format("hour:%s
minute:%s
second:%s", hour, minute, second));

打印結(jié)果:

hour:13
minute:45
second:20

LocalDate和LocalTime都可以通過(guò)解析代表它們的字符串創(chuàng)建。使用靜態(tài)方法parse,你可以實(shí)現(xiàn)這一目的:

LocalDate date = LocalDate.parse("2018-11-17");
LocalTime time = LocalTime.parse("21:27:58");

你可以向parse方法傳遞一個(gè)DateTimeFormatter。該類(lèi)的實(shí)例定義了如何格式化一個(gè)日期或者時(shí)間對(duì)象。正如我們之前所介紹的,它是替換老版java.util.DateFormat的推薦替代品。這個(gè)我們后面將會(huì)討論到。同時(shí),也請(qǐng)注意,一旦傳遞的字符串參數(shù)無(wú)法被解析為合法的LocalDate或LocalTime對(duì)象,這兩個(gè)parse方法都會(huì)拋出一個(gè)繼承自RuntimeException的DateTimeParseException異常。

合并日期和時(shí)間

這個(gè)復(fù)合類(lèi)名叫LocalDateTime,是LocalDate和LocalTime的合體。它同時(shí)表示了日期和時(shí)間,但不帶有時(shí)區(qū)信息,你可以直接創(chuàng)建,也可以通過(guò)合并日期和時(shí)間對(duì)象構(gòu)造,如下所示。

// 2018-11-17T21:31:50
LocalTime time = LocalTime.of(21, 31, 50);
LocalDate date = LocalDate.of(2018, 11, 17);

LocalDateTime dt1 = LocalDateTime.of(2018, Month.NOVEMBER, 17, 21, 31, 50);
LocalDateTime dt2 = LocalDateTime.of(date, time);
LocalDateTime dt3 = date.atTime(21, 11, 17);
LocalDateTime dt4 = date.atTime(time);
LocalDateTime dt5 = time.atDate(date);

注意,通過(guò)它們各自的atTime或者atDate方法,向LocalDate傳遞一個(gè)時(shí)間對(duì)象,或者向LocalTime傳遞一個(gè)日期對(duì)象的方式,你可以創(chuàng)建一個(gè)LocalDateTime對(duì)象。你也可以使用toLocalDate或者toLocalTime方法,從LocalDateTime中提取LocalDate或者LocalTime組件:

LocalDate date1 = dt1.toLocalDate();
LocalTime time1 = dt1.toLocalTime();
機(jī)器的日期和時(shí)間格式

作為人,我們習(xí)慣于以星期幾、幾號(hào)、幾點(diǎn)、幾分這樣的方式理解日期和時(shí)間。毫無(wú)疑問(wèn),這種方式對(duì)于計(jì)算機(jī)而言并不容易理解。從計(jì)算機(jī)的角度來(lái)看,建模時(shí)間最自然的格式是表示一個(gè)持續(xù)時(shí)間段上某個(gè)點(diǎn)的單一大整型數(shù)。這也是新的java.time.Instant類(lèi)對(duì)時(shí)間建模的方式,基本上它是以Unix元年時(shí)間(傳統(tǒng)的設(shè)定為UTC時(shí)區(qū)1970年1月1日午夜時(shí)分)開(kāi)始所經(jīng)歷的秒數(shù)進(jìn)行計(jì)算。

你可以通過(guò)向靜態(tài)工廠方法ofEpochSecond傳遞一個(gè)代表秒數(shù)的值創(chuàng)建一個(gè)該類(lèi)的實(shí)例。靜態(tài)工廠方法ofEpochSecond還有一個(gè)增強(qiáng)的重載版本,它接收第二個(gè)以納秒為單位的參數(shù)值,對(duì)傳入作為秒數(shù)的參數(shù)進(jìn)行調(diào)整。重載的版本會(huì)調(diào)整納秒?yún)?shù),確保保存的納秒分片在0到999 999999之間。這意味著下面這些對(duì)ofEpochSecond工廠方法的調(diào)用會(huì)返回幾乎同樣的Instant對(duì)象:

Instant.ofEpochSecond(3);
Instant.ofEpochSecond(3, 0);
// 2 秒之后再加上100萬(wàn)納秒(1秒)
Instant.ofEpochSecond(2, 1_000_000_000);
// 4秒之前的100萬(wàn)納秒(1秒)
Instant.ofEpochSecond(4, -1_000_000_000);

正如你已經(jīng)在LocalDate及其他為便于閱讀而設(shè)計(jì)的日期-時(shí)間類(lèi)中所看到的那樣,Instant類(lèi)也支持靜態(tài)工廠方法now,它能夠幫你獲取當(dāng)前時(shí)刻的時(shí)間戳。我們想要特別強(qiáng)調(diào)一點(diǎn),Instant的設(shè)計(jì)初衷是為了便于機(jī)器使用。它包含的是由秒及納秒所構(gòu)成的數(shù)字。所以,它無(wú)法處理那些我們非常容易理解的時(shí)間單位。比如下面這段語(yǔ)句:

int day = Instant.now().get(ChronoField.DAY_OF_MONTH);

它會(huì)拋出下面這樣的異常:

Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: DayOfMonth

但是你可以通過(guò)Duration和Period類(lèi)使用Instant,接下來(lái)我們會(huì)對(duì)這部分內(nèi)容進(jìn)行介紹。

定義Duration 或Period

目前為止,你看到的所有類(lèi)都實(shí)現(xiàn)了Temporal接口,Temporal接口定義了如何讀取和操縱為時(shí)間建模的對(duì)象的值。之前的介紹中,我們已經(jīng)了解了創(chuàng)建Temporal實(shí)例的幾種方法。很自然地你會(huì)想到,我們需要?jiǎng)?chuàng)建兩個(gè)Temporal對(duì)象之間的duration。Duration類(lèi)的靜態(tài)工廠方法between就是為這個(gè)目的而設(shè)計(jì)的。你可以創(chuàng)建兩個(gè)LocalTimes對(duì)象、兩個(gè)LocalDateTimes對(duì)象,或者兩個(gè)Instant對(duì)象之間的duration,如下所示:

LocalTime time1 = LocalTime.of(21, 50, 10);
LocalTime time2 = LocalTime.of(22, 50, 10);
LocalDateTime dateTime1 = LocalDateTime.of(2018, 11, 17, 21, 50, 10);
LocalDateTime dateTime2 = LocalDateTime.of(2018, 11, 17, 23, 50, 10);
Instant instant1 = Instant.ofEpochSecond(1000 * 60 * 2);
Instant instant2 = Instant.ofEpochSecond(1000 * 60 * 3);

Duration d1 = Duration.between(time1, time2);
Duration d2 = Duration.between(dateTime1, dateTime2);
Duration d3 = Duration.between(instant1, instant2);
// PT1H 相差1小時(shí)
System.out.println("d1:" + d1);
// PT2H 相差2小時(shí)
System.out.println("d2:" + d2);
// PT16H40M 相差16小時(shí)40分鐘
System.out.println("d3:" + d3);

由于LocalDateTime和Instant是為不同的目的而設(shè)計(jì)的,一個(gè)是為了便于人閱讀使用,另一個(gè)是為了便于機(jī)器處理,所以你不能將二者混用。如果你試圖在這兩類(lèi)對(duì)象之間創(chuàng)建duration,會(huì)觸發(fā)一個(gè)DateTimeException異常。此外,由于Duration類(lèi)主要用于以秒和納秒衡量時(shí)間的長(zhǎng)短,你不能僅向between方法傳遞一個(gè)LocalDate對(duì)象做參數(shù)。

如果你需要以年、月或者日的方式對(duì)多個(gè)時(shí)間單位建模,可以使用Period類(lèi)。使用該類(lèi)的工廠方法between,你可以使用得到兩個(gè)LocalDate之間的時(shí)長(zhǎng),如下所示:

Period period = Period.between(LocalDate.of(2018, 11, 7), LocalDate.of(2018, 11, 17));
// P10D 相差10天
System.out.println("Period between:" + period);

最后,Duration和Period類(lèi)都提供了很多非常方便的工廠類(lèi),直接創(chuàng)建對(duì)應(yīng)的實(shí)例;換句話說(shuō),就像下面這段代碼那樣,不再是只能以?xún)蓚€(gè)temporal對(duì)象的差值的方式來(lái)定義它們的對(duì)象。

Duration threeMinutes = Duration.ofMinutes(3);
Duration fourMinutes = Duration.of(4, ChronoUnit.MINUTES);

Period tenDay = Period.ofDays(10);
Period threeWeeks = Period.ofWeeks(3);
Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);

Duration類(lèi)和Period類(lèi)共享了很多相似的方法,有興趣的可以參考官網(wǎng)的文檔。

截至目前,我們介紹的這些日期?時(shí)間對(duì)象都是不可修改的,這是為了更好地支持函數(shù)式編程,確保線程安全,保持領(lǐng)域模式一致性而做出的重大設(shè)計(jì)決定。當(dāng)然,新的日期和時(shí)間API也提供了一些便利的方法來(lái)創(chuàng)建這些對(duì)象的可變版本。比如,你可能希望在已有的LocalDate實(shí)例上增加3天。除此之外,我們還會(huì)介紹如何依據(jù)指定的模式,比如dd/MM/yyyy,創(chuàng)建日期-時(shí)間格式器,以及如何使用這種格式器解析和輸出日期。

操縱、解析和格式化日期

如果你已經(jīng)有一個(gè)LocalDate對(duì)象,想要?jiǎng)?chuàng)建它的一個(gè)修改版,最直接也最簡(jiǎn)單的方法是使用withAttribute方法。withAttribute方法會(huì)創(chuàng)建對(duì)象的一個(gè)副本,并按照需要修改它的屬性。注意,下面的這段代碼中所有的方法都返回一個(gè)修改了屬性的對(duì)象。它們都不會(huì)修改原來(lái)的對(duì)象!

// 2018-11-17
LocalDate date1 = LocalDate.of(2018, 11, 17);
// 2019-11-17
LocalDate date2 = date1.withYear(2019);
// 2019-11-25
LocalDate date3 = date2.withDayOfMonth(25);
// 2019-09-25
LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 9);

它們都聲明于Temporal接口,所有的日期和時(shí)間API類(lèi)都實(shí)現(xiàn)這兩個(gè)方法,它們定義了單點(diǎn)的時(shí)間,比如LocalDate、LocalTime、LocalDateTime以及Instant。更確切地說(shuō),使用get和with方法,我們可以將Temporal對(duì)象值的讀取和修改區(qū)分開(kāi)。如果Temporal對(duì)象不支持請(qǐng)求訪問(wèn)的字段,它會(huì)拋出一個(gè)UnsupportedTemporalTypeException異常,比如試圖訪問(wèn)Instant對(duì)象的ChronoField.MONTH_OF_YEAR字段,或者LocalDate對(duì)象的ChronoField.NANO_OF_SECOND字段時(shí)都會(huì)拋出這樣的異常。

它甚至能以聲明的方式操縱LocalDate對(duì)象。比如,你可以像下面這段代碼那樣加上或者減去一段時(shí)間。

// 2018-11-17
LocalDate date1 = LocalDate.of(2018, 11, 17);
// 2018-11-24
LocalDate date2 = date1.plusWeeks(1);
// 2015-11-24
LocalDate date3 = date2.minusYears(3);
// 2016-05-24
LocalDate date4 = date3.plus(6, ChronoUnit.MONTHS);

與我們剛才介紹的get和with方法類(lèi)似最后一行使用的plus方法也是通用方法,它和minus方法都聲明于Temporal接口中。通過(guò)這些方法,對(duì)TemporalUnit對(duì)象加上或者減去一個(gè)數(shù)字,我們能非常方便地將Temporal對(duì)象前溯或者回滾至某個(gè)時(shí)間段,通過(guò)ChronoUnit枚舉我們可以非常方便地實(shí)現(xiàn)TemporalUnit接口。

大概你已經(jīng)猜到,像LocalDate、LocalTime、LocalDateTime以及Instant這樣表示時(shí)
間點(diǎn)的日期?時(shí)間類(lèi)提供了大量通用的方法,我們目前所使用的只有一小部分,有興趣的可以去看官網(wǎng)文檔。

使用TemporalAdjuster

截至目前,你所看到的所有日期操作都是相對(duì)比較直接的。有的時(shí)候,你需要進(jìn)行一些更加復(fù)雜的操作,比如,將日期調(diào)整到下個(gè)周日、下個(gè)工作日,或者是本月的最后一天。這時(shí),你可以使用重載版本的with方法,向其傳遞一個(gè)提供了更多定制化選擇的TemporalAdjuster對(duì)象,更加靈活地處理日期。對(duì)于最常見(jiàn)的用例, 日期和時(shí)間API已經(jīng)提供了大量預(yù)定義的TemporalAdjuster。你可以通過(guò)TemporalAdjuster類(lèi)的靜態(tài)工廠方法訪問(wèn)它們,如下所示。

// 2018-11-17
LocalDate date1 = LocalDate.of(2018, 11, 17);
// 2018-11-19
LocalDate date2 = date1.with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY));
// 2018-11-30
LocalDate date3 = date2.with(TemporalAdjusters.lastDayOfMonth());

正如我們看到的,使用TemporalAdjuster我們可以進(jìn)行更加復(fù)雜的日期操作,而且這些方法的名稱(chēng)也非常直觀,方法名基本就是問(wèn)題陳述。此外,即使你沒(méi)有找到符合你要求的預(yù)定義的TemporalAdjuster,創(chuàng)建你自己的TemporalAdjuster也并非難事。實(shí)際上,TemporalAdjuster接口只聲明了單一的一個(gè)方法(這使得它成為了一個(gè)函數(shù)式接口),定義如下。

@FunctionalInterface
public interface TemporalAdjuster {
    Temporal adjustInto(Temporal temporal);
}

這意味著TemporalAdjuster接口的實(shí)現(xiàn)需要定義如何將一個(gè)Temporal對(duì)象轉(zhuǎn)換為另一個(gè)Temporal對(duì)象。你可以把它看成一個(gè)UnaryOperator

你可能希望對(duì)你的日期時(shí)間對(duì)象進(jìn)行的另外一個(gè)通用操作是,依據(jù)你的業(yè)務(wù)領(lǐng)域以不同的格式打印輸出這些日期和時(shí)間對(duì)象。類(lèi)似地,你可能也需要將那些格式的字符串轉(zhuǎn)換為實(shí)際的日期對(duì)象。接下來(lái)的一節(jié),我們會(huì)演示新的日期和時(shí)間API提供那些機(jī)制是如何完成這些任務(wù)的。

打印輸出及解析日期-時(shí)間對(duì)象

處理日期和時(shí)間對(duì)象時(shí),格式化以及解析日期?時(shí)間對(duì)象是另一個(gè)非常重要的功能。新的java.time.format包就是特別為這個(gè)目的而設(shè)計(jì)的。這個(gè)包中,最重要的類(lèi)是DateTimeFormatter。創(chuàng)建格式器最簡(jiǎn)單的方法是通過(guò)它的靜態(tài)工廠方法以及常量。像BASIC_ISO_DATE和ISO_LOCAL_DATE 這樣的常量是DateTimeFormatter 類(lèi)的預(yù)定義實(shí)例。所有的DateTimeFormatter實(shí)例都能用于以一定的格式創(chuàng)建代表特定日期或時(shí)間的字符串。比如,下面的這個(gè)例子中,我們使用了兩個(gè)不同的格式器生成了字符串:

LocalDate date1 = LocalDate.of(2018, 11, 17);
// 20181117
String s1 = date1.format(DateTimeFormatter.BASIC_ISO_DATE);
// 2018-11-17
String s2 = date1.format(DateTimeFormatter.ISO_LOCAL_DATE);

你也可以通過(guò)解析代表日期或時(shí)間的字符串重新創(chuàng)建該日期對(duì)象。所有的日期和時(shí)間API都提供了表示時(shí)間點(diǎn)或者時(shí)間段的工廠方法,你可以使用工廠方法parse達(dá)到重創(chuàng)該日期對(duì)象的目的:

LocalDate date2 = LocalDate.parse("20181117", DateTimeFormatter.BASIC_ISO_DATE);
LocalDate date3 = LocalDate.parse("2018-11-17", DateTimeFormatter.ISO_LOCAL_DATE);

和老的java.util.DateFormat相比較,所有的DateTimeFormatter實(shí)例都是線程安全的。所以,你能夠以單例模式創(chuàng)建格式器實(shí)例,就像DateTimeFormatter所定義的那些常量,并能在多個(gè)線程間共享這些實(shí)例。DateTimeFormatter類(lèi)還支持一個(gè)靜態(tài)工廠方法,它可以按照某個(gè)特定的模式創(chuàng)建格式器,代碼清單如下。

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
// 17/11/2018
String formattedDate = date1.format(formatter);
LocalDate date4 = LocalDate.parse(formattedDate, formatter);

這段代碼中,LocalDate的formate方法使用指定的模式生成了一個(gè)代表該日期的字符串。緊接著,靜態(tài)的parse方法使用同樣的格式器解析了剛才生成的字符串,并重建了該日期對(duì)象。ofPattern方法也提供了一個(gè)重載的版本,使用它你可以創(chuàng)建某個(gè)Locale的格式器,代碼清單如下所示。

 DateTimeFormatter italianFormatter = DateTimeFormatter.ofPattern("d. MMMM yyyy", Locale.ITALIAN);
LocalDate date5 = LocalDate.of(2018, 11, 16);
// 16. novembre 2018
String formattedDate2 = date5.format(italianFormatter);
// 2018-11-16
LocalDate date6 = LocalDate.parse(formattedDate2, italianFormatter);

最后,如果你還需要更加細(xì)粒度的控制,DateTimeFormatterBuilder類(lèi)還提供了更復(fù)雜的格式器,你可以選擇恰當(dāng)?shù)姆椒ǎ徊揭徊降貥?gòu)造自己的格式器。另外,它還提供了非常強(qiáng)大的解析功能,比如區(qū)分大小寫(xiě)的解析、柔性解析(允許解析器使用啟發(fā)式的機(jī)制去解析輸入,不精確地匹配指定的模式)、填充, 以及在格式器中指定可選節(jié)。

比如, 你可以通過(guò)DateTimeFormatterBuilder自己編程實(shí)現(xiàn)我們?cè)谏厦娲a中使用的italianFormatter,代碼清單如下。

DateTimeFormatter italianFormatter = new DateTimeFormatterBuilder()
                .appendText(ChronoField.DAY_OF_MONTH)
                .appendLiteral(". ")
                .appendText(ChronoField.MONTH_OF_YEAR)
                .appendLiteral(" ")
                .appendText(ChronoField.YEAR)
                .parseCaseInsensitive()
                .toFormatter(Locale.ITALIAN);

LocalDate now = LocalDate.now();
// 17. novembre 2018
String s1 = now.format(italianFormatter);

目前為止,你已經(jīng)學(xué)習(xí)了如何創(chuàng)建、操縱、格式化以及解析時(shí)間點(diǎn)和時(shí)間段,但是你還不了解如何處理日期和時(shí)間之間的微妙關(guān)系。比如,你可能需要處理不同的時(shí)區(qū),或者由于不同的歷法系統(tǒng)帶來(lái)的差異。接下來(lái)的一節(jié),我們會(huì)探究如何使用新的日期和時(shí)間API解決這些問(wèn)題。

處理不同的時(shí)區(qū)和歷法

之前你看到的日期和時(shí)間的種類(lèi)都不包含時(shí)區(qū)信息。時(shí)區(qū)的處理是新版日期和時(shí)間API新增加的重要功能,使用新版日期和時(shí)間API時(shí)區(qū)的處理被極大地簡(jiǎn)化了。新的java.time.ZoneId類(lèi)是老版java.util.TimeZone的替代品。它的設(shè)計(jì)目標(biāo)就是要讓你無(wú)需為時(shí)區(qū)處理的復(fù)雜和繁瑣而操心,比如處理日光時(shí)(Daylight Saving Time,DST)這種問(wèn)題。跟其他日期和時(shí)間類(lèi)一樣,ZoneId類(lèi)也是無(wú)法修改的。

時(shí)區(qū)是按照一定的規(guī)則將區(qū)域劃分成的標(biāo)準(zhǔn)時(shí)間相同的區(qū)間。在ZoneRules這個(gè)類(lèi)中包含了40個(gè)這樣的實(shí)例。你可以簡(jiǎn)單地通過(guò)調(diào)用ZoneId的getRules()得到指定時(shí)區(qū)的規(guī)則。每個(gè)特定的ZoneId對(duì)象都由一個(gè)地區(qū)ID標(biāo)識(shí),比如:

ZoneId shanghaiZone = ZoneId.of("Asia/Shanghai");

地區(qū)ID都為“{區(qū)域}/{城市}”的格式,這些地區(qū)集合的設(shè)定都由英特網(wǎng)編號(hào)分配機(jī)構(gòu)(IANA)的時(shí)區(qū)數(shù)據(jù)庫(kù)提供。你可以通過(guò)Java 8的新方法toZoneId將一個(gè)老的時(shí)區(qū)對(duì)象轉(zhuǎn)換為ZoneId:

ZoneId zoneId = TimeZone.getDefault().toZoneId();

一旦得到一個(gè)ZoneId對(duì)象,你就可以將它與LocalDate、LocalDateTime或者是Instant對(duì)象整合起來(lái),構(gòu)造為一個(gè)ZonedDateTime實(shí)例,它代表了相對(duì)于指定時(shí)區(qū)的時(shí)間點(diǎn),代碼清單如下所示。

LocalDate date = LocalDate.of(2018, 11, 17);
ZonedDateTime zdt1 = date.atStartOfDay(shanghaiZone);

LocalDateTime dateTime = LocalDateTime.of(2018, 11, 27, 18, 13, 15);
ZonedDateTime zdt2 = dateTime.atZone(shanghaiZone);

Instant instant = Instant.now();
ZonedDateTime zdt3 = instant.atZone(shanghaiZone);

通過(guò)ZoneId,你還可以將LocalDateTime轉(zhuǎn)換為Instant:

LocalDateTime dateTime = LocalDateTime.of(2018, 11, 17, 18, 45);
Instant instantFromDateTime = dateTime.toInstant(shanghaiZone);

你也可以通過(guò)反向的方式得到LocalDateTime對(duì)象:

Instant instant = Instant.now();
LocalDateTime timeFromInstant = LocalDateTime.ofInstant(instant, shanghaiZone);
利用和UTC/格林尼治時(shí)間的固定偏差計(jì)算時(shí)區(qū)

另一種比較通用的表達(dá)時(shí)區(qū)的方式是利用當(dāng)前時(shí)區(qū)和UTC/格林尼治的固定偏差。比如,基于這個(gè)理論,你可以說(shuō)“紐約落后于倫敦5小時(shí)”。這種情況下,你可以使用ZoneOffset類(lèi),它是ZoneId的一個(gè)子類(lèi),表示的是當(dāng)前時(shí)間和倫敦格林尼治子午線時(shí)間的差異:

ZoneOffset newYorkOffset = ZoneOffset.of("-05:00");

“-05:00”的偏差實(shí)際上對(duì)應(yīng)的是美國(guó)東部標(biāo)準(zhǔn)時(shí)間。注意,使用這種方式定義的ZoneOffset并未考慮任何日光時(shí)的影響,所以在大多數(shù)情況下,不推薦使用。由于ZoneOffset也是ZoneId,所以你可以像上面的代碼那樣使用它。你甚至還可以創(chuàng)建這樣的OffsetDateTime,它使用ISO-8601的歷法系統(tǒng),以相對(duì)于UTC/格林尼治時(shí)間的偏差方式表示日期時(shí)間。

LocalDateTime dateTime = LocalDateTime.of(2018, 11, 17, 18, 45);
OffsetDateTime offsetDateTime = OffsetDateTime.of(dateTime, newYorkOffset);
總結(jié)

Java 8之前老版的java.util.Date類(lèi)以及其他用于建模日期時(shí)間的類(lèi)有很多不一致及設(shè)計(jì)上的缺陷,包括易變性以及糟糕的偏移值、默認(rèn)值和命名。

新版的日期和時(shí)間API中,日期-時(shí)間對(duì)象是不可變的。

新的API提供了兩種不同的時(shí)間表示方式,有效地區(qū)分了運(yùn)行時(shí)人和機(jī)器的不同需求。

你可以用絕對(duì)或者相對(duì)的方式操縱日期和時(shí)間,操作的結(jié)果總是返回一個(gè)新的實(shí)例,老的日期時(shí)間對(duì)象不會(huì)發(fā)生變化。

TemporalAdjuster讓你能夠用更精細(xì)的方式操縱日期,不再局限于一次只能改變它的一個(gè)值,并且你還可按照需求定義自己的日期轉(zhuǎn)換器。

你現(xiàn)在可以按照特定的格式需求,定義自己的格式器,打印輸出或者解析日期?時(shí)間對(duì)象。這些格式器可以通過(guò)模板創(chuàng)建,也可以自己編程創(chuàng)建,并且它們都是線程安全的。

你可以用相對(duì)于某個(gè)地區(qū)/位置的方式,或者以與UTC/格林尼治時(shí)間的絕對(duì)偏差的方式表示時(shí)區(qū),并將其應(yīng)用到日期?時(shí)間對(duì)象上,對(duì)其進(jìn)行本地化。

可以說(shuō)《Java8實(shí)戰(zhàn)》的讀書(shū)筆記相關(guān)的已經(jīng)寫(xiě)完了,這本書(shū)后面還有最后一部分超越Java8,這一部分相關(guān)的章節(jié)都是跟函數(shù)式編程的思考與技巧相關(guān),以及Java以后的未來(lái)等等。《Java8實(shí)戰(zhàn)》這本書(shū)真的寫(xiě)的太好了而且這本書(shū)完全可以當(dāng)作一本關(guān)于Java8使用的工具書(shū),隨時(shí)可以翻開(kāi)看看,看看關(guān)于Java8的特性是如何使用,該如何去避免一些坑,該如何使用Stream和Lambda表達(dá)式去簡(jiǎn)化你的代碼。

代碼

Gitee:chap12

Github:chap12

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/72235.html

相關(guān)文章

  • 《java 8 實(shí)戰(zhàn)》讀書(shū)筆記 -第十二章 新的日期時(shí)間 API

    摘要:類(lèi)似地,一天中的時(shí)間,比如,可以使用類(lèi)表示。合并日期和時(shí)間這個(gè)復(fù)合類(lèi)名叫,是和的合體。對(duì)于最常見(jiàn)的用例,日期和時(shí)間已經(jīng)提供了大量預(yù)定義的。你甚至還可以創(chuàng)建這樣的,它使用的歷法系統(tǒng),以相對(duì)于格林尼治時(shí)間的偏差方式表示日期時(shí)間。 一、LocalDate、LocalTime、Instant、Duration 以及 Period 1.使用 LocalDate 和 LocalTime 創(chuàng)建一個(gè)L...

    darry 評(píng)論0 收藏0
  • <javascript高級(jí)程序設(shè)計(jì)>第十二章讀書(shū)筆記----偏移量

    摘要:包括元素的高度上下內(nèi)邊距上下邊框值,如果元素的的值為那么該值為。該值為元素的包含元素。最后,所有這些偏移量都是只讀的,而且每次訪問(wèn)他們都需要重新計(jì)算。為了避免重復(fù)計(jì)算,可以將計(jì)算的值保存起來(lái),以提高性能。 offsetHeight 包括元素的高度、上下內(nèi)邊距、上下邊框值,如果元素的style.display的值為none,那么該值為0。offsetWidth 包括元素的寬度、左...

    dayday_up 評(píng)論0 收藏0
  • 【譯】 WebSocket 協(xié)議第十二章——使用其他規(guī)范中的WebSocket協(xié)議

    摘要:概述本文為協(xié)議的第十二章,本文翻譯的主要內(nèi)容為如何使用其他規(guī)范中的協(xié)議。使用其他規(guī)范中的協(xié)議協(xié)議正文協(xié)議旨在由另一規(guī)范使用,以提供動(dòng)態(tài)作者定義內(nèi)容的通用機(jī)制。當(dāng)連接打開(kāi)時(shí),文檔需要處理收到一條消息第節(jié)的場(chǎng)景。 概述 本文為 WebSocket 協(xié)議的第十二章,本文翻譯的主要內(nèi)容為如何使用其他規(guī)范中的 WebSocket 協(xié)議。 使用其他規(guī)范中的WebSocket協(xié)議(協(xié)議正文) Web...

    KoreyLee 評(píng)論0 收藏0
  • 《深入理解ES6》筆記——導(dǎo)讀

    摘要:最近買(mǎi)了深入理解的書(shū)籍來(lái)看,為什么學(xué)習(xí)這么久還要買(mǎi)這本書(shū)呢主要是看到核心團(tuán)隊(duì)成員及的創(chuàng)造者為本書(shū)做了序,作為一個(gè)粉絲,還是挺看好這本書(shū)能給我?guī)?lái)一個(gè)新的升華,而且本書(shū)的作者也非常厲害。 使用ES6開(kāi)發(fā)已經(jīng)有1年多了,以前看的是阮一峰老師的ES6教程,也看過(guò)MDN文檔的ES6語(yǔ)法介紹。 最近買(mǎi)了《深入理解ES6》的書(shū)籍來(lái)看,為什么學(xué)習(xí)ES6這么久還要買(mǎi)這本書(shū)呢?主要是看到Daniel A...

    Godtoy 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<