文章目錄
一、簡介
Java 8通過發佈新的Date-Time API (JSR 310)來進一步加強對日期與時間的處理。
在舊版的 Java 中,日期時間 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類,但他們同樣存在上述所有的問題。
Java8引入了一套全新的日期時間處理API,新的API基於ISO標準日曆系統。
Java8 在 java.time 包下提供了很多新的 API。以下爲兩個比較重要的 API:
-
Local(本地) − 簡化了日期時間的處理,沒有時區的問題。
-
Zoned(時區) − 通過制定的時區處理日期時間。
新的java.time包涵蓋了所有處理日期,時間,日期/時間,時區,時刻(instants),過程(during)與時鐘(clock)的操作。
二、日期/時間初識
示例1:獲取當前日期/時間
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
public class DateTimeAPI {
public static void main(String[] args) {
LocalDate.now(); // 獲取當前日期 format: yyyy-MM-dd
LocalTime.now(); // 獲取當前時間 format: HH:mm:ss
LocalDateTime.now(); // 獲取當前日期時間 format: yyyy-MM-dd HH:mm:ss
}
}
以上是對當前日期的獲取,比較三者的輸出(print console:)有什麼區別:
2020-04-20
11:27:01.570
2020-04-20T11:27:01.570
示例2: 構造指定日期/時間
調用工廠方法LocalDate.of()創建任意日期, 該方法需要傳入年、月、日做參數,返回對應的LocalDate實例。這個方法的好處是沒再犯老API的設計錯誤,比如年度起始於1900,月份是從0開始等等.
public static void main(String[] args) {
LocalDate date = LocalDate.of(2000, 1, 1);
LocalTime time = LocalTime.of(0,0,0);
LocalDateTime dateTime = LocalDateTime.of(date, time);
System.out.println("千禧年=" + dateTime);
LocalDateTime dateTime1 = LocalDateTime.of(2000,1,1,0,0,0);
System.out.println("千禧年=" + dateTime1);
}
輸出結果如下:
千禧年=2000-01-01T00:00
千禧年=2000-01-01T00:00
示例3: 獲取年月日信息
public static void main(String[] args) {
LocalDate date = LocalDate.now();
System.out.printf("年=%d, 月=%d, 日=%d", date.getYear(), date.getMonthValue(), date.getDayOfMonth());
System.out.println();
LocalTime time = LocalTime.now();
System.out.printf("時=%d, 分=%d, 秒=%d, 毫秒=%d", time.getHour(), time.getMinute(), time.getSecond(), time.getNano());
System.out.println();
LocalDateTime dateTime = LocalDateTime.now();
System.out.printf("年=%d, 月=%d, 日=%d, 時=%d, 分=%d, 秒=%d, 毫秒=%d", dateTime.getYear(), dateTime.getMonthValue(),
dateTime.getDayOfMonth(), dateTime.getHour(), dateTime.getMinute(), dateTime.getSecond(), dateTime.getNano());
}
輸出結果如下:
年=2020, 月=4, 日=20
時=11, 分=58, 秒=1, 毫秒=800000000
年=2020, 月=4, 日=20, 時=11, 分=58, 秒=1, 毫秒=801000000
示例4: 獲取當前日期時間戳
public static void main(String[] args) {
// 獲取秒數
Long second = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
System.out.println("時間戳 秒數: " + second);
// 獲取毫秒
Long millisecond = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
System.out.println("時間戳 毫秒: " + millisecond);
}
輸出結果如下:
時間戳 秒數: 1587367802
時間戳 毫秒: 1587367802085
三、比較與計算
示例1: 日期時間計算
Java8提供了新的plusXxx() 、minsXxx()方法用於計算日期時間增量值 / 減量值,替代了原來的add()方法。新的API將返回一個全新的日期時間示例,需要使用新的對象進行接收。
public static void main(String[] args) {
// 時間增量
LocalTime time = LocalTime.now();
System.out.println("當前時間 time: " + time);
System.out.println("增加 2小時: " + time.plusHours(2));
System.out.println("增加 2分鐘: " + time.plusMinutes(2));
System.out.println("增加 2秒鐘: " + time.plusSeconds(2));
System.out.println("增加 2毫秒: " + time.plusNanos(2));
// 日期增量
LocalDate date = LocalDate.now();
System.out.println("當前日期 date: " + date);
System.out.println("增加 2年: " + date.plusYears(2));
System.out.println("增加 2月: " + date.plusMonths(2));
System.out.println("增加 2周: " + date.plusWeeks(2));
System.out.println("增加 2天: " + date.plusDays(2));
}
輸出結果如下:
當前時間 time: 14:31:54.785
增加 2小時: 16:31:54.785
增加 2分鐘: 14:33:54.785
增加 2秒鐘: 14:31:56.785
增加 2毫秒: 14:31:54.785000002
當前日期 date: 2020-04-20
增加 2年: 2022-04-20
增加 2月: 2020-06-20
增加 2周: 2020-05-04
增加 2天: 2020-04-22
示例2: 日期時間比較
Java8提供了isAfter()、isBefore()用於判斷當前日期時間和指定日期時間的比較。
public static void main(String[] args) {
LocalDateTime dateTime = LocalDateTime.now();
LocalDateTime dateTime1 = LocalDateTime.of(2000,1,1,0,0,0,0);
if (dateTime.isAfter(dateTime1)) {
System.out.println("千禧年已經過去了。。。。");
}
LocalDateTime dateTime2 = LocalDateTime.of(2050, 1,1,0,0,0,0);
if (dateTime.isBefore(dateTime2)) {
System.out.println("2050年還未到來!!!!!");
}
}
輸出結果如下:
千禧年已經過去了。。。。
2050年還未到來!!!!!
示例3: 比較兩個日期是否相等
public static void main(String[] args) {
LocalDate now = LocalDate.now();
LocalDate date = LocalDate.of(2018, 9, 24);
System.out.println("日期是否相等=" + now.equals(date));
}
輸出結果如下:
日期是否相等=false
示例4: 計算時間點之間的間隔
public static void main(String[] args) {
// 計算時間點之間的間隔
LocalDateTime startTime = LocalDateTime.now();
LocalDateTime endTime = startTime.plusHours(1).plusMinutes(30);
Duration duration = Duration.between(startTime, endTime);
Long day = duration.toDays();
System.out.println("間隔天數: " + day);
Long hour = duration.toHours();
System.out.println("間隔小時: " + hour);
Long minute = duration.toMinutes();
System.out.println("間隔分鐘: " + minute);
Long millis = duration.toMillis();
System.out.println("間隔毫秒: " + millis);
}
輸出結果如下:
間隔天數: 0
間隔小時: 1
間隔分鐘: 90
間隔毫秒: 5400000
示例5: 計算日期之間的間隔
public static void main(String[] args) {
// 計算日期之間的間隔
LocalDate startDate = LocalDate.now();
LocalDate endDate = startDate.plusMonths(1).plusDays(5);
Period period = Period.between(startDate, endDate);
int year = period.getYears();
System.out.println("間隔年數: " + year);
int month = period.getMonths();
System.out.println("間隔月數: " + month);
int day = period.getDays();
System.out.println("間隔天數: " + day);
}
輸出結果如下:
間隔年數: 0
間隔月數: 1
間隔天數: 5
四、時區
示例1: 創建帶有時區的日期時間(ZoneId)
Java 8不僅分離了日期和時間,也把時區分離出來了。現在有一系列單獨的類如ZoneId來處理特定時區,ZoneDateTime類來表示某時區下的時間。
public static void main(String[] args) {
// 上海時間
ZoneId shanghaiZoneId = ZoneId.of("Asia/Shanghai");
ZonedDateTime shanghaiZonedDateTime = ZonedDateTime.now(shanghaiZoneId);
// 東京時間
ZoneId tokyoZoneId = ZoneId.of("Asia/Tokyo");
ZonedDateTime tokyoZonedDateTime = ZonedDateTime.now(tokyoZoneId);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println("上海時間: " + shanghaiZonedDateTime.format(formatter));
System.out.println("東京時間: " + tokyoZonedDateTime.format(formatter));
}
輸出結果如下:
上海時間: 2020-04-20 15:03:18
東京時間: 2020-04-20 16:03:18
示例2: 創建帶有時區的日期時間(UTC)
public static void main(String[] args) {
// 北京時間
ZonedDateTime zonedDateTime = LocalDateTime.now().atZone(ZoneId.of("Asia/Shanghai"));
ZonedDateTime zonedDateTime1 = LocalDateTime.now().atZone(ZoneId.of("UTC+8"));
System.out.println("上海時間 : " + zonedDateTime);
System.out.println("上海時間 UTC: " + zonedDateTime1);
// 東京時間
ZonedDateTime zonedDateTime2 = LocalDateTime.now().atZone(ZoneId.of("Asia/Tokyo"));
ZonedDateTime zonedDateTime3 = LocalDateTime.now().atZone(ZoneId.of("UTC+9"));
System.out.println("東京時間 : " + zonedDateTime2);
System.out.println("東京時間 UTC: " + zonedDateTime3);
}
輸出結果如下:
上海時間 : 2020-04-20T15:15:58.378+08:00[Asia/Shanghai]
上海時間 UTC: 2020-04-20T15:15:58.380+08:00[UTC+08:00]
東京時間 : 2020-04-20T15:15:58.380+09:00[Asia/Tokyo]
東京時間 UTC: 2020-04-20T15:15:58.381+09:00[UTC+09:00]
五、格式化
示例1: 使用預定義格式解析與格式化日期
public static void main(String[] args) {
// 解析日期
String dateText = "20200420";
LocalDate date = LocalDate.parse(dateText, DateTimeFormatter.BASIC_ISO_DATE);
System.out.println("格式化後的日期: " + date);
// 格式化日期
dateText = date.format(DateTimeFormatter.BASIC_ISO_DATE);
System.out.println("dateText: " + dateText);
}
輸出結果如下:
格式化後的日期: 2020-04-20
dateText: 20200420
示例2: 日期和字符串的相互轉換
public static void main(String[] args) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 日期時間轉字符串
LocalDateTime now = LocalDateTime.now();
String dateToStr = now.format(formatter);
System.out.println("日期轉換成字符串: " + dateToStr);
// 字符串轉日期時間
String datetimeText = "1999-12-31 23:59:59";
LocalDateTime strToDate = LocalDateTime.parse(datetimeText, formatter);
System.out.println("字符串轉換成日期: " + strToDate);
}
輸出結果如下:
日期轉換成字符串: 2020-04-20 15:50:45
字符串轉換成日期: 1999-12-31T23:59:59
示例3: 日期和時間戳的相互轉換
public static void main(String[] args) {
// 日期轉時間戳
System.out.println(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli());
System.out.println(LocalDateTime.now().toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
System.out.println(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
// 時間戳轉成日期
Long instantL = 1587367830000L;
Instant instant = Instant.ofEpochMilli(instantL);
LocalDateTime instantToDate = LocalDateTime.ofInstant(instant, ZoneId.of("+8"));
System.out.println("時間戳轉換成日期: " + instantToDate);
}
輸出結果如下:
1587371801887
1587371801888
1587371801888
時間戳轉換成日期: 2020-04-20T15:30:30
示例4: 字符串和時間戳的相互轉換
public static void main(String[] args) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 字符串轉時間戳
String datetimeText = "2020-04-20 15:30:30";
LocalDateTime strToDate = LocalDateTime.parse(datetimeText, formatter);
System.out.println("字符串轉換成日期: " + strToDate);
Long dateToInstant = strToDate.toInstant(ZoneOffset.of("+8")).toEpochMilli();
System.out.println("日期轉換成毫秒時間戳: " + dateToInstant);
// 時間戳轉換成字符串
Long instant = 1587367830000L;
LocalDateTime instantToDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(instant), ZoneId.of("+8"));
System.out.println("時間戳轉換成日期: " + instantToDate);
String dateToStr = formatter.format(instantToDate);
System.out.println("日期轉換成字符串: " + dateToStr);
}
輸出結果如下:
字符串轉換成日期: 2020-04-20T15:30:30
日期轉換成毫秒時間戳: 1587367830000
時間戳轉換成日期: 2020-04-20T15:30:30
日期轉換成字符串: 2020-04-20 15:30:30
六、時間矯正器
TemporalAdjuster : 時間校正器。它是一個函數式接口,只有一個方法 adjustInto。有時我們可能需要獲取例如:將日期調整到“下個週日”等操作。
TemporalAdjusters : 該類通過靜態方法提供了大量的常用TemporalAdjuster 的實現。
Temporal 是一個接口,LocalDateTime 、LocalDate 、 LocalTime 都是它的實現類,有實現方法 with(TemporalAdjuster adjuster)。
@FunctionalInterface
public interface TemporalAdjuster {
Temporal adjustInto(Temporal temporal);
}
示例1: 時間矯正
public static void main(String[] args) {
// 接下來第一個週五
LocalDateTime nextFriday = LocalDateTime.now().with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
System.out.println("接下來第一個週五: " + nextFriday);
// 明年的感恩節(明年11月第四個星期四)
LocalDate date = LocalDate.now().plusYears(1).withMonth(11)
.with(TemporalAdjusters.dayOfWeekInMonth(4, DayOfWeek.THURSDAY));
System.out.println("明年感恩節: " + date);
}
輸出結果如下:
接下來第一個週五: 2020-04-24T18:55:50.841
明年感恩節: 2021-11-25
注意: TemporalAdjusters.next(DayOfWeek day) 方法返回的是 接下來第一個週五,並不是我們一般理解的 下週五,比如說:今天 2020-01-13(週一),那麼返回的就是 2020-01-17 四天後的週五。
七、相關類說明
Instant 時間戳
Duration 持續時間、時間差
LocalDate 只包含日期,比如:2018-09-24
LocalTime 只包含時間,比如:10:32:10
LocalDateTime 包含日期和時間,比如:2018-09-24 10:32:10
Peroid 時間段
ZoneOffset 時區偏移量,比如:+8:00
ZonedDateTime 帶時區的日期時間
Clock 時鐘,可用於獲取當前時間戳
java.time.format.DateTimeFormatter 時間格式化類