前言
Java8之前日期/時間API存在的問題
- 非線程安全 − java.util.Date 是非線程安全的,所有的日期類都是可變的,這是Java日期類最大的問題之一。
- 設計很差 − Java的日期/時間類的定義並不一致,在java.util和java.sql的包中都有日期類,此外用於格式化和解析的類在java.text包中定義。java.util.Date同時包含日期和時間,而java.sql.Date僅包含日期,將其納入java.sql包並不合理。另外這兩個類都有相同的名字,這本身就是一個非常糟糕的設計。
- 時區處理麻煩 − 日期類並不提供國際化,沒有時區支持,因此Java引入了java.util.Calendar和java.util.TimeZone類,但他們同樣存在上述所有的問題。
因爲上面這些原因,誕生了第三方庫Joda-Time,可以替代Java的時間管理API。Java 8中新的時間和日期管理API深受Joda-Time影響,並吸收了很多Joda-Time的精華。新的java.time包包含了所有關於日期、時間、時區、Instant(跟日期類似但是精確到納秒)、duration(持續時間)和時鐘操作的類。新設計的API認真考慮了這些類的不變性(從java.util.Calendar吸取的教訓),如果某個實例需要修改,則返回一個新的對象。
Java8新日期/時間API的改變
Java 8日期/時間API是JSR-310的實現,它的實現目標是克服舊的日期時間實現中所有的缺陷,新的日期/時間API的一些設計原則是:
- 不變性:新的日期/時間API中,所有的類都是不可變的,這對多線程環境有好處。
- 關注點分離:新的API將人可讀的日期時間和機器時間(unix timestamp)明確分離,它爲日期(Date)、時間(Time)、日期時間(DateTime)、時間戳(unix timestamp)以及時區定義了不同的類。
- 清晰:在所有的類中,方法都被明確定義用以完成相同的行爲。舉個例子,要拿到當前實例我們可以使用now()方法,在所有的類中都定義了format()和parse()方法,而不是像以前那樣專門有一個獨立的類。爲了更好的處理問題,所有的類都使用了工廠模式和策略模式,一旦你使用了其中某個類的方法,與其他類協同工作並不困難。
- 實用操作:所有新的日期/時間API類都實現了一系列方法用以完成通用的任務,如:加、減、格式化、解析、從日期/時間中提取單獨部分,等等。
- 可擴展性:新的日期/時間API是工作在ISO-8601日曆系統上的,但我們也可以將其應用在非IOS的日曆上。
Java日期/時間API包含以下相應的包:
- java.time包:這是新的Java日期/時間API的基礎包,所有的主要基礎類都是這個包的一部分,如:LocalDate, LocalTime, LocalDateTime, Instant, Period, Duration等等。所有這些類都是不可變的和線程安全的,在絕大多數情況下,這些類能夠有效地處理一些公共的需求。
- java.time.chrono包:這個包爲非ISO的日曆系統定義了一些泛化的API,我們可以擴展AbstractChronology類來創建自己的日曆系統。
- java.time.format包:這個包包含能夠格式化和解析日期時間對象的類,在絕大多數情況下,我們不應該直接使用它們,因爲java.time包中相應的類已經提供了格式化和解析的方法。
- java.time.temporal包:這個包包含一些時態對象,我們可以用其找出關於日期/時間對象的某個特定日期或時間,比如說,可以找到某月的第一天或最後一天。你可以非常容易地認出這些方法,因爲它們都具有“withXXX”的格式。
- java.time.zone包:這個包包含支持不同時區以及相關規則的類。
Java8提供的LocalDate和DateTimeFormat是如何保證線程安全的?
LocalDate類和DateTimeFormatter類都是final類型的,也就是說,它們是不可變的,一旦實例化,值就固定了。而Java8之前的Date類和SimpleDateFormat類都不是final的。
分析一下源碼。
Java8日期/時間API介紹
Java8新增類型
Instant:時間戳
Duration:持續時間,時間差
LocalDate:只包含日期,比如:2016-10-20
LocalTime:只包含時間,比如:23:12:10
LocalDateTime:包含日期和時間,比如:2016-10-20 23:14:21
Period:時間段
ZoneOffset:時區偏移量,比如:+8:00
ZonedDateTime:帶時區的時間
Clock:時鐘,比如獲取目前美國紐約的時間
java.time.format.DateTimeFormatter:時間格式化類
下面介紹下如何使用:
LocalDate 只會獲取年月日
// 創建 LocalDate
// 獲取當前年月日
LocalDate localDate = LocalDate.now();
// 構造指定的年月日
LocalDate localDate1 = LocalDate.of(2019, 9, 12);
// 獲取年、月、日、星期幾
int year = localDate1.getYear();
int year1 = localDate1.get(ChronoField.YEAR);
Month month = localDate1.getMonth();
int month1 = localDate1.get(ChronoField.MONTH_OF_YEAR);
// 月份中的第幾天:12
int day = localDate1.getDayOfMonth();
int day1 = localDate1.get(ChronoField.DAY_OF_MONTH);
// 一週的第幾天:THURSDAY
DayOfWeek dayOfWeek = localDate1.getDayOfWeek();
// 一週的第幾天:4
int dayOfWeek1 = localDate1.get(ChronoField.DAY_OF_WEEK);
// 是否爲閏年:false
boolean leapYear = localDate1.isLeapYear();
// 紐約日期
LocalDate todayNewYork = LocalDate.now(ZoneId.of("America/New_York"));
LocalTime 只會獲取時分秒
// 創建 LocalTime
LocalTime localTime = LocalTime.of(14, 14, 14);
LocalTime localTime1 = LocalTime.now();
// 獲取小時
int hour = localTime.getHour();
int hour1 = localTime.get(ChronoField.HOUR_OF_DAY);
// 獲取分
int minute = localTime.getMinute();
int minute1 = localTime.get(ChronoField.MINUTE_OF_HOUR);
// 獲取秒
int second = localTime.getMinute();
int second1 = localTime.get(ChronoField.SECOND_OF_MINUTE);
// 紐約時間
LocalTime timeNewYork = LocalTime.now(ZoneId.of("America/New_York"));
LocalDateTime 獲取年月日時分秒
相當於 LocalDate + LocalTime
// 創建 LocalDateTime
LocalDateTime localDateTime = LocalDateTime.now();
LocalDateTime localDateTime1 = LocalDateTime.of(2019, Month.SEPTEMBER, 10, 14, 46, 56);
LocalDateTime localDateTime2 = LocalDateTime.of(localDate, localTime);
LocalDateTime localDateTime3 = localDate.atTime(localTime);
LocalDateTime localDateTime4 = localTime.atDate(localDate);
// 獲取LocalDate
LocalDate localDate2 = localDateTime.toLocalDate();
// 獲取LocalTime
LocalTime localTime2 = localDateTime.toLocalTime();
// 紐約日期+時間
LocalDateTime timeNewYork = LocalDateTime.now(ZoneId.of("America/New_York"));
Instant 獲取秒數
用於表示一個時間戳(精確到納秒)
它與我們常使用的System.currentTimeMillis()
有些類似,不過Instant
可以精確到納秒(Nano-Second),System.currentTimeMillis()
方法只精確到毫秒(Milli-Second)。如果查看Instant
源碼,發現它的內部使用了兩個常量,seconds
表示從1970-01-01 00:00:00開始到現在的秒數,nanos
表示納秒部分(nanos
的值不會超過999,999,999
)。Instant
除了使用now()
方法創建外,還可以通過ofEpochSecond
方法創建.
// ofEpochSecond()方法的第一個參數爲秒,第二個參數爲納秒,上面的代碼表示從1970-01-01 00:00:00開始後兩分鐘的10萬納秒的時刻,控制檯上的輸出爲:
// 1970-01-01T00:02:00.000100Z
Instant instant = Instant.ofEpochSecond(120, 100000);
// 創建Instant對象
Instant instant = Instant.now();
// 獲取秒數
long currentSecond = instant.getEpochSecond();
// 獲取毫秒數
long currentMilli = instant.toEpochMilli();
Period/Duration 時間差
- Period - 處理有關基於時間的日期數量。
- Duration - 處理有關基於時間的時間量。
Duration 表示一個時間段
// Duration.between()方法創建 Duration 對象
LocalDateTime from = LocalDateTime.of(2017, Month.JANUARY, 1, 00, 0, 0); // 2017-01-01 00:00:00
LocalDateTime to = LocalDateTime.of(2019, Month.SEPTEMBER, 12, 14, 28, 0); // 2019-09-15 14:28:00
Duration duration = Duration.between(from, to); // 表示從 from 到 to 這段時間
long days = duration.toDays(); // 這段時間的總天數
long hours = duration.toHours(); // 這段時間的小時數
long minutes = duration.toMinutes(); // 這段時間的分鐘數
long seconds = duration.getSeconds(); // 這段時間的秒數
long milliSeconds = duration.toMillis(); // 這段時間的毫秒數
long nanoSeconds = duration.toNanos(); // 這段時間的納秒數
Duration duration1 = Duration.of(5, ChronoUnit.DAYS); // 5天
Duration duration2 = Duration.of(1000, ChronoUnit.MILLIS); // 1000毫秒
Period 表示日期上的時間差
Period
在概念上和Duration
類似,區別在於Period
是以年月日來衡量一個時間段,比如2年3個月6天:
Period period = Period.of(2, 3, 6);
Period
對象也可以通過between()
方法創建,值得注意的是,由於Period
是以年月日衡量時間段,所以between()方法只能接收LocalDate類型的參數:
// 2017-01-05 到 2017-02-05 這段時間
Period period = Period.between(
LocalDate.of(2017, 1, 5),
LocalDate.of(2017, 2, 5));
ChronoUnit 時間枚舉類
LocalDate today = LocalDate.now();
LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS);
Clock
它通過指定一個時區,然後就可以獲取到當前的時刻,日期與時間。Clock可以替換System.currentTimeMillis()與TimeZone.getDefault()。
Clock clock = Clock.systemUTC();
System.out.println(clock.instant() );//2020-05-26T16:54:54.141Z
System.out.println(clock.millis() );//1590512094273
時區
Java 8中的時區操作被很大程度上簡化了,新的時區類java.time.ZoneId
是原有的java.util.TimeZone
類的替代品。ZoneId
對象可以通過ZoneId.of()
方法創建,也可以通過ZoneId.systemDefault()
獲取系統默認時區:
ZoneId shanghaiZoneId = ZoneId.of("Asia/Shanghai");
ZoneId systemZoneId = ZoneId.systemDefault();
of()
方法接收一個“區域/城市”的字符串作爲參數,你可以通過getAvailableZoneIds()
方法獲取所有合法的“區域/城市”字符串:
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
對於老的時區類TimeZone
,Java 8也提供了轉化方法:
ZoneId oldToNewZoneId = TimeZone.getDefault().toZoneId();
有了ZoneId
,我們就可以將一個LocalDate
、LocalTime
或LocalDateTime
對象轉化爲ZonedDateTime
對象:
LocalDateTime localDateTime = LocalDateTime.now();
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, shanghaiZoneId);
將zonedDateTime
打印到控制檯爲:
2017-01-05T15:26:56.147+08:00[Asia/Shanghai]
ZonedDateTime 帶時區日期時間處理
對象由兩部分構成,LocalDateTime
和ZoneId
,其中2017-01-05T15:26:56.147
部分爲LocalDateTime
,+08:00[Asia/Shanghai]
部分爲ZoneId
。
另一種表示時區的方式是使用ZoneOffset
,它是以當前時間和**世界標準時間(UTC)/格林威治時間(GMT)**的偏差來計算,例如:
ZoneOffset zoneOffset = ZoneOffset.of("+09:00");
LocalDateTime localDateTime = LocalDateTime.now();
OffsetDateTime offsetDateTime = OffsetDateTime.of(localDateTime, zoneOffset);
GMT(格林威治時間)、CST(可視爲美國、澳大利亞、古巴或中國的標準時間)、PST(太平洋時間)
GMT: UTC +0 = GMT: GMT +0
CST: UTC +8 = CST: GMT +8
PST: UTC -8 = PST: GMT -8
從上面的圖中我們可以看出,LocalDateTime,並不能表示我們人類世界中完整的時間,而ZonedDateTime可以。
而且上面的轉換中我們可以知道,LocalDateTime轉Instant或者OffsetDatetime都是需要加上偏移時區的(ZoneOffset)。
所以可以得出 OffsetDatetime和Instant也是可以表示人類世界中完整的時間的,和ZoneDateTime是等效的。
OffsetDatetime、Instant和 ZoneDateTime區別
OffsetDateTime ,ZonedDateTime 和 Instant 都會在時間線上存儲一個納秒級精度。 Instant 是最簡單的,只需代表instant。 OffsetDateTime 添加到UTC / Greenwich的偏移瞬間,這允許獲得本地日期時間。 ZonedDateTime 添加完整的時區規則。
因此 OffsetDateTime 和之間的區別ZonedDateTime 是後者包括涵蓋夏令時調整的規則。
國際時區 TimeZone ID列表 獲取方法:TimeZone.getAvailableIDs()
其他曆法
Java中使用的歷法是ISO 8601日曆系統,它是世界民用曆法,也就是我們所說的公曆。平年有365天,閏年是366天。閏年的定義是:非世紀年,能被4整除;世紀年能被400整除。爲了計算的一致性,公元1年的前一年被當做公元0年,以此類推。
此外Java 8還提供了4套其他曆法(很奇怪爲什麼沒有漢族人使用的農曆),每套曆法都包含一個日期類,分別是:
ThaiBuddhistDate
:泰國佛教歷MinguoDate
:中華民國曆JapaneseDate
:日本歷HijrahDate
:伊斯蘭曆
每個日期類都繼承ChronoLocalDate
類,所以可以在不知道具體曆法的情況下也可以操作。不過這些曆法一般不常用,除非是有某些特殊需求情況下才會使用。
這些不同的歷法也可以用於向公曆轉換:
LocalDate date = LocalDate.now();
JapaneseDate jpDate = JapaneseDate.from(date);
由於它們都繼承ChronoLocalDate
類,所以在不知道具體曆法情況下,可以通過ChronoLocalDate
類操作日期:
Chronology jpChronology = Chronology.ofLocale(Locale.JAPANESE);
ChronoLocalDate jpChronoLocalDate = jpChronology.dateNow();
我們在開發過程中應該儘量避免使用ChronoLocalDate
,儘量用與曆法無關的方式操作時間,因爲不同的歷法計算日期的方式不一樣,比如開發者會在程序中做一些假設,假設一年中有12個月,如果是中國農曆中包含了閏月,一年有可能是13個月,但開發者認爲是12個月,多出來的一個月屬於明年的。再比如假設年份是累加的,過了一年就在原來的年份上加一,但日本天皇在換代之後需要重新紀年,所以過了一年年份可能會從1開始計算。
在實際開發過程中建議使用LocalDate
,包括存儲、操作、業務規則的解讀;除非需要將程序的輸入或者輸出本地化,這時可以使用ChronoLocalDate
類。
日期的操作和格式化
增加和減少日期-簡單操作
LocalDate date = LocalDate.of(2017, 1, 5); // 2017-01-05
LocalDate date1 = date.withYear(2016); // 修改爲 2016-01-05
LocalDate date2 = date.withMonth(2); // 修改爲 2017-02-05
LocalDate date3 = date.withDayOfMonth(1); // 修改爲 2017-01-01
LocalDate date4 = date.plusYears(1); // 增加一年 2018-01-05
LocalDate date5 = date.minusMonths(2); // 減少兩個月 2016-11-05
LocalDate date6 = date.plus(5, ChronoUnit.DAYS); // 增加5天 2017-01-10
像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 對象爲模板,對某些狀態進行修改創建該對象的副本 |
TemporalField-複雜操作
TemporalField
是一個接口,它定義瞭如何訪問temporal對象某個字段的值。ChronoField
枚舉類實現了這一接口,所以你可以很方便地使用get方法得到枚舉元素的值:
int year = LocalDate.now().get(ChronoField.YEAR);
LocalDate date1 = LocalDate.now();
// 下一個週二
LocalDate nextTuesday = date1.with(TemporalAdjusters.next(DayOfWeek.TUESDAY));
// 當月的第二個週六
LocalDate firstInMonth = LocalDate.of(date1.getYear(), date1.getMonth(), 1);
LocalDate secondSaturday = firstInMonth.with(TemporalAdjusters.nextOrSame(DayOfWeek.SATURDAY)).with(TemporalAdjusters.next(DayOfWeek.SATURDAY));
注:TemporalAdjusters類中有許多常用的特殊的日期的方法(類方法),使用時可以仔細查看,可以很大程度減少日期判斷的代碼量!
TemporalAdjusters 包含許多靜態方法,可以直接調用,以下列舉一些:
方法名 | 描述 |
---|---|
dayOfWeekInMonth | 返回同一個月中每週的第幾天 |
firstDayOfMonth | 返回當月的第一天 |
firstDayOfNextMonth | 返回下月的第一天 |
firstDayOfNextYear | 返回下一年的第一天 |
firstDayOfYear | 返回本年的第一天 |
firstInMonth | 返回同一個月中第一個星期幾 |
lastDayOfMonth | 返回當月的最後一天 |
lastDayOfNextMonth | 返回下月的最後一天 |
lastDayOfNextYear | 返回下一年的最後一天 |
lastDayOfYear | 返回本年的最後一天 |
lastInMonth | 返回同一個月中最後一個星期幾 |
next / previous | 返回後一個/前一個給定的星期幾 |
nextOrSame / previousOrSame | 返回後一個/前一個給定的星期幾,如果這個值滿足條件,直接返回 |
DateTimeFormatter 格式化日期
LocalDate localDate = LocalDate.of(2019, 9, 12);
String s1 = localDate.format(DateTimeFormatter.BASIC_ISO_DATE);
String s2 = localDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
System.out.println("s1:"+ s1); // 20190912
System.out.println("s2:"+ s2); // 2019-09-12
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println("獲取當前時間:"+localDateTime); // 2019-09-16T14:54:36.520
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:MM:SS");
String s = localDateTime.format(formatter);
System.out.println("格式化當前時間:"+ s); // 2019-09-16 14:09:52
LocalDate localDate1 = LocalDate.parse("20190912", DateTimeFormatter.BASIC_ISO_DATE);
LocalDate localDate2 = LocalDate.parse("2019-09-12", DateTimeFormatter.ISO_LOCAL_DATE);
DateTimeFormatter我們更多的是直接使用pattern來做轉換,其實這個類本身已經提供了一些預定義好的實例供我們使用。
下面把兩者的具體釋義和示例都貼出來供大家參考。
預定義
Predefined Formatters | Formatter Description | Example |
---|---|---|
ofLocalizedDate(dateStyle) | Formatter with date style from the locale | ‘2011-12-03’ |
ofLocalizedTime(timeStyle) | Formatter with time style from the locale | ‘10:15:30’ |
ofLocalizedDateTime(dateTimeStyle) | Formatter with a style for date and time from the locale | ‘3 Jun 2008 11:05:30’ |
ofLocalizedDateTime(dateStyle,timeStyle) | Formatter with date and time styles from the locale | ‘3 Jun 2008 11:05’ |
BASIC_ISO_DATE | Basic ISO date | ‘20111203’ |
ISO_LOCAL_DATE | ISO Local Date | ‘2011-12-03’ |
ISO_OFFSET_DATE | ISO Date with offset | ‘2011-12-03+01:00’ |
ISO_DATE | ISO Date with or without offset | ‘2011-12-03+01:00’; ‘2011-12-03’ |
ISO_LOCAL_TIME | Time without offset | ‘10:15:30’ |
ISO_OFFSET_TIME | Time with offset | ‘10:15:30+01:00’ |
ISO_TIME | Time with or without offset | ‘10:15:30+01:00’; ‘10:15:30’ |
ISO_LOCAL_DATE_TIME | ISO Local Date and Time | ‘2011-12-03T10:15:30’ |
ISO_OFFSET_DATE_TIME | Date Time with Offset | ‘2011-12-03T10:15:30+01:00’ |
ISO_ZONED_DATE_TIME | Zoned Date Time | ‘2011-12-03T10:15:30+01:00[Europe/Paris]’ |
ISO_DATE_TIME | Date and time with ZoneId | ‘2011-12-03T10:15:30+01:00[Europe/Paris]’ |
ISO_ORDINAL_DATE | Year and day of year | ‘2012-337’ |
ISO_WEEK_DATE | Year and Week | ‘2012-W48-6’ |
ISO_INSTANT | Date and Time of an Instant | ‘2011-12-03T10:15:30Z’ |
RFC_1123_DATE_TIME | RFC 1123 / RFC 822 | ‘Tue, 3 Jun 2008 11:05:30 GMT’ |
Pattern
All letters ‘A’ to ‘Z’ and ‘a’ to ‘z’ are reserved as pattern letters. The following pattern letters are defined:
Symbol | Meaning | Presentation | Examples |
---|---|---|---|
G | era | text | AD; Anno Domini; A |
u | year | year | 2004; 04 |
y | year-of-era | year | 2004; 04 |
D | day-of-year | number | 189 |
M/L | month-of-year | number/text | 7; 07; Jul; July; J |
d | day-of-month | number | 10 |
Q/q | quarter-of-year | number/text | 3; 03; Q3; 3rd quarter |
Y | week-based-year | year | 1996; 96 |
w | week-of-week-based-year | number | 27 |
W | week-of-month | number | 4 |
E | day-of-week | text | Tue; Tuesday; T |
e/c | localized day-of-week | number/text | 2; 02; Tue; Tuesday; T |
F | week-of-month | number | 3 |
a | am-pm-of-day | text | PM |
h | clock-hour-of-am-pm (1-12) | number | 12 |
K | hour-of-am-pm (0-11) | number | 0 |
k | clock-hour-of-am-pm (1-24) | number | 0 |
H | hour-of-day (0-23) | number | 0 |
m | minute-of-hour | number | 30 |
s | second-of-minute | number | 55 |
S | fraction-of-second | fraction | 978 |
A | milli-of-day | number | 1234 |
n | nano-of-second | number | 987654321 |
N | nano-of-day | number | 1234000000 |
V | time-zone ID | zone-id | America/Los_Angeles; Z; -08:30 |
z | time-zone name | zone-name | Pacific Standard Time; PST |
O | localized zone-offset | offset-O | GMT+8; GMT+08:00; UTC-08:00; |
X | zone-offset ‘Z’ for zero | offset-X | Z; -08; -0830; -08:30; -083015; -08:30:15; |
x | zone-offset | offset-x | +0000; -08; -0830; -08:30; -083015; -08:30:15; |
Z | zone-offset | offset-Z | +0000; -0800; -08:00; |
p | pad next | pad modifier | 1 |
’ | escape for text | delimiter | |
‘’ | single quote | literal | ’ |
[ | optional section start | ||
] | optional section end | ||
# | reserved for future use | ||
{ | reserved for future use | ||
} | reserved for future use |
舊的日期時間支持
Date 和 LocalDate 互轉
// Date -> LocalDateTime/LocalDate
Date date = new Date();
LocalDateTime localDateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
// LocalDate -> Date
LocalDate nowLocalDate = LocalDate.now();
Date date = Date.from(nowLocalDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
// LocalDateTime -> Date
LocalDateTime localDateTime = LocalDateTime.now();
Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
Date 和 Instant 互轉
// Date -> Instant
Instant timestamp = new Date().toInstant();
// Instant -> Date
Date date = Date.from(Instant.now());
TimeZone 和 ZoneId 互轉
ZoneId defaultZone = TimeZone.getDefault().toZoneId();
TimeZone tz = TimeZone.getTimeZone(defaultZone);
ZonedDateTime 和 GregorianCalendar 互轉
ZonedDateTime gregorianCalendarDateTime = new GregorianCalendar().toZonedDateTime();//GregorianCalendar -> ZonedDateTime
GregorianCalendar gc = GregorianCalendar.from(gregorianCalendarDateTime);//ZonedDateTime -> GregorianCalendar
其他相關轉換
Long時間戳 和 LocalDateTime 互轉
// Long時間戳 -> LocalDateTime
long timestamp = System.currentTimeMillis();
LocalDateTime localDateTime = Instant.ofEpochMilli(timestamp).atZone(ZoneId.systemDefault()).toLocalDateTime();
// LocalDate -> Long時間戳
LocalDate localDate = LocalDate.now();
long timestamp = localDate.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
// LocalDateTime -> Long時間戳
LocalDateTime localDateTime = LocalDateTime.now();
long timestamp = localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli(); // GMT +8時區
Instant 和 LocalDateTime互轉
// LocalDateTime -> Instant
Instant instant = LocalDateTime.now().toInstant(ZoneOffset.of("+8"));
// 或者
Instant instant1 = LocalDateTime.now().toInstant(ZoneOffset.ofHours(8));
// Instant -> LocalDateTime
LocalDateTime instantToLocalDateTime = LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());
String 和 LocalDateTime 互轉
// String -> LocalDateTime
LocalDateTime stringToLocalDateTime = LocalDateTime.parse("2018-03-11 15:30:11", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
// LocalDateTime -> String
String localDateTimeToString = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
String 和 Date 互轉
// String -> Date
Date stringToDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2018-03-11 15:30:11");
//Date -> String
String dateToString = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
Timestamp 和 LocalDateTime 互轉
// Timestamp -> LocalDateTime
LocalDateTime timeStampToLocalDateTime = LocalDateTime.ofInstant(new Timestamp(1520754566856L).toInstant(), ZoneId.systemDefault());
// LocalDateTime -> TimeStamp
Timestamp localDateTimeToTimeStamp = Timestamp.valueOf(LocalDateTime.now());
Timestamp 和 Date 互轉
// Timestamp -> Date
Date timestampToDate = Date.from(new Timestamp(1520754566856L).toInstant());
// Date -> LocalDateTime
LocalDateTime dateToLocalDateTime = LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault());
參考
Java 8 日期時間 API 指南 | Java 8 教程彙總
Java 8新特性(四):新的時間和日期API | 一書生VOID的博客
Java8新特性總結 -6.Date/Time API_java_BlueKitty的博客-CSDN博客
Java8新特性整理之新的時間和日期API(終章)_java_一大三千的博客-CSDN博客
Java8學習筆記:LocalDateTime、Instant 和 OffsetDateTime 相互轉換_java_山鬼謠的專欄-CSDN博客