Java8已經出來好久了,然後我們平時工作中也遇到了好多的關于時間轉換的問題,基本上就是需要的時間看一看源碼,然後拿來直接用,其實真正了解的并不多。今天又遇到了關于String轉換的問題,就決定寫一篇文章出來。
那麼在寫具體的LocalDate前,我們先來看下為什麼要在Java8中搞一套新的API呢,因為舊的Date類非常的難用,比如,其中的幾個構造方法都被标注為@Deprecated,這裡我總結了一些Date的一些問題
- Date這個類既可以描述年月日,也可以描述時分秒,雖然萬花筒用起來是挺好的,但是它既可以表示時間戳還可以表示日期,直覺看來是不明确的。
- 而且作為一個日期類,它是可變的。是以作為傳回對象時,傳回的都應該是它的clone,而不是對象本身,否則的話可能會改變它的結構 。既然它是可變的,也就不是線程安全的,這是Date類面臨的很大的問題之一。
- 這裡請注意,Java8的LocalDate是線程安全的是因為它沒有提供set方法,也就意味着一旦建立就不能修改值。而Date方法則提供了set方法

- 在它的内部API中,getMonth傳回的是0-11代表的月份,而getYear傳回的是基于1900年的,即2018年為118年
下面面是摘自源碼的注釋
//The value returned is between <code>0</code> and <code>11</code>,
with the value <code>0</code> representing January.
上面的注釋已經指出了,一月是0,而且這個getMonth()方法明确了是@Deprecated。
/**
* Returns a number representing the month that contains or begins
* with the instant in time represented by this <tt>Date</tt> object.
* The value returned is between <code>0</code> and <code>11</code>,
* with the value <code>0</code> representing January.
*
* @return the month represented by this date.
* @see java.util.Calendar
* @deprecated As of JDK version 1.1,
* replaced by <code>Calendar.get(Calendar.MONTH)</code>.
*/
@Deprecated
public int getMonth() {
return normalize().getMonth() - 1; // adjust 1-based to 0-based
}
複制
- Date類為了相容SQL,有一個java.sql.Date(這個Date僅包含日期),這就給我們日常的使用帶來了很多迷惑。下圖是sql.Date的方法
- 還有一個就是閏秒的問題,閏秒通常會在一個小時内用ntp更新一個好的系統時鐘。在引入兩個閏秒(至少每六個月一次,實際上每幾年一次)的情況下,系統仍在運作的可能性非常小,特别是考慮到您必須不時地重新部署新版本的代碼。即使使用動态語言來重新生成類或類似于WAR引擎的東西,也會污染類空間并最終耗盡permgen(這裡摘自網絡)
而且在我們經常和Date搭配使用的SimpleDateFormat中,parse()中,其中解析的時候,使用了CalendarBuilder calb = new CalendarBuilder();,然後在設定值的時候,是先用
CalendarBuilder的 establish(),establish方法的内容:
//這裡是先清空
cal.clear();
...
//然後再設定新的值
cal.setWeekDate(field[MAX_FIELD + WEEK_YEAR], weekOfYear, dayOfWeek);
複制
如果在多個線程中,如果一個線程已經進行了clear(),而另一個線程期望這個值進行讀取,可以想象造成的後果,是以如果在多線程中,要麼不使用它,要麼就要使它是安全的,是以可以:
1. 進行synchronization,雖然這是一個不好的做法
2. 在每個線程中進行單獨的執行個體化,這将造成記憶體上的消耗,但是這是一個笨辦法
3. 最後就是使用ThreadLocal,這是3個方法中最快的(3點建議摘自stackoverflow)
上邊說了Date的一些問題,然後我們來說下Java8新增的日期API --- Date Time API
首先讓我們來看下包結構。
我們可以看到常用的LocalDate, LocalDateTime, LocalTime.Instant類,這些類都是不可變,并且是線程安全的,沒有提供set方法。
- chrono包,這是一個月曆相關的包,A calendar system, used to organize and identify dates 代碼注釋已經說明了
而且這個月曆包是包括ISO月曆和非ISO月曆的(也就是公曆和非公曆)
ISO公曆:國際标準ISO 8601,是國際标準化組織的日期和時間的表示方法,全稱為《資料存儲和交換形式·資訊交換·日期和時間的表示方法》(https://zh.wikipedia.org/wiki/ISO_8601)
比如年由4位數字組成YYYY,或者帶正負号的四或五位數字表示±YYYYY。月、日用兩位數字表示:MM、DD。隻使用數字為基本格式。使用短橫線"-"間隔開年、月、日為擴充格式。
非ISO公曆:泰國佛教月曆,Hijrah月曆,Minguo月曆
其中LocalDate就是我們的公曆,而ThaiBuddhistDate是泰國的佛教月曆
輸出結果是
當然有了不同的月曆就有了轉換,看代碼
輸出是
LocalDate和ThaiBuddhistDate都是Temporal的子類
- format包,這是一個用于格式化和解析的包,不過我們不會經常用它,LocalDate類本身已經提供了相關操作
- temporal包,使用字段和機關以及日期時間調整器通路日期和時間。該軟體包擴充了基礎軟體包,為更強大的用例提供了額外的功能,包括
- 日期時間機關,例如年,月,日和小時
- 日期時間字段,例如月份,星期幾或小時
- 日期時間調整功能
- 周的不同定義
- 比如像Date Time Package圖提到的Month,MonthDay都是Temporal的子類
- 要查找給定日期之後的第一個星期幾,請使用TemporalAdjusters.next(DayOfWeek),例如 date.with(next(MONDAY))
- zone包。支援不同時區和規則的包。
- 接下來我們來看LocalDate
在LocalDate中,有以下常用的方法,
public static LocalDate now() {
return now(Clock.systemDefaultZone());
}
public static LocalDate now(ZoneId var0) {
return now(Clock.system(var0));
}
public static LocalDate now(Clock var0) {
}
public static LocalDate of(int var0, Month var1, int var2) { ...}
public static LocalDate of(int var0, int var1, int var2) {
}
public static LocalDate ofYearDay(int var0, int var1) {
}
public static LocalDate from(TemporalAccessor var0) {
}
public static LocalDate parse(CharSequence var0) {
return parse(var0, DateTimeFormatter.ISO_LOCAL_DATE);
}
public static LocalDate parse(CharSequence var0, DateTimeFormatter var1) {
Objects.requireNonNull(var1, "formatter");
return (LocalDate) var1.parse(var0, LocalDate::from);
}
複制
在上面的代碼中,其中now()方法,還有parse(),of(),是比較常用的方法,
輸出是
可以看出,使用起來還是很友善的
- LocalTime
LocalTime是一個不可變類,其執行個體表示人類可讀格式的時間。它的預設格式是hh:mm:ss.zzz。
輸出是
基本和LocalDate一樣,這裡不做太多叙述
- LocalDateTime
輸出是
- Instant這是一個時間線上的瞬時點時間,可以了解為格林威治時間
我現在的時間是2018年10月9日21點02,輸出是
接下來是java8 時間API的一些基本應用
1.轉Date
2.轉String
now.toString()
3.一般用法
4.String轉LocalDate
也可以自己自定義格式
5.取相關的日期
6.取具體時間
7.時間比較