Java 8 日期時間 API
在舊版本的Java中,日期時間API存在諸多問題,其中有:
- 非線程安全 - java.util.Date 是非線程安全的,所有的日期類都是可變的,這是Java日期類最大的問題之一。
- 設計很差 - Java的日期/時間類的定義並不一致,在java.util和java.sql的包中都有日期類,此外用於格式化和解析的類在java.text包中定義。java.Date同時包含日期和時間,而java.Date僅包含日期,將其納入java.sql包並不合理,另外這兩個類都有相同的名字,本身就是一個非常糟糕的設計。
- 時區處理麻煩 - 日期類並不提供國際化,沒有時區支持,因此Java引入了java.util.Calenda和java.util.TimeZone類,單他們同樣存在上述的所有問題。
Java 8 在java.time包下提供了很多新的API。以下爲兩個比較重要的API:
- Local(本地) - 簡化了日期時間的處理,沒有時區的問題。
- Zoned(時區) - 通過制定的時區處理日期時間。
新的java.time包涵蓋了所有處理日期,時間,日期/時間,時區,時刻(instants),過程(during),與時鐘(clock)的操作。
1.本地化日期時間 API
LocalDate/LocalTime和LocalDateTime類可以在處理時區不是必須的情況。代碼如下
public class Java8Tester{
public static void main(String args[]){
Java8Tester java8Tester = new Java8Tester();
java8Tester.testLocalDateTime();
}
public void testLocalDateTime(){
//獲取當前日期時間
LocalDateTime currentTime = LocalDateTime.now();
System.out.println("當前時間:"+currentTime);
LocalDate date1 = currentTime.toLocalDate();
System.out.println("date1: "+date1);
Month month = currentTime.getMonth();
int day = currentTime.getDayOfMonth();
int senconds = currentTime.getSecond();
System.out.println("月:"+ month + ",日:" + day + ",秒:" + senconds);
//指定年日 2019/09/10
LocalDateTime date2 = currentTime.withDayOfMonth(10).withYear(2019);
System.out.println("date2: "+ date2);
//指定年月日 2019-11-10
LocalDate date3 = LocalDate.of(2019,Month.NOVEMBER,10);
System.out.println("date3: "+ date3);
//22時15分鐘
LocalTime date4 = LocalTime.of(22,10);
System.out.println("date4: "+ date4);
//解析字符串
LocalTime date5 = LocalTime.parse("20:15:30");
System.out.println(date5);
}
}
執行以上腳本,輸出結果爲:
當前時間: 2018-06-08T15:19:16.910
date1:2018-06-08
月: JUNE, 日: 8, 秒: 16
date2:2012-06-10T15:19:16.910
date3:2014-12-12
date4:22:15
date5:20:15:30
2 使用時區的日期時間API
如果我們需要考慮到時區,就可以使用時區的日期時間API:
public class Java8Tester {
public static void main(String args[]) {
Java8Tester java8Tester = new Java8Tester();
java8Tester.testZonedDateTime();
}
public void testZonedDateTime() {
// 獲取當前時間日期
ZonedDateTime date1 = ZonedDateTime.parse("2019-12-03T10:15:30+05:30[Asia/Shanghai]");
System.out.println("date1: " + date1);
ZoneId id = ZoneId.of("Europe/Paris");
System.out.println("ZoneId: " + id);
ZoneId currentZone = ZoneId.systemDefault();
System.out.println("當期時區: " + currentZone);
}
}
執行以上腳本,輸出結果爲:
date1:2015-12-03T10:15:30+08:00[Asia/Shanghai]
ZoneId:Europe/Paris
當期時區: Asia/Shanghai
3.常用代碼
LocalDate today = LocalDate.now();
System.out.println("今天的日期是:"+today);
System.out.println("-----------------------------");
int year = today.getYear();
int month = today.getMonthValue();
int day = today.getDayOfMonth();
System.out.println("年:"+year+",月:"+month+",日:"+day);
System.out.println("-----------------------------");
LocalDate birthday = LocalDate.of(2011, 11, 11);
System.out.println("特定日期:"+birthday);
System.out.println("-----------------------------");
System.out.println("今天的日期是2011-11-11嗎?"+today.equals(birthday));
System.out.println("-----------------------------");
LocalDate dayofbirth = LocalDate.of(2008, 11, 20);
LocalDate tday = LocalDate.now();
// 獲取生日的月、日
MonthDay birthMonthDay = MonthDay.of(dayofbirth.getMonth(), dayofbirth.getDayOfMonth());
MonthDay currentMonthDay = MonthDay.from(tday);
if (currentMonthDay.equals(birthMonthDay)) {
System.out.println("今天是你的生日");
}else{
System.out.println("對不起,今天不是你的生日");
}
System.out.println("-----------------------------");
LocalTime localTime = LocalTime.now();
System.out.println("現在的時間是"+localTime);
System.out.println("-----------------------------");
LocalTime twoHourLaterTime = localTime.plusHours(2);
System.out.println("當前時間兩小時後的時間是"+twoHourLaterTime);
System.out.println("-----------------------------");
LocalDate oneWeekLaterDate = today.plus(1,ChronoUnit.WEEKS);
System.out.println("兩週後的日期是:"+oneWeekLaterDate);
System.out.println("-----------------------------");
LocalDate oneYearBeforeDate = today.minus(1, ChronoUnit.YEARS);
System.out.println("一年前的日期是:"+oneYearBeforeDate);
LocalDate oneYearLaterDate = today.plus(1, ChronoUnit.YEARS);
System.out.println("一年後的日期是:"+oneYearLaterDate);
System.out.println("-----------------------------");
Clock clock = Clock.systemUTC();
System.out.println("Clock:"+clock);
Clock.systemDefaultZone();
System.out.println("Clock:"+clock.millis());
System.out.println("-----------------------------");
LocalDate day1 = LocalDate.of(2011, 12, 15);
LocalDate day2 = LocalDate.of(2011, 9, 17);
System.out.println("day1是否在day2之後:"+day1.isAfter(day2));
System.out.println("day1是否在day2之前:"+day1.isBefore(day2));
System.out.println("-----------------------------");
LocalDateTime todaytime = LocalDateTime.now();
System.out.println("當前的日期時間:"+todaytime);
ZoneId zone = ZoneId.of(ZoneId.SHORT_IDS.get("ACT"));
ZonedDateTime dateandtimeinNewYork = ZonedDateTime.of(todaytime, zone);
System.out.println("現在時區的時間在特定時區的時間:"+dateandtimeinNewYork);
System.out.println("-----------------------------");
YearMonth currentYearMonth = YearMonth.now();
System.out.println("今年的當前月"+currentYearMonth+"有"+currentYearMonth.lengthOfMonth()+"天");
YearMonth creditCardExpiry = YearMonth.of(2018, Month.FEBRUARY);
System.out.println("您輸入的年月日期是:"+creditCardExpiry);
System.out.println("-----------------------------");
System.out.println("今年"+today+"是否是閏年:"+today.isLeapYear());
System.out.println("-----------------------------");
LocalDate day3 = LocalDate.of(2002, 12, 10);
LocalDate day4 = LocalDate.of(2001, 9, 12);
Period period = Period.between(day3, day4);
System.out.println(day3+"和"+day4+"之間相差"+period.getMonths()+"月");
System.out.println("-----------------------------");
LocalDateTime datetime = LocalDateTime.of(2016, Month.APRIL, 14, 14, 02, 24);
ZoneOffset offset = ZoneOffset.of("+05:30");
OffsetDateTime offsetdatetime = OffsetDateTime.of(datetime, offset);
System.out.println("日期和時間在時區上的偏移時間:"+offsetdatetime);
System.out.println("-----------------------------");
Instant timestamp = Instant.now();
System.out.println("當前時間戳:"+timestamp);
System.out.println("-----------------------------");
String dayaftertommrow = "20160205";
LocalDate formatdate = LocalDate.parse(dayaftertommrow,DateTimeFormatter.BASIC_ISO_DATE);
System.out.println(dayaftertommrow+"格式化後的日期是"+formatdate);
System.out.println("-----------------------------");
String goodFriday = "04 14 2016";
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("MM dd yyyy");
LocalDate holiday = LocalDate.parse(goodFriday,dateTimeFormatter);
System.out.println(goodFriday+"自定義格式化後的日期是"+holiday);
System.out.println("-----------------------------");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM dd yyyy HH:mm a");
String datestr = todaytime.format(formatter);
System.out.println("自定義格式化後的當前日期時間是"+datestr);
4.注意點
-
Instant 它代表的是時間戳,比如2016-04-14T14:20:13.592Z,這可以從java.time.Clock類中獲取,像這樣: Instant current = Clock.system(ZoneId.of(“Asia/Tokyo”)).instant();
-
LocalDate 它表示的是不帶時間的日期,比如2016-04-14。它可以用來存儲生日,週年紀念日,入職日期等。
-
LocalTime - 它表示的是不帶日期的時間
-
LocalDateTime - 它包含了時間與日期,不過沒有帶時區的偏移量
-
ZonedDateTime - 這是一個帶時區的完整時間,它根據UTC/格林威治時間來進行時區調整
-
這個庫的主包是java.time,裏面包含了代表日期,時間,瞬時以及持續時間的類。它有兩個子package,一個是java.time.foramt,這個是什麼用途就很明顯了,還有一個是java.time.temporal,它能從更低層面對各個字段進行訪問。
-
時區指的是地球上共享同一標準時間的地區。每個時區都有一個唯一標識符,同時還有一個地區/城市(Asia/Tokyo)的格式以及從格林威治時間開始的一個偏移時間。比如說,東京的偏移時間就是+09:00。
-
OffsetDateTime類實際上包含了LocalDateTime與ZoneOffset。它用來表示一個包含格林威治時間偏移量(+/-小時:分,比如+06:00或者 -08:00)的完整的日期(年月日)及時間(時分秒,納秒)。
-
DateTimeFormatter類用於在Java中進行日期的格式化與解析。與SimpleDateFormat不同,它是不可變且線程安全的,如果需要的話,可以賦值給一個靜態變量。DateTimeFormatter類提供了許多預定義的格式器,你也可以自定義自己想要的格式。當然了,根據約定,它還有一個parse()方法是用於將字符串轉換成日期的,如果轉換期間出現任何錯誤,它會拋出DateTimeParseException異常。類似的,DateFormatter類也有一個用於格式化日期的format()方法,它出錯的話則會拋出DateTimeException異常。
-
再說一句,“MMM d yyyy”與“MMm dd yyyy”這兩個日期格式也略有不同,前者能識別出”Jan 2 2014”與”Jan 14 2014”這兩個串,而後者如果傳進來的是”Jan 2 2014”則會報錯,因爲它期望月份處傳進來的是兩個字符。爲了解決這個問題,在天爲個位數的情況下,你得在前面補0,比如”Jan 2 2014”應該改爲”Jan 02 2014”。