前言
最近看別人項目源碼,發現Java8新的日期時間API很方便強大,因此轉載該入門介紹博客,記錄一下。java
使用新時間日期API的必要性
在java8之前,或許:安全
- 當你在作有關時間日期的操做時,你會想到用Date;
- 當你在作日期、月份、天數相加減時,你會想到用Calendar;
- 當你須要對時間日期進行格式化時,你會想到使用SimpleDateFormat或DateFormat下的其餘子類;
……
可是,你必須知道,以上有關的時間日期操做對象,都是可變的、線程不安全的,同時,若是做爲一個常常寫過相似代碼的人來講,儘管有相關對象提供某些操做,但並不能很快、很簡單的就能獲得最終想要的結果,如:要計算兩個時間點之間相差的年、月、日、周、時、分、秒等,這些計算儘管原有API也可以實現,但原有API除了線程不安全以外,另一個不足之處就是代碼繁瑣,性能低!多線程
爲什麼咱們總提多線程下,線程不安全?對於初學者來講,可能以爲可以簡單實現出功能就已經足夠,可是真正的開發項目是不可能僅僅考慮功能的實現的,還要考慮項目的安全性、穩定性、高性能、高可用性等等!所以,做爲java開發者,多線程的知識是必不可少的。而也正由於多線程,纔會出現一大堆問題(簡稱線程安全性問題),做爲開發者,就應該寫出不只能實現功能的代碼,還要是線程安全的代碼。那麼,學習並熟悉掌握新的線程安全的API就顯得很是重要了!ide
沒錯,java8出的新的時間日期API都是線程安全的,而且性能更好,代碼更簡潔!性能
新時間日期API經常使用、重要對象介紹
- ZoneId: 時區ID,用來肯定Instant和LocalDateTime互相轉換的規則
- Instant: 用來表示時間線上的一個點(瞬時)
- LocalDate: 表示沒有時區的日期, LocalDate是不可變而且線程安全的
- LocalTime: 表示沒有時區的時間, LocalTime是不可變而且線程安全的
- LocalDateTime: 表示沒有時區的日期時間, LocalDateTime是不可變而且線程安全的
- Clock: 用於訪問當前時刻、日期、時間,用到時區
- Duration: 用秒和納秒錶示時間的數量(長短),用於計算兩個日期的「時間」間隔
- Period: 用於計算兩個「日期」間隔
其中,LocalDate、LocalTime、LocalDateTime是新API裏的基礎對象,絕大多數操做都是圍繞這幾個對象來進行的,有必要搞清楚:學習
LocalDate : 只含年月日的日期對象
LocalTime :只含時分秒的時間對象
LocalDateTime : 同時含有年月日時分秒的日期對象spa
本文將以實例講解平常開發中經常使用到的時間日期操做,如:線程
獲取當前日期、時間
指定時間日期建立對應的對象
計算兩個時間點的間隔
判斷兩個時間的先後
時間日期的格式化
獲取時間戳
時間、日期相加減
獲取給定時間點的年份、月份、周、星期等
……code
新時間日期API詳解與示例
獲取當前時間
LocalDate localDate = LocalDate.now(); LocalTime localTime = LocalTime.now(); LocalDateTime localDateTime = LocalDateTime.now(); System.out.println(localDate); System.out.println(localTime); System.out.println(localDateTime);
運行結果:orm
根據指定日期/時間建立對象
LocalDate localDate = LocalDate.of(2018, 1, 13); LocalTime localTime = LocalTime.of(9, 43, 20); LocalDateTime localDateTime = LocalDateTime.of(2018, 1, 13, 9, 43, 20); System.out.println(localDate); System.out.println(localTime); System.out.println(localDateTime);
運行結果:
日期時間的加減
- 對於LocalDate,只有精度大於或等於日的加減,如年、月、日;
- 對於LocalTime,只有精度小於或等於時的加減,如時、分、秒、納秒;
- 對於LocalDateTime,則能夠進行任意精度的時間相加減;
LocalDateTime localDateTime = LocalDateTime.now(); //如下方法的參數都是long型,返回值都是LocalDateTime LocalDateTime plusYearsResult = localDateTime.plusYears(2L); LocalDateTime plusMonthsResult = localDateTime.plusMonths(3L); LocalDateTime plusDaysResult = localDateTime.plusDays(7L); LocalDateTime plusHoursResult = localDateTime.plusHours(2L); LocalDateTime plusMinutesResult = localDateTime.plusMinutes(10L); LocalDateTime plusSecondsResult = localDateTime.plusSeconds(10L); System.out.println("當前時間是 : " + localDateTime + "\n" + "當前時間加2年後爲 : " + plusYearsResult + "\n" + "當前時間加3個月後爲 : " + plusMonthsResult + "\n" + "當前時間加7往後爲 : " + plusDaysResult + "\n" + "當前時間加2小時後爲 : " + plusHoursResult + "\n" + "當前時間加10分鐘後爲 : " + plusMinutesResult + "\n" + "當前時間加10秒後爲 : " + plusSecondsResult + "\n" ); //也能夠以另外一種方式來相加減日期,即plus(long amountToAdd, TemporalUnit unit) // 參數1 : 相加的數量, 參數2 : 相加的單位 LocalDateTime nextMonth = localDateTime.plus(1, ChronoUnit.MONTHS); LocalDateTime nextYear = localDateTime.plus(1, ChronoUnit.YEARS); LocalDateTime nextWeek = localDateTime.plus(1, ChronoUnit.WEEKS); System.out.println("now : " + localDateTime + "\n" + "nextYear : " + nextYear + "\n" + "nextMonth : " + nextMonth + "\n" + "nextWeek :" + nextWeek + "\n" ); //日期的減法用法同樣,在此再也不舉例
運行結果:
將年、月、日等修改成指定的值,並返回新的日期(時間)對象
析: 其效果與時間日期相加減差很少,現在天是2018-01-13,要想變爲2018-01-20有兩種方式
a. localDate.plusDays(20L) -> 相加指定的天數
b. localDate.withDayOfYear(20) -> 直接指定到哪一天
LocalDate localDate = LocalDate.now(); //當前時間基礎上,指定本年當中的第幾天,取值範圍爲1-365,366 LocalDate withDayOfYearResult = localDate.withDayOfYear(200); //當前時間基礎上,指定本月當中的第幾天,取值範圍爲1-29,30,31 LocalDate withDayOfMonthResult = localDate.withDayOfMonth(5); //當前時間基礎上,直接指定年份 LocalDate withYearResult = localDate.withYear(2017); //當前時間基礎上,直接指定月份 LocalDate withMonthResult = localDate.withMonth(5); System.out.println("當前時間是 : " + localDate + "\n" + "指定本年當中的第200天 : " + withDayOfYearResult + "\n" + "指定本月當中的第5天 : " + withDayOfMonthResult + "\n" + "直接指定年份爲2017 : " + withYearResult + "\n" + "直接指定月份爲5月 : " + withMonthResult + "\n" );
運行結果:
獲取日期的年月日周時分秒
LocalDateTime localDateTime = LocalDateTime.now(); int dayOfYear = localDateTime.getDayOfYear(); int dayOfMonth = localDateTime.getDayOfMonth(); DayOfWeek dayOfWeek = localDateTime.getDayOfWeek(); System.out.println("今天是" + localDateTime + "\n" + "本年當中第" + dayOfYear + "天" + "\n" + "本月當中第" + dayOfMonth + "天" + "\n" + "本週中星期" + dayOfWeek.getValue() + "-即" + dayOfWeek + "\n"); //獲取當天時間的年月日時分秒 int year = localDateTime.getYear(); Month month = localDateTime.getMonth(); int day = localDateTime.getDayOfMonth(); int hour = localDateTime.getHour(); int minute = localDateTime.getMinute(); int second = localDateTime.getSecond(); System.out.println("今天是" + localDateTime + "\n" + "年 : " + year + "\n" + "月 : " + month.getValue() + "-即 "+ month + "\n" + "日 : " + day + "\n" + "時 : " + hour + "\n" + "分 : " + minute + "\n" + "秒 : " + second + "\n" );
運行結果:
時間日期先後的比較與判斷
//判斷兩個時間點的先後 LocalDate localDate1 = LocalDate.of(2017, 8, 8); LocalDate localDate2 = LocalDate.of(2018, 8, 8); boolean date1IsBeforeDate2 = localDate1.isBefore(localDate2); System.out.println("date1IsBeforeDate2 : " + date1IsBeforeDate2); // date1IsBeforeDate2 == true
判斷是否爲閏年
LocalDate now = LocalDate.now(); System.out.println("now : " + now + ", is leap year ? " + );
java8時鐘 : clock()
//返回當前時間,根據系統時間和UTC Clock clock = Clock.systemUTC(); // 運行結果: SystemClock[Z] System.out.println(clock);
時間戳
事實上Instant就是java8之前的Date,
可使用如下兩個類中的方法在這兩個類型之間進行轉換,
好比Date.from(Instant)就是用來把Instant轉換成java.util.date的,
而new Date().toInstant()就是將Date轉換成Instant的
Instant instant = Instant.now(); //2019-06-08T16:50:19.174Z System.out.println(instant); Date date = Date.from(instant); Instant instant2 = date.toInstant(); //Sun Jun 09 00:50:19 CST 2019 System.out.println(date); //2019-06-08T16:50:19.174Z System.out.println(instant2);
計算時間、日期間隔
Duration:用於計算兩個「時間」間隔
Period:用於計算兩個「日期」間隔
//計算兩個日期的日期間隔-年月日 LocalDate date1 = LocalDate.of(2018, 2, 13); LocalDate date2 = LocalDate.of(2017, 3, 12); //內部是用date2-date1,因此獲得的結果是負數 Period period = Period.between(date1, date2); System.out.println("相差年數 : " + period.getYears()); System.out.println("相差月數 : " + period.getMonths()); System.out.println("相差日數 : " + period.getDays()); //還能夠這樣獲取相差的年月日 System.out.println("-------------------------------"); long years = period.get(ChronoUnit.YEARS); long months = period.get(ChronoUnit.MONTHS); long days = period.get(ChronoUnit.DAYS); System.out.println("相差的年月日分別爲 : " + years + "," + months + "," + days); //注意,當獲取兩個日期的間隔時,並非單純的年月日對應的數字相加減,而是會先算出具體差多少天,在折算成相差幾年幾月幾日的 //計算兩個時間的間隔 System.out.println("-------------------------------"); LocalDateTime date3 = LocalDateTime.now(); LocalDateTime date4 = LocalDateTime.of(2018, 1, 13, 22, 30, 10); Duration duration = Duration.between(date3, date4); System.out.println(date3 + " 與 " + date4 + " 間隔 " + "\n" + " 天 :" + duration.toDays() + "\n" + " 時 :" + duration.toHours() + "\n" + " 分 :" + duration.toMinutes() + "\n" + " 毫秒 :" + duration.toMillis() + "\n" + " 納秒 :" + duration.toNanos() + "\n" ); //注意,並無得到秒差的,但既然能夠得到毫秒,秒就能夠自行獲取了
運行結果:
當計算程序的運行時間時,應當使用時間戳Instant
Instant ins1 = Instant.now(); for (int i = 0; i < 10000000; i++) { //循環一百萬次 } Instant ins2 = Instant.now(); Duration duration = Duration.between(ins1, ins2); System.out.println("程序運行耗時爲 : " + duration.toMillis() + "毫秒");
時間日期的格式化(格式化後返回的類型是String)
1. 使用jdk自身配置好的日期格式
//使用jdk自身配置好的日期格式 DateTimeFormatter formatter1 = DateTimeFormatter.ISO_DATE_TIME; LocalDateTime date1 = LocalDateTime.now(); //反過來調用也能夠 : date1.format(formatter1); String date1Str = formatter1.format(date1); System.out.println(date1Str);
運行結果:
2. 使用自定義格式
LocalDateTime date1 = LocalDateTime.now(); DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"); String date2Str = formatter2.format(date1); System.out.println(date2Str);
運行結果:
注:自定義轉化的格式必定要與日期類型對應
- LocalDate只能設置僅含年月日的格式
- LocalTime只能設置僅含時分秒的格式
- LocalDateTime能夠設置含年月日時分秒的格式
代碼以下:
DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd"); System.out.println(formatter3.format(LocalDate.now())); System.out.println("-------------------------------"); DateTimeFormatter formatter4 = DateTimeFormatter.ofPattern("HH:mm:ss"); System.out.println(formatter4.format(LocalTime.now()));
運行結果:
將時間字符串形式轉化爲日期對象
String datetime = "2018-01-13 21:27:30"; DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); LocalDateTime ldt = LocalDateTime.parse(datetime, dtf); System.out.println(ldt);
運行結果:
注:格式的寫法必須與字符串的形式同樣
2018-01-13 21:27:30 對應 yyyy-MM-dd HH:mm:ss
20180113213328 對應 yyyyMMddHHmmss
不然會報運行時異常!
但要記住:獲得的最終結果都是相似2018-01-13T21:27:30的格式
由於在輸出LocalDateTime對象時,會調用其重寫的toString方法。
@Override public String toString() { return date.toString() + 'T' + time.toString(); }
將時間日期對象轉爲格式化後的時間日期對象
//新的格式化API中,格式化後的結果都默認是String,有時咱們也須要返回通過格式化的同類型對象 LocalDateTime ldt1 = LocalDateTime.now(); DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); String temp = dtf1.format(ldt1); LocalDateTime formatedDateTime = LocalDateTime.parse(temp, dtf1); System.out.println(formatedDateTime);
運行結果:
long毫秒值轉換爲日期
System.out.println("---------long毫秒值轉換爲日期---------"); DateTimeFormatter df= DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); String longToDateTime = df.format(LocalDateTime.ofInstant( Instant.ofEpochMilli(System.currentTimeMillis()),ZoneId.of("Asia/Shanghai"))); System.out.println(longToDateTime);
運行結果: