Java8 time

5,428 views
5,155 views

Published on

Published in: Technology

Java8 time

  1. 1. java.time.* @kojilin
 2014/03/29@TWJUG
  2. 2. Java的Data和Time •java.util.Date •Since 1.0 •java.util.Calendar •Since 1.1 •java.time.* •Since 1.8 •為取代而生
  3. 3. Date •雖然叫 Date, 但實際上是 timestamp, #toString 又帶有 time zone •存取麻煩 •年是1900 + ? •月份從 0 開始 •不支援國際化 •Unix time
  4. 4. Calendar •為了國際化而導入 •雖然叫 Calendar, 但實際上是 Date 和 Time •建立和編輯日期與時間 •存取和運算比 java.util.Date 稍好一點,但仍 不敷使用
  5. 5. Calendar.Builder Calendar cal1 = new Calendar.Builder()
 .setDate(2014, Calendar.MARCH, 29)
 .build(); •Java 8 新增,讓建立和變更能方便一點 Calendar cal2 = new Calendar.Builder()
 .setCalendarType("japanese") 
 .setFiled(YEAR, 1, DAY_OF_YEAR, 1)
 .build();
  6. 6. java.time.* •JSR-310 •為了 JDK 設計 •從 Joda-Time 啓發和演化 •ISO 8601 為基礎 •Immutable •Type-safe •考慮到 XML 和 資料庫
  7. 7. ISO-8601 •國際標準化組織的國際標準ISO 8601是日期 和時間的表示方法 •hh:mm:ss.s •YYYY-MM-DDThh:mm:ss •nYnMnD •Gregorian Calendar (1582)
  8. 8. ThreeTen backport project •https://github.com/ThreeTen/ threetenbp •JSR-310 backport to JDK 7 •最終會有一個釋出會和 JDK 8 正式版本相同
  9. 9. 以人的角度看時間? vs 以機器的角度看時間?
  10. 10. 機器 •Instant •從 Java Epoch 開始經過的時間 •1970-01-01T00:00:00Z •支援到奈秒 •Duration •兩個 Instant 之間的差距 •Clock •模擬系統時鐘
  11. 11. Instant Instant now = Instant.now(); //2014-03-29T14:23:25.223Z
 now.toString(); //2014-03-29T14:23:28.223Z 
 now.plusSeconds(3); //2014-03-27T14:23:25.223Z
 now.minus(2, ChronoUnit.DAYS);
  12. 12. Duration •顯示時間的量 •使用 seconds 和 nanoseconds 表示 •toString 結果會是 PTnHnMnS •顯示時可以用 hours,minutes,seconds 和 nanoseconds •用 of 建立
 Duration.ofHours(2); Duration.ofDays(1);
  13. 13. •從兩個 Temporal 取得 ! 
 •有 Day 相關的方法,但 邊的 Day 就是 24 Hours 的意思 // 兩個參數都必須支援 ChronoUnit.SECONDS
 Duration.between(LocalDateTime.now(), 
 LocalDateTime.now().plusDays(2)); ! ! ! // 現在時間加 24
 LocalDateTime.now().plus(
 Duration.ofDays(1));
  14. 14. 人 •出生於 1983/12/11 •2月3日生 •營業時間是 14:00 到 22:00 •兩天,三小時
  15. 15. 日期與時間 •LocalDate •LocalTime •LocalDateTime •OffsetTime •OffsetDateTime •ZonedDateTime
  16. 16. Local 系列 •沒有帶時差和時區的資 •LocalDate •12月5日生日 •LocalTime •14:00 開始營業 •LocalDateTime
  17. 17. LocalDate LocalDate now = LocalDate.now(); now.toString(); // 2014-03-29 now.getDayOfWeek(); // SATURDAY now.getDayOfMonth(); // 29 now.isLeapYear(); // false // 2014-04-10
 now.plusDays(12).toString();
  18. 18. LocalTime LocalTime now = LocalTime.now(); now.toString(); // 14:20:42.270 now.getHour(); // 14 now.getMinute(); // 20 LocalTime.MIDNIGHT; // 00:00
  19. 19. LocalDateTime LocalDateTime now = LocalDateTime.now(); // 2014-03-29T14:20:42.270
 now.toString(); now.getDayOfMonth(); // 29 now.getHour(); // 14
  20. 20. •LocalDate <=> LocalDateTime Local 之間的轉換 LocalDate localDate = 
 LocalDate.now(); // LocalDate to LocalDateTime
 LocalDateTime localDateTime = 
 localDate.atTime(14, 20, 20); // LocalDateTime to LocalDate
 localDate = 
 localDateTime.toLocalDate();
  21. 21. •LocalTime <=> LocalDateTime LocalTime localTime = 
 LocalTime.now(); // LocalTime to LocalDateTime
 LocalDateTime localDateTime = 
 localTime.atDate(
 LocalDate.of(2014, 3, 29)); // LocalDateTime to LocalTime
 localTime = 
 localDateTime.toLocalTime();
  22. 22. 時差和 time zone •OffsetTime •OffsetDateTime •ZonedDateTime •ZonedDateTime != OffsetDateTime •時差是固定的,Zoned 則會依照狀況變換 時差,例如:日光節約時間, 更改時差
  23. 23. ZonedDateTime dateTime = 
 ZonedDateTime.of( 
 LocalDate.of(1975, 3, 31),
 LocalTime.of(23, 0),
 ZoneId.of("Asia/Taipei")); ! //1975-03-31T23:00+08:00[Asia/Taipei]
 dateTime.toString(); //1975-04-01T01:00+09:00[Asia/Taipei]
 dateTime.plusHours(1); •台灣1975日光節約時間爲4月1日到9月30日
  24. 24. ZonedDateTime dateTime = 
 ZonedDateTime.of( 
 LocalDate.of(1979, 6, 30),
 LocalTime.of(23, 0),
 ZoneId.of("Asia/Taipei")); ! //1979-06-30T23:00+09:00[Asia/Taipei]
 dateTime.toString(); •台灣1979日光節約時間爲7月1日到9月30日
  25. 25. Time zone •ZoneOffset •-18:00 +18:00(-12:00 +14:00) •ZoneRules •切換的規則 •ZoneId •例如 Asia/Taipei, +8
  26. 26. Time zone data •tz database 有世界時區的分類和命名 •IANA Time Zone Database (TZDB) •$JDK_HOME/jre/lib/tzdb.dat •透過 ZoneRulesProvider 讀取 •一直有更新,所以記得要持續更新 JRE 或透 過 Oracle 提供的工具更新
  27. 27. ZoneRuleProvider •Service provider interface •可以在 META-INF/services 自己提供 •系統預設是 TzdbZoneRuleProvider •提供動態更新的方法 •帶來很多複雜的問題,例如:取消或減少暫存造 成效能問題,或是程式中拿不同規則來做運算 •通常不建議動態更新
  28. 28. public static boolean refresh() { boolean changed = false; for (ZoneRulesProvider provider : PROVIDERS) { changed |= provider.provideRefresh(); } return changed; } •ZoneRulesProvider.refresh
  29. 29. public static boolean refresh() { boolean changed = false; for (ZoneRulesProvider provider : PROVIDERS) { changed |= provider.provideRefresh(); } return changed; } •ZoneRulesProvider.refresh •回傳的 boolean 可以讓應用程式知道有沒有更新 •系統預設的 provider#provideRefresh 是回傳 false
  30. 30. DateTime 之間的轉換 •LocalDateTime, LocalDate, LocalTime •OffsetDateTime, OffsetTime •ZonedDateTime •補足需要的資 或拿掉多餘的資 就可以彼此 間轉換
  31. 31. DateTime 之間的轉換 •LocalDateTime, LocalDate, LocalTime •OffsetDateTime, OffsetTime •ZonedDateTime •補足需要的資 或拿掉多餘的資 就可以彼此 間轉換 ZonedDateTime zdt = LocalDateTime.now()
 .atZone(ZoneId.of(“Asia/Taipei”)); LocalDateTime ldt = zdt.toLocalDateTime();
  32. 32. Period •顯示時間的量 •使用 years, months 和 days 表示 •toString 結果會是 PnYnMnD •用 of 建立
 Period.ofWeeks(2);
  33. 33. // 兩個參數都是 LocalDate
 Period.between(localDate1, LocalDate.now()); ! ! // 會是1975-04-01T23:00+09:00[Asia/Taipei]
 // 如果是 Duration, 則會是加上 24 小時
 ZonedDateTime.of( 
 LocalDate.of(1975, 3, 31),
 LocalTime.of(23, 0),
 ZoneId.of("Asia/Taipei"))
 .plus(Period.ofDays(1)); •從兩個 LocalDate 取得 ! ! •用在 ZonedDateTime 時有考慮到 DST
  34. 34. Formatting •用 DateTimeFormatter 取代 SimpleDateFormat
 
 
 
 •Immutable - Thread safe •可以利用 DateTimeFormatterBuilder 建立新 格式 DateTimeFormatter dateTimeFormatter = 
 DateTimeFormatter.ofPattern("yyyy/MM/dd"); localDate.format(dateTimeFormatter); offset.format(dateTimeFormatter);
  35. 35. ISO_LOCAL_DATE = new DateTimeFormatterBuilder() .appendValue(YEAR, 4, 10, 
 SignStyle.EXCEEDS_PAD) .appendLiteral('-') .appendValue(MONTH_OF_YEAR, 2) .appendLiteral('-') .appendValue(DAY_OF_MONTH, 2) .toFormatter(ResolverStyle.STRICT, 
 IsoChronology.INSTANCE); ! LocalDate ld = LocalDate.parse("2011-12-03",
 ISO_LOCAL_DATE);
  36. 36. Parsing •Instant, Date 和 Time 類別都用一樣方式 parse
 
 •Period, Duration 用 ISO-8601格式 Instant.parse("…", dateTimeFormatter); LocalDate.parse("…", dateTimeFormatter); //1 Year 2 months 3days
 Period.parse("P1Y2M3D"); //2 days 3 hours 2.21 seconds
 Duration.parse("P2DT3H2.21S");
  37. 37. •有些格式變得比較嚴格 DateTimeFormatter formatter = 
 DateTimeFormatter.ofPattern(
 "yyyy/MM/dd"); //ok
 LocalDate.parse("2014/03/02", formatter); // error, DateTimeParseException
 //LocalDate.parse("2014/3/2", formatter); //ok
 new SimpleDateFormat("yyyy/MM/dd")
 .parse("2014/3/2");
  38. 38. Clock •透過 Inject 便於測試
 
 
 
 
 •可以建立多種類型的 Clock •讓測試可以獨立於 time zone private Clock clock; // dependency inject public void process(LocalDate event) { if(event.isBefore(LocalDate.now(clock)){
 ...
  39. 39. //使用系統
 Clock.systemDefaultZone(); //使用指定 ZoneId
 Clock.system(ZoneId.of("Asia/Tokyo")); //固定時間
 Instant instant = … ; Clock.fixed(instant, 
 ZoneIf.of("Asia/Taipei")); //使用 30秒 tick 的時鐘
 Clock.tick(Clock.systemDefault(), 
 Duration.ofSeconds(30));
  40. 40. 其他表示方式 •Year •Month •YearMonth •MonthDay •DayOfWeek
  41. 41. 現在 LocalTime.now(); LocalTime.now(Clock.fixed(...)); LocalDate.now(); LocalDateTime.now(); OffsetDateTime.now();
  42. 42. of •Static factory method LocalDate.of(2014, 3, 29); LocalTime.of(14, 5, 25); Year.of(-1);
  43. 43. at •Combines this object with another LocalDate.of(2014, 3, 29).atTime(
 LocalTime.of(14, 5, 25));
  44. 44. parse •Static factory method focussed on parsing LocalDate.parse("2014-03-29"); LocalTime.parse("14:23:20");
  45. 45. with •The immutable equivalent of a setter LocalDate.now().withMonth(4); LocalTime.now().withMinute(30);
  46. 46. plus, minus •Adds/Subtracts an amount to an object LocalDate.now().plusDays(4)
 .plusYear(2); LocalTime.now().plusMinutes(30)
 .plusHours(4)
 .minusSeconds(30);

  47. 47. isBefore, isAfter •Check order LocalDate.now().isBefore(
 LocalDate.of(2014, 12, 3)); LocalTime.now().isAfter(
 LocalTime.of(4, 12, 31));
  48. 48. until, between •Amount of time until another LocalDate.of(2014, 12, 3).until(
 LocalDate.of(2014, 12, 7),
 ChronoUnit.WEEKS); ChronoUnit.DAYS.between(
 LocalDate.of(2014, 12, 3), 
 LocalDate.of(2014, 12, 7));
  49. 49. Chronology •Chronology •曆 •Era •紀年 •ChronoLocalDate •ChronoLocalDateTime •ChronoZonedDateTime
  50. 50. 民國年 •MinguoDate ! ! •和 ISO-8601 之間轉換 //Minguo ROC 103-03-29
 MinguoDate date = 
 MinguoDate.of(103, 3, 29); LocalDate.from(
 MinguoDate.of(103, 3, 29));
 
 MinguoDate.from(
 LocalDate.now());
  51. 51. 與 Date 的轉換 •LocalDate 和 LocalTime 本身沒有 Instant 值,所以必須要先轉換成能取得 Instant •從 LocalDateTime 到 Date LocalDate now = LocalDate.now(); Instant instant = 
 now.atZone(ZoneId.systemDefault())
 .toInstant(); Date date = Date.from(instant);
  52. 52. •從 Date 到 LocalDateTime Date date = new Date(); Instant instant = date.toInstant(); LocalDateTime = 
 LocalDateTime.ofInstant(
 instant, 
 ZoneId.systemDefault());
  53. 53. 可擴充的介面設計 •TemporalAccessor •Temporal •TemporalField •TemporalAmount •TemporalUnit •TemporalQuery •TemporalAdjuster •可讀取的 Date&Time •可修改的 Date&Time •Date&Time 的 filed •時間的量 •Date&Time 的單位 •查詢 Date&Time •修改 Date&Time
  54. 54. TemporalQuery @FunctionalInterface public interface TemporalQuery<R>{ R queryFrom(TemporalAccessor 
 temporal); }
  55. 55. //Days
 LocalDate.of(2014, 3, 29)
 .query(TemporalQueries.precision()); //2014-03-29T16:30:26.043
 ZonedDateTime.now
 .query(LocalDateTime::from); //SATURDAY
 LocalDate.of(2014, 3, 29)
 .query(DayOfWeek::from);
  56. 56. TemporalAdjuster @FunctionalInterface public interface TemporalAdjuster{ Temporal adjustInfo(Temporal 
 temporal); }
  57. 57. //2014-03-31
 LocalDate.of(2014, 3, 29)
 .with(TemporalAdjusters.lastDayOfMondth()); //2014-04-05
 LocalDate.of(2014, 3, 29)
 .with(TemporalAdjusters.next(
 DayOfWeek.SATURDAY)); //2014-01-02
 LocalDate.now().with(t -> t.with(ChronoField
 .DAY_OF_YEAR, 2));
  58. 58. JDBC •LocalDate •LocalTime •LocalDateTime
 •OffsetTime •OffsetDateTime •DATE •TIME WITHOUT TIME ZONE •TIMESTAMP WITHOUT TIME ZONE •TIME WITH TIME ZONE •TIMESTAMP WITH TIMZ ZONE
  59. 59. LocalDateTime << SQL ResultSet rs = …; … java.sql.Time time = rs.getTime(…); LocalTime lt = time.toLocalTime(); java.sql.Date date = rs.getDate(…); LocalDate ld = date.toLocalDate(); java.sql.Timestamp ts = rs.getTimeStamp(…); LocalDateTime ldt = 
 ts.toLocalDateTime();
  60. 60. LocalDateTime >> SQL PreparedStatement pstmt = …; pstmt.setTime(1, 
 Time.valueOf(LocalTime.now())); pstmt.setDate(2, 
 Date.valueOf(LocalDate.now())); pstmt.setTimestamp(3, 
 Timestamp.valueOf(LocalDateTime.now()));
  61. 61. 總結 JSR 310 的好處 •良好的命名和 API 設計 •日期和時間的分別處理 •Immutable •精確到奈秒 •多樣化的操作 •存取各種 filed 方便

×