文章來源:http://www.javashuo.com/article/p-gwqadnbt-kw.html
在Java 8以前的日期/時間API以前,現有的與日期和時間相關的類存在諸多問題,其中主要有:html
使用Calendar類實現日期和時間字段之間轉換 使用DateFormat類來格式化和分析日期字符串 而Date只用來承載日期和時間信息
瞬時時間(Instant),持續時間(duration),日期(date),時間(time),時區(time-zone)以及時間段(Period)。Java 8仍然延用了ISO的日曆體系,而且與它的前輩們不一樣,java.time包中的類是不可變且線程安全的。新的時間及日期API位於java.time包中,下面是裏面的一些關鍵的類:java
LocalDate 依然是一個不可變類,它關注時間中年月日部分sql
初始化實例 public static LocalDate now():截斷當前系統時間的年月日信息並初始化一個實例對象 public static LocalDate of(int year, int month, int dayOfMonth):顯式指定年月日信息 public static LocalDate ofYearDay(int year, int dayOfYear):根據 dayOfYear 能夠推出 month 和 dayOfMonth public static LocalDate ofEpochDay(long epochDay):相對於格林零時區時間的日偏移量 …………
示例api
// 取當前日期: LocalDate today = LocalDate.now(); // -> 2019-01-31 // 根據年月日取日期,12月就是12: LocalDate crischristmas = LocalDate.of(2018, 12, 25); // -> 2018-12-25 // 根據字符串取: LocalDate endOfFeb = LocalDate.parse("2018-12-25"); // 嚴格按照ISO yyyy-MM-dd驗證,02寫成2都不行,固然也有一個重載方法容許本身定義格式 // 如何獲取1周後的日期 LocalDate oneToday = today.plus(1, ChronoUnit.WEEKS); // ->2019-02-07 //一年前的日期 LocalDate previousYear = today.minus(1, ChronoUnit.YEARS); // 取本月第1天: LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMonth()); // 2019-01-01 // 取本月第2天: LocalDate secondDayOfThisMonth = today.withDayOfMonth(2); // 2019-01-02 // 取本月最後一天,不再用計算是28,29,30仍是31: LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth()); // 2019-01-31 // 取下一天: LocalDate firstDay = lastDayOfThisMonth.plusDays(1); // 變成了2019-02-01 // 取2019年1月第一個週一 LocalDate firstMonday = LocalDate.parse("2019-01-01").with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)); // 2019-01-07
在java8中,可使用MonthDay,該類不包含年份信息,固然還有一個類是YearMonth安全
LocalDate birthday = LocalDate.of(1990, 10, 12); MonthDay birthdayMd = MonthDay.of(birthday.getMonth(), birthday.getDayOfMonth()); MonthDay today = MonthDay.from(LocalDate.of(2019, 10, 12)); System.out.println(today.equals(birthdayMd)); //結果 true
可是有些時候咱們要面臨更復雜的時間操做,好比將時間調到下一個工做日,或者是下個月的最後一天,這時候咱們可使用with()方法的另外一個重載方法,它接收一個TemporalAdjuster參數,可使咱們更加靈活的調整日期:多線程
LocalDate date7 = date.with(nextOrSame(DayOfWeek.SUNDAY)); // 返回下一個距離當前時間最近的星期日 LocalDate date9 = date.with(lastInMonth(DayOfWeek.SATURDAY)); // 返回本月最後一個星期六
若是自己API不知足你的需求,你還能夠建立自定義的TemporalAdjuster接口的實現線程
相似於 LocalDate,LocalTime 專一於時間的處理,它提供小時,分鐘,秒,毫微秒的各類處理設計
初始化LocalTime實例 public static LocalTime now():根據系統當前時刻獲取其中的時間部份內容 public static LocalTime of(int hour, int minute):顯式傳入小時和分鐘來構建一個實例對象 public static LocalTime of(int hour, int minute, int second):經過傳入時分秒構造實例 public static LocalTime of(int hour, int minute, int second, int nanoOfSecond):傳入時分秒和毫微秒構建一個實例 public static LocalTime ofSecondOfDay(long secondOfDay):傳入一個長整型數值表明當前日已通過去的秒數 public static LocalTime ofNanoOfDay(long nanoOfDay):傳入一個長整型表明當前日已通過去的毫微秒數
示例unix
//包含毫秒 LocalTime now = LocalTime.now(); // 11:09:09.240 //不包含毫秒 LocalTime now = LocalTime.now().withNano(0)); // 11:09:09 //構造時間 LocalTime zero = LocalTime.of(0, 0, 0); // 00:00:00 LocalTime mid = LocalTime.parse("12:00:00"); // 12:00:00 LocalTime twoHour = now.plusHours(2);
LocalDateTime類是LocalDate和LocalTime的結合體,能夠經過of()方法直接建立,也能夠調用LocalDate的atTime()方法或LocalTime的atDate()方法將LocalDate或LocalTime合併成一個LocalDateTimecode
LocalDateTime ldt1 = LocalDateTime.of(2017, Month.JANUARY, 4, 17, 23, 52); LocalDate localDate = LocalDate.of(2017, Month.JANUARY, 4); LocalTime localTime = LocalTime.of(17, 23, 52); LocalDateTime ldt2 = localDate.atTime(localTime);
LocalDateTime也提供用於向LocalDate和LocalTime的轉化:
LocalDate date = ldt1.toLocalDate(); LocalTime time = ldt1.toLocalTime();
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方法建立:
Instant instant = Instant.ofEpochSecond(120, 100000);
關於時間差的計算,主要涉及到兩個類,年月日的日期間差值的計算使用 Period 類足以,而時分秒毫秒的時間的差值計算則須要使用Duration類。
LocalDateTime from = LocalDateTime.of(2019, Month.JANUARY, 5, 10, 7, 0); // 2019-01-05 10:07:00 LocalDateTime to = LocalDateTime.of(2019, Month.FEBRUARY, 5, 10, 7, 0); // 2019-02-05 10:07:00 Duration duration = Duration.between(from, to); // 表示從 2019-01-05 10:07:00 到 2019-02-05 10:07:00 這段時間 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對象還能夠經過of()方法建立,該方法接受一個時間段長度,和一個時間單位做爲參數:
Duration duration1 = Duration.of(5, ChronoUnit.DAYS); // 5天 Duration duration2 = Duration.of(1000, ChronoUnit.MILLIS); // 1000毫秒
Period在概念上和Duration相似,區別在於Period是以年月日來衡量一個時間段,好比2年3個月6天
Period period = Period.of(2, 3, 6);
Period對象也能夠經過between()方法建立,值得注意的是,因爲Period是以年月日衡量時間段,因此between()方法只能接收LocalDate類型的參數:
Period period = Period.between( LocalDate.of(2019, 1, 5), LocalDate.of(2019, 2, 5));
示例
LocalDate date = LocalDate.of(2019,01,22); LocalDate date1 = LocalDate.now(); Period period = Period.between(date,date1); System.out.println(period.getYears() + "年" + period.getMonths() + "月" + period.getDays() + "天"); LocalTime time = LocalTime.of(20,30); LocalTime time1 = LocalTime.of(23,59); Duration duration = Duration.between(time,time1); System.out.println(duration.toMinutes() + "分鐘");
不管是咱們的 LocalDate,或是 LocalTime,甚至是 LocalDateTime,它們基本是時區無關的,內部並無存儲時區屬性,而基本用的系統默認時區。每每有些場景之下,缺少必定的靈活性。
ZonedDateTime 能夠被理解爲 LocalDateTime 的外層封裝,它的內部存儲了一個 LocalDateTime 的實例,專門用於普通的日期時間處理。此外,它還定義了 ZoneId 和 ZoneOffset 來描述時區的概念。
ZonedDateTime 和 LocalDateTime 的一個很大的不一樣點在於,後者內部並無存儲時區,因此對於系統的依賴性很強,每每換一個時區可能就會致使程序中的日期時間不一致。
然後者則能夠經過傳入時區的名稱,使用 ZoneId 進行匹配存儲,也能夠經過傳入與零時區的偏移量,使用 ZoneOffset 存儲時區信息。
初始化實例 public static ZonedDateTime now():系統將以默認時區計算並存儲日期時間信息 public static ZonedDateTime now(ZoneId zone):指定時區 public static ZonedDateTime of(LocalDate date, LocalTime time, ZoneId zone):指定日期時間和時區 public static ZonedDateTime of(LocalDateTime localDateTime, ZoneId zone) public static ZonedDateTime ofInstant(Instant instant, ZoneId zone):經過時刻和時區構建實例對象 等等
示例
ZonedDateTime zonedDateTime = ZonedDateTime.now(); System.out.println(zonedDateTime); //->2019-01-31T16:27:23.179+08:00[Asia/Shanghai] LocalDateTime localDateTime = LocalDateTime.now(); ZoneId zoneId = ZoneId.of("America/Los_Angeles"); ZonedDateTime zonedDateTime1 = ZonedDateTime.of(localDateTime,zoneId); System.out.println(zonedDateTime1); // ->2019-01-31T16:27:23.179-08:00[America/Los_Angeles] Instant instant = Instant.now(); ZoneId zoneId1 = ZoneId.of("GMT"); ZonedDateTime zonedDateTime2 = ZonedDateTime.ofInstant(instant,zoneId1); System.out.println(zonedDateTime2); // ->2019-01-31T08:27:23.183Z[GMT]
第二個小例子,LocalDateTime 實例保存了時區無關的當前日期時間信息,也就是這裏的年月日時分秒,接着構建一個 ZonedDateTime 實例並傳入一個美國時區(西七區)。你會發現輸出的日期時間爲西七區的 16 點 27 分。
像這種關聯了時區的日期時間就很可以解決那種,換時區致使程序中時間錯亂的問題。由於我關聯了時區,不管你程序換到什麼地方運行了,日期+時區 本就已經惟一肯定了某個時刻,就至關於我在存儲某個時刻的時候,說明了這是某某時區的某某時間,即使你換了一個地區,也不至於把這個時間按本身當前的時區進行解析並直接使用。
第三個小例子,構建 ZonedDateTime實例的時候,給定一個時刻和一個時區,而這個時刻值就是相對於給定時區的標準時間所通過的毫秒數。
有關 ZonedDateTime 的其餘日期時間的處理方法和 LocalDateTime 是同樣的,由於 ZonedDateTime 是直接封裝了一個 LocalDateTime 實例對象,因此全部相關日期時間的操做都會間接的調用 LocalDateTime 實例的方法,咱們再也不贅述。
Java 8 的新式日期時間 API 中,DateTimeFormatter 做爲格式化日期時間的主要類,它與以前的 DateFormat 類最大的不一樣就在於它是線程安全的,若是須要的話,能夠賦值給一個靜態變量。
DateTimeFormatter類提供了許多預約義的格式器,你也能夠自定義本身想要的格式。固然根據約定,它還有一個parse()方法是用於將字符串轉換成日期的,若是轉換期間出現任何錯誤,它會拋出DateTimeParseException異常。相似的,DateFormatter類也有一個用於格式化日期的format()方法,它出錯的話則會拋出DateTimeException異常
再說一句,「MMM d yyyy」與「MMm dd yyyy」這兩個日期格式也略有不一樣,前者能識別出"Jan 2 2018"與"Jan 14 2018"這兩個串,然後者若是傳進來的是"Jan 2 2018"則會報錯,由於它指望月份處傳進來的是兩個字符。爲了解決這個問題,在天爲個位數的狀況下,你得在前面補0,好比"Jan 2 2018"應該改成"Jan 02 2018"。
public static void main(String[] a){ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"); LocalDateTime localDateTime = LocalDateTime.now(); System.out.println(formatter.format(localDateTime)); String str = "2008年08月23日 23:59:59"; DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"); LocalDateTime localDateTime2 = LocalDateTime.parse(str,formatter2); System.out.println(localDateTime2); }
由於java8以前Date是包含日期和時間的,而LocalDate只包含日期,LocalTime只包含時間,因此與Date在互轉中,勢必會丟失日期或者時間,或者會使用起始時間。若是轉LocalDateTime,那麼就不存在信息偏差。
/Date與Instant的相互轉化 Instant instant = Instant.now(); Date date = Date.from(instant); Instant instant2 = date.toInstant(); //Date轉爲LocalDateTime Date date2 = new Date(); LocalDateTime localDateTime2 = LocalDateTime.ofInstant(date2.toInstant(), ZoneId.systemDefault()); //LocalDateTime轉Date LocalDateTime localDateTime3 = LocalDateTime.now(); Instant instant3 = localDateTime3.atZone(ZoneId.systemDefault()).toInstant(); Date date3 = Date.from(instant); //LocalDate轉Date //由於LocalDate不包含時間,因此轉Date時,會默認轉爲當天的起始時間,00:00:00 LocalDate localDate4 = LocalDate.now(); Instant instant4 = localDate4.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant(); Date date4 = Date.from(instant); // Calendar to Instant Instant time = Calendar.getInstance().toInstant(); System.out.println(time); // TimeZone to ZoneId ZoneId defaultZone = TimeZone.getDefault().toZoneId(); System.out.println(defaultZone); // ZonedDateTime from specific Calendar ZonedDateTime gregorianCalendarDateTime = new GregorianCalendar().toZonedDateTime(); System.out.println(gregorianCalendarDateTime); GregorianCalendar gc = GregorianCalendar.from(gregorianCalendarDateTime); System.out.println(gc);