java8 time

61
java.time.* @kojilin 2014/03/29@TWJUG

Upload: koji-lin

Post on 25-May-2015

4.846 views

Category:

Technology


5 download

TRANSCRIPT

Page 1: Java8 time

java.time.*@kojilin

2014/03/29@TWJUG

Page 2: Java8 time

Java的Data和Time•java.util.Date

•Since 1.0

•java.util.Calendar

•Since 1.1

•java.time.*

•Since 1.8

•為取代而生

Page 3: Java8 time

Date•雖然叫 Date, 但實際上是 timestamp,

#toString 又帶有 time zone

•存取麻煩

•年是1900 + ?

•月份從 0 開始

•不支援國際化

•Unix time

Page 4: Java8 time

Calendar•為了國際化而導入

•雖然叫 Calendar, 但實際上是 Date 和 Time

•建立和編輯日期與時間

•存取和運算比 java.util.Date 稍好一點,但仍不敷使用

Page 5: Java8 time

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();

Page 6: Java8 time

java.time.*•JSR-310

•為了 JDK 設計

•從 Joda-Time 啓發和演化

•ISO 8601 為基礎

•Immutable

•Type-safe

•考慮到 XML 和 資料庫

Page 7: Java8 time

ISO-8601•國際標準化組織的國際標準ISO 8601是日期

和時間的表示方法

•hh:mm:ss.s

•YYYY-MM-DDThh:mm:ss

•nYnMnD

•Gregorian Calendar (1582)

Page 8: Java8 time

ThreeTen backport project

•https://github.com/ThreeTen/threetenbp

•JSR-310 backport to JDK 7

•最終會有一個釋出會和 JDK 8 正式版本相同

Page 9: Java8 time

以人的角度看時間? vs

以機器的角度看時間?

Page 10: Java8 time

機器•Instant

•從 Java Epoch 開始經過的時間

•1970-01-01T00:00:00Z

•支援到奈秒

•Duration

•兩個 Instant 之間的差距

•Clock

•模擬系統時鐘

Page 11: Java8 time

InstantInstant 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);

Page 12: Java8 time

Duration•顯示時間的量

•使用 seconds 和 nanoseconds 表示

•toString 結果會是 PTnHnMnS

•顯示時可以用 hours,minutes,seconds 和 nanoseconds

•用 of 建立Duration.ofHours(2);

Duration.ofDays(1);

Page 13: Java8 time

•從兩個 Temporal 取得

!

•有 Day 相關的方法,但這邊的 Day 就是 24 Hours 的意思

// 兩個參數都必須支援 ChronoUnit.SECONDSDuration.between(LocalDateTime.now(), LocalDateTime.now().plusDays(2));

!

!

!

// 現在時間加 24LocalDateTime.now().plus( Duration.ofDays(1));

Page 14: Java8 time

•出生於 1983/12/11

•2月3日生

•營業時間是 14:00 到 22:00

•兩天,三小時

Page 15: Java8 time

日期與時間•LocalDate

•LocalTime

•LocalDateTime

•OffsetTime

•OffsetDateTime

•ZonedDateTime

Page 16: Java8 time

Local 系列•沒有帶時差和時區的資訊

•LocalDate

•12月5日生日

•LocalTime

•14:00 開始營業

•LocalDateTime

Page 17: Java8 time

LocalDateLocalDate now = LocalDate.now();

now.toString(); // 2014-03-29

now.getDayOfWeek(); // SATURDAY

now.getDayOfMonth(); // 29

now.isLeapYear(); // false

// 2014-04-10 now.plusDays(12).toString();

Page 18: Java8 time

LocalTimeLocalTime now = LocalTime.now();

now.toString(); // 14:20:42.270

now.getHour(); // 14

now.getMinute(); // 20

LocalTime.MIDNIGHT; // 00:00

Page 19: Java8 time

LocalDateTimeLocalDateTime now = LocalDateTime.now();

// 2014-03-29T14:20:42.270 now.toString();

now.getDayOfMonth(); // 29

now.getHour(); // 14

Page 20: Java8 time

•LocalDate <=> LocalDateTime

Local 之間的轉換

LocalDate localDate = LocalDate.now();

// LocalDate to LocalDateTimeLocalDateTime localDateTime = localDate.atTime(14, 20, 20);

// LocalDateTime to LocalDatelocalDate = localDateTime.toLocalDate();

Page 21: Java8 time

•LocalTime <=> LocalDateTime

LocalTime localTime = LocalTime.now();

// LocalTime to LocalDateTimeLocalDateTime localDateTime = localTime.atDate( LocalDate.of(2014, 3, 29));

// LocalDateTime to LocalTimelocalTime = localDateTime.toLocalTime();

Page 22: Java8 time

時差和 time zone•OffsetTime

•OffsetDateTime

•ZonedDateTime

•ZonedDateTime != OffsetDateTime

•時差是固定的,Zoned 則會依照狀況變換時差,例如:日光節約時間, 更改時差

Page 23: Java8 time

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日

Page 24: Java8 time

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日

Page 25: Java8 time

Time zone•ZoneOffset

•-18:00~+18:00(-12:00 ~ +14:00)

•ZoneRules

•切換的規則

•ZoneId

•例如 Asia/Taipei, +8

Page 26: Java8 time

Time zone data•tz database 有世界時區的分類和命名

•IANA Time Zone Database (TZDB)

•$JDK_HOME/jre/lib/tzdb.dat

•透過 ZoneRulesProvider 讀取

•一直有更新,所以記得要持續更新 JRE 或透過 Oracle 提供的工具更新

Page 27: Java8 time

ZoneRuleProvider•Service provider interface

•可以在 META-INF/services 自己提供

•系統預設是 TzdbZoneRuleProvider

•提供動態更新的方法

•帶來很多複雜的問題,例如:取消或減少暫存造成效能問題,或是程式中拿不同規則來做運算

•通常不建議動態更新

Page 28: Java8 time

public static boolean refresh() {

boolean changed = false;

for (ZoneRulesProvider provider : PROVIDERS) {

changed |= provider.provideRefresh();

}

return changed;

}

•ZoneRulesProvider.refresh

Page 29: Java8 time

public static boolean refresh() {

boolean changed = false;

for (ZoneRulesProvider provider : PROVIDERS) {

changed |= provider.provideRefresh();

}

return changed;

}

•ZoneRulesProvider.refresh

•回傳的 boolean 可以讓應用程式知道有沒有更新

•系統預設的 provider#provideRefresh 是回傳 false

Page 30: Java8 time

DateTime 之間的轉換•LocalDateTime, LocalDate, LocalTime

•OffsetDateTime, OffsetTime

•ZonedDateTime

•補足需要的資訊或拿掉多餘的資訊就可以彼此間轉換

Page 31: Java8 time

DateTime 之間的轉換•LocalDateTime, LocalDate, LocalTime

•OffsetDateTime, OffsetTime

•ZonedDateTime

•補足需要的資訊或拿掉多餘的資訊就可以彼此間轉換

ZonedDateTime zdt = LocalDateTime.now() .atZone(ZoneId.of(“Asia/Taipei”));

LocalDateTime ldt = zdt.toLocalDateTime();

Page 32: Java8 time

Period•顯示時間的量

•使用 years, months 和 days 表示

•toString 結果會是 PnYnMnD

•用 of 建立

Period.ofWeeks(2);

Page 33: Java8 time

// 兩個參數都是 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

Page 34: Java8 time

Formatting•用 DateTimeFormatter 取代

SimpleDateFormat

•Immutable - Thread safe

•可以利用 DateTimeFormatterBuilder 建立新格式

DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");

localDate.format(dateTimeFormatter);

offset.format(dateTimeFormatter);

Page 35: Java8 time

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);

Page 36: Java8 time

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 secondsDuration.parse("P2DT3H2.21S");

Page 37: Java8 time

•有些格式變得比較嚴格

DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyy/MM/dd");

//okLocalDate.parse("2014/03/02", formatter);

// error, DateTimeParseException//LocalDate.parse("2014/3/2", formatter);

//oknew SimpleDateFormat("yyyy/MM/dd") .parse("2014/3/2");

Page 38: Java8 time

Clock•透過 Inject 便於測試

•可以建立多種類型的 Clock

•讓測試可以獨立於 time zone

private Clock clock; // dependency inject

public void process(LocalDate event) {

if(event.isBefore(LocalDate.now(clock)){ ...

Page 39: Java8 time

//使用系統 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));

Page 40: Java8 time

其他表示方式

•Year

•Month

•YearMonth

•MonthDay

•DayOfWeek

Page 41: Java8 time

現在LocalTime.now();

LocalTime.now(Clock.fixed(...));

LocalDate.now();

LocalDateTime.now();

OffsetDateTime.now();

Page 42: Java8 time

of•Static factory method

LocalDate.of(2014, 3, 29);

LocalTime.of(14, 5, 25);

Year.of(-1);

Page 43: Java8 time

at•Combines this object with another

LocalDate.of(2014, 3, 29).atTime( LocalTime.of(14, 5, 25));

Page 44: Java8 time

parse•Static factory method focussed on

parsing

LocalDate.parse("2014-03-29");

LocalTime.parse("14:23:20");

Page 45: Java8 time

with•The immutable equivalent of a setter

LocalDate.now().withMonth(4);

LocalTime.now().withMinute(30);

Page 46: Java8 time

plus, minus•Adds/Subtracts an amount to an object

LocalDate.now().plusDays(4) .plusYear(2);

LocalTime.now().plusMinutes(30) .plusHours(4) .minusSeconds(30);

Page 47: Java8 time

isBefore, isAfter•Check order

LocalDate.now().isBefore( LocalDate.of(2014, 12, 3));

LocalTime.now().isAfter( LocalTime.of(4, 12, 31));

Page 48: Java8 time

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));

Page 49: Java8 time

Chronology•Chronology

•曆

•Era

•紀年

•ChronoLocalDate

•ChronoLocalDateTime

•ChronoZonedDateTime

Page 50: Java8 time

民國年•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());

Page 51: Java8 time

與 Date 的轉換•LocalDate 和 LocalTime 本身沒有 Instant 值,所以必須要先轉換成能取得 Instant

•從 LocalDateTime 到 DateLocalDate now = LocalDate.now();

Instant instant = now.atZone(ZoneId.systemDefault()) .toInstant();

Date date = Date.from(instant);

Page 52: Java8 time

•從 Date 到 LocalDateTime

Date date = new Date();

Instant instant = date.toInstant();

LocalDateTime = LocalDateTime.ofInstant( instant, ZoneId.systemDefault());

Page 53: Java8 time

可擴充的介面設計•TemporalAccessor

•Temporal

•TemporalField

•TemporalAmount

•TemporalUnit

•TemporalQuery

•TemporalAdjuster

•可讀取的 Date&Time

•可修改的 Date&Time

•Date&Time 的 filed

•時間的量

•Date&Time 的單位

•查詢 Date&Time

•修改 Date&Time

Page 54: Java8 time

TemporalQuery

@FunctionalInterface

public interface TemporalQuery<R>{

R queryFrom(TemporalAccessor temporal);

}

Page 55: Java8 time

//DaysLocalDate.of(2014, 3, 29) .query(TemporalQueries.precision());

//2014-03-29T16:30:26.043 ZonedDateTime.now .query(LocalDateTime::from);

//SATURDAYLocalDate.of(2014, 3, 29) .query(DayOfWeek::from);

Page 56: Java8 time

TemporalAdjuster

@FunctionalInterface

public interface TemporalAdjuster{

Temporal adjustInfo(Temporal temporal);

}

Page 57: Java8 time

//2014-03-31 LocalDate.of(2014, 3, 29) .with(TemporalAdjusters.lastDayOfMondth());

//2014-04-05LocalDate.of(2014, 3, 29) .with(TemporalAdjusters.next( DayOfWeek.SATURDAY));

//2014-01-02LocalDate.now().with(t -> t.with(ChronoField .DAY_OF_YEAR, 2));

Page 58: Java8 time

JDBC• LocalDate

• LocalTime

• LocalDateTime

• OffsetTime

• OffsetDateTime

• DATE

• TIME WITHOUT TIME ZONE

• TIMESTAMP WITHOUT TIME ZONE

• TIME WITH TIME ZONE

• TIMESTAMP WITH TIMZ ZONE

Page 59: Java8 time

LocalDateTime << SQLResultSet 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();

Page 60: Java8 time

LocalDateTime >> SQLPreparedStatement pstmt = …;

pstmt.setTime(1, Time.valueOf(LocalTime.now()));

pstmt.setDate(2, Date.valueOf(LocalDate.now()));

pstmt.setTimestamp(3, Timestamp.valueOf(LocalDateTime.now()));

Page 61: Java8 time

總結 JSR 310 的好處•良好的命名和 API 設計

•日期和時間的分別處理

•Immutable

•精確到奈秒

•多樣化的操作

•存取各種 filed 方便