引言
java 8通過發布新的Date-Time API進一步加強對日期與時間的處理。在舊版的java中,日期時間APi存在諸多問題。java 8引入的新的一系列API,對時間日期處理提供了更好的支持,清楚的定義了時間日期的一些概念,比如說,瞬時時間(Instant),持續時間(duration),日期(date),時間(time),時區(time-zone)以及時間段(Period)。同時,借鑒了Joda庫的一些優點,比如將人和機器對時間日期的理解區分開的。
java8新特性
- jdk8新特性之一Lambda表達式
- jdk8新特性之二方法引用
- jdk8新特性之三函數式接口
- jdk8新特性之四默認方法
- jdk8新特性之五Stream API
- jdk8新特性之六Optional類
- jdk8新特性之七Nashorn JavaScript
- jdk8新特性之八日期時間API
- jdk8新特性之九Base64
舊版日期時間API的問題
- 非線程安全:java.uttil.Date 是非線程安全的,所有的日期類都是可變的,這是java日期類最大的問題之一。
- 設計很差:java的日期/時間類的定義並不一致,在java.util和java.sql的包中毒有日期類,此外用於格式化和解析的類在java.text包中定義。java.util.Date同時包含日期和時間,而java.sql.Date僅包含日期,將其納入java.sql包並不合理。另外這兩個類都有相同提高的名字,這本身就是一個非常糟糕的設計。
- 時區處理麻煩:日期類並不提供國際化,沒有時區支持,因此java引入了java.util.Caleandar和java.util.TimeZone類,但他們同樣存在上述所有的問題。
新日期時間API
java8在java.time包下提供了很多新的API。以下為兩個比較重要的API:
- Local(本地):簡化了日期時間的處理,沒有時區的問題。
- Zoned(時區):通過制定的時區處理日期時間。
具體的API: - LocalDate:存儲不包含時間的日期,比如2020-01-11。可以用來存儲生日,周年紀念日,入職日期等。
- LocalTime:存儲不包含日期的時間,比如11:07:03.580。
LocalDate-日期
只存儲年月日。格式:2020-01-11
創建LocalDate對象
//獲取當前日期
LocalDate currDate=LocalDate.now();
System.out.println("當前日期:"+currDate);
//指定日期
LocalDate noeDay=LocalDate.of(2020, 1, 11);
System.out.println("當前日期:"+noeDay);
//通過字符串指定日期
LocalDate towDay=LocalDate.parse("2020-01-11");
System.out.println("當前日期:"+towDay);
運行結果輸出:
當前日期:2020-01-11
當前日期:2020-01-11
當前日期:2020-01-11
獲取年月日周
// 年
int year = currDate.getYear();
// 月
int month = currDate.getMonthValue();
// 一月的第幾天
int day = currDate.getDayOfMonth();
// 一周的第幾天
int week = currDate.getDayOfWeek().getValue();
System.out.printf("%d年%d月%d日,星期%d%n", year, month, day, week);
運行結果輸出:
2020年1月11日,星期6
比較兩個日期是否相同
LocalDate重寫了equals方法,讓日期的比較也變得簡單了。
LocalDate date1 = LocalDate.parse("2020-01-11");
LocalDate date2 = LocalDate.parse("2020-01-11");
System.out.println("比較兩個日期是否相同:"+date1.equals(date2));
運行結果輸出:
比較兩個日期是否相同:true
日期前后比較
//isBefore在之前
boolean isBefore=LocalDate.parse("2020-01-11").isBefore(LocalDate.parse("2020-01-10"));
//isAfter在之后
boolean isAfter=LocalDate.parse("2020-01-11").isAfter(LocalDate.parse("2020-01-10"));
日期加減
System.out.println("當前時間"+LocalDate.now());
System.out.println("當前時間加1天"+LocalDate.now().plusDays(1));
System.out.println("當前時間加1月"+LocalDate.now().plusMonths(1));
System.out.println("當前時間加1年"+LocalDate.now().plusYears(1));
運行結果輸出:
當前時間2020-01-11
當前時間加1天2020-01-12
當前時間加1月2020-02-11
當前時間加1年2021-01-11
LocalTime-時間
存儲不包含日期的時間,比如11:07:03.580。
LocalTime和LocalDate類似,區別在於LocalDate不包含具體時間,而LocalTime包含具體時間。同樣可以使用now、of或parse方法來獲得對象。
LocalTime time = LocalTime.now();
LocalTime time2 =LocalTime.of(11, 7,3);
LocalTime time3 =LocalTime.parse("11:07:03");
LocalDate類似它也擁有isBefore、isAfter、獲取時間單元等方法,就不再贅述。
需要注意的是,LocalTime獲得的時間格式為:11:41:58.904。也就是,HH:mm:ss.nnn,這里nnn是納秒。
還有一個在實戰中查詢日期區間時我們經常定義的“23:59:59.99”常量再也不用自己定義了。
//最大時間23:59:59.999999999
LocalTime maxTime =LocalTime.MAX;
//最小時間00:00
LocalTime minTime =LocalTime.MIN;
LocalDateTime-日期和時間的組合
LocalDateTime表示日期和時間組合。同樣可以使用now、of或parse方法來獲得對象。
LocalDateTime datetime=LocalDateTime.now();
LocalDateTime datetime2=LocalDateTime.of(2020, 1,11,11,54,30);
LocalDateTime datetime3=LocalDateTime.parse("2020-01-11T12:35");
也可以調用LocalDate的atTime()方法或LocalTime的atDate()方法將LocalDate或LocalTime合並成一個LocalDateTime。
LocalDateTime datetime4=LocalDate.now().atTime(LocalTime.now());
LocalDateTime datetime5=LocalTime.now().atDate(LocalDate.now());
LocalDateTime與LocalDate和LocalTime之間可以相互轉化。其他日期增減等操作與上面的類似。
Instant-時間戳
用於“時間戳”的運算。它是以Unix元年(傳統的設定為UTC時區1970年1月1日午夜時分)開始所經歷的描述進行運算。
// 默認獲取UTC時區
Instant instant=Instant.now();
System.out.println("當前時間(UTC時區):"+instant);
System.out.println("獲取時間戳:"+instant.toEpochMilli());
// 偏移量運算
OffsetDateTime offsetDateTime =instant.atOffset(ZoneOffset.ofHours(8));
System.out.println("當前時間:"+offsetDateTime);
// 以Unix元年為起點,進行偏移量運算
Instant instant2 = Instant.ofEpochSecond(60);
System.out.println(instant2);
運行結果輸出為:
當前時間(UTC時區):2020-01-11T05:55:35.493Z
獲取時間戳:1578722135493
當前時間:2020-01-11T13:55:35.493+08:00
1970-01-01T00:01:00Z
Duration-獲取時間段
Duration的內部實現與Instant類似,但Duration表示時間段,通過between方法創建。
LocalDateTime from = LocalDateTime.now();
LocalDateTime to = LocalDateTime.now().plusDays(1);
Duration duration=Duration.between(from, to);
//總天數
long days=duration.toDays();
//小時
long hours=duration.toHours();
//分鍾
long millis=duration.toMillis();
//秒數
long seconds=duration.getSeconds();
//毫秒數
long minutes=duration.toMinutes();
//納秒數
long nanos=duration.toNanos();
System.out.println("天數"+days);
System.out.println("小時"+hours);
System.out.println("分鍾"+millis);
System.out.println("秒數"+seconds);
System.out.println("毫秒數"+minutes);
System.out.println("納秒數"+nanos);
運行結果輸出:
天數1
小時24
分鍾86400000
秒數86400
毫秒數1440
納秒數86400000000000
Duration對象還可以通過of()方法創建,該方法參數為時間段長度和時間單位。
// 7天
Duration duration1 = Duration.of(7, ChronoUnit.DAYS);
// 60秒
Duration duration2 = Duration.of(60, ChronoUnit.SECONDS);
Period-獲取日期段
Period與Duration類似,獲取一個時間段,只不過單位為年月日,也可以通過of方法和between方法創建,between方法接收的參數為LocalDate。
Period period=Period.of(2020, 1, 11);
Period period1 = Period.between(LocalDate.now(), LocalDate.now().plusYears(1));
ZonedDateTime-創建時區時間
ZonedDateTime類,用於處理帶時區的日期和時間。ZoneId表示不同的時區。大約有40不同的時區。
Set<String> allZoneIds=ZoneId.getAvailableZoneIds();
創建時區:
ZoneId zoneId=ZoneId.of("Asia/Shanghai");
把LocalDateTime轉換成特定的時區:
ZonedDateTime zonedDateTime=ZonedDateTime.of(LocalDateTime.now(), zoneId);
獲取當前時區:
//獲取當前時區
ZoneId z=ZoneId.systemDefault();
獲取日期時間:
ZonedDateTime dd = ZonedDateTime.now();
ZonedDateTime date1 = ZonedDateTime.parse("2015-12-03T10:15:30+05:30[Asia/Shanghai]");
時間日期格式化
Java8對日期的格式化操作非常簡單,首先看到上面的類大多都提供了parse方法,可以直接通過解析字符串得到對應的對象。
而日期和時間的格式化可通過LocalDateTime的format方法進行格式化。
LocalDateTime dateTime=LocalDateTime.now();
String str=dateTime.format(DateTimeFormatter.ISO_DATE_TIME);
System.out.println(str);
str = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(str);
可以使用DateTimeFormatter預置的格式,也可以通過DateTimeFormatter.ofPattern方法來指定格式。