JavaSE8 Goldへの道(Upgrade to Java SE 8 Programmer 1Z0-810 試験対策)12回目です。
一連の記事は「JavaSE8Gold」ラベルを付けていきます。
Java8では
これを3回に分けて紹介していきます。
Date/TimeAPIの全てのクラスは不変(イミュータブル)でスレッドセーフに設計されています。
一連の記事は「JavaSE8Gold」ラベルを付けていきます。
Java8では
java.util.Date、java.util.Calendarなどに代わる新しい「Date/Time API」が整備されました。これを3回に分けて紹介していきます。
特徴
従来のクラス群は不変でない(ミュータブル)、スレッドセーフでないという問題がありました。Date/TimeAPIの全てのクラスは不変(イミュータブル)でスレッドセーフに設計されています。
基本となるInstantクラス
時系列のある一時点を表します。
標準Javaエポック(1970-01-01T00:00:00Z)からの経過秒とナノ秒を保持しています。
注意として、タイムゾーンがUTC固定です。(ISO 8601形式で末尾"Z"はUTCを表す)
生成方法の例(いつも参考にしているこちらのサイトのサンプルコードより。以降同様。)
// 現在時刻(UTC)
Instant now = Instant.now();
System.out.println(now);
// Unixタイムスタンプから(2015-07-13T20:45:44.404Z)
Instant fromUnixTimestamp = Instant.ofEpochSecond(1436820344);
// 同じ時刻をミリ秒で指定
Instant fromEpochMilli = Instant.ofEpochMilli(1436820344404L);
// ISO 8601形式の文字列からパース
Instant fromIso8601 = Instant.parse("2015-07-10T12:00:00Z");
System.out.println(fromIso8601);
// toString()はISO 8601形式で返す
String toIso8601 = now.toString();
System.out.println(toIso8601);
// Javaエポック1970-01-01T00:00:00Zからの秒数
long toUnixTimestamp = now.getEpochSecond();
System.out.println(toUnixTimestamp);
// エポック1970-01-01T00:00:00Zからのミリ秒数
long toEpochMillis = now.toEpochMilli();
System.out.println(toEpochMillis);実行結果
2018-04-23T08:15:12.371Z
2015-07-10T12:00:00Z
2018-04-23T06:35:26.015Z
1524465326
1524465326015
2015-07-10T12:00:00Z
2018-04-23T06:35:26.015Z
1524465326
1524465326015
タイムゾーンが固定なので、そのまま使うと日本時間とずれます。
他にも加算・減算を行う
これらのメソッドは以降で紹介するクラスにも実装されています。
イミュータブルなので結果は全て新しいインスタンスを返します。
(ただし
他にも加算・減算を行う
plus,minusメソッド、比較を行うisAfter,isBeforeメソッド、ある属性を特定の値を指定(月の最初の日など)するwithメソッドなどがあります。これらのメソッドは以降で紹介するクラスにも実装されています。
イミュータブルなので結果は全て新しいインスタンスを返します。
Instantを基本として、3つの日時を表すクラスが存在します。(ただし
Instantがスーパークラスというわけではありません)3つの日時クラス
LocalDateTime
タイムゾーン情報を持たない日時クラスです。
内部に
LocalDateとLocalTimeクラスのインスタンスを持ちます。これらは独立してそれぞれ日付のみ/時刻のみを表すクラスとして存在します。生成方法の例
// Date with time
LocalDateTime currentDateTime = LocalDateTime.now();
System.out.println("Current date and time : " + currentDateTime);
// 2015-09-15 10:15
LocalDateTime sept15th = LocalDateTime.of(2015, 9, 15, 10, 15);
System.out.println("September 15th : " + sept15th);
// 2015-12-25 12:00
LocalDateTime christmas2015 = LocalDateTime.of(2015, Month.DECEMBER, 25, 12, 0);
System.out.println("Christmas 2015 : " + christmas2015);
// Instantから生成
Instant n = Instant.now();
System.out.println(n);
System.out.println(LocalDateTime.ofInstant(n, ZoneId.systemDefault()));実行結果Current date and time : 2018-04-23T17:15:12.370
September 15th : 2015-09-15T10:15
Christmas 2015 : 2015-12-25T12:00
2018-04-23T08:15:12.371Z
2018-04-23T17:15:12.371
September 15th : 2015-09-15T10:15
Christmas 2015 : 2015-12-25T12:00
2018-04-23T08:15:12.371Z
2018-04-23T17:15:12.371
ofメソッドには他にもたくさんの引数パターンがあります。月にはMonth列挙型を渡すものとintで指定するものがあり、intの方は従来と違い1~12で指定します。「タイムゾーン情報を持たない」と言いながら
Instantから生成する際にZoneIdを引数に取るのはなんか変な感じがしますが、LocalDateTimeが従来のDateのようにシステムデフォルトのタイムゾーンで生成されるものと考えれば良いでしょう。LocalDateTime#nowメソッドはデフォルトタイムゾーンで現在日時を生成します。ZonedDateTime
タイムゾーン情報を持つ日時クラスです。タイムゾーンは
ZoneIdというクラスで保持しています。ややこしいですが、ZonedDateやZonedTimeというクラスはありません。
ZonedDateTimeのみです。生成には
nowメソッドでフォルトのタイムゾーンかZoneIdを指定して生成するか、ofメソッドにLocalDateTimeとZoneIdも追加で引数に渡します。生成方法の例
ZoneId minsk = ZoneId.ofOffset("GMT", ZoneOffset.ofHours(+3));
ZoneId berlin = ZoneId.of("Europe/Berlin");
// Current date and time
LocalDateTime dateTime = LocalDateTime.now();
System.out.println("Here : " + dateTime);
ZonedDateTime minskDateTime = ZonedDateTime.of(dateTime, minsk);
System.out.println("Minsk : " + minskDateTime);
// Current date and time in Berlin, Germany
ZonedDateTime berlinDateTime = minskDateTime.withZoneSameInstant(berlin);
System.out.println("Berlin : " + berlinDateTime);実行結果
Here : 2018-04-23T17:44:12.897
Minsk : 2018-04-23T17:44:12.897+03:00[GMT+03:00]
Berlin : 2018-04-23T16:44:12.897+02:00[Europe/Berlin]
Minsk : 2018-04-23T17:44:12.897+03:00[GMT+03:00]
Berlin : 2018-04-23T16:44:12.897+02:00[Europe/Berlin]
withZoneSameInstantメソッドはインスタントを保持したまま別のタイムゾーンを使ってコピーを生成します。結果示す日時は変更されます。
OffsetDateTime
試験範囲としては載っていないのですが、一応これも説明しておきます。
ゾーン情報は持たず、オフセットのみを保持します。これは
ZoneOffsetとして表されています。生成時も
ofメソッドにはZoneOffsetを引数に取ります。とてもややこしいことに、OffsetDateはないのに
OffsetTimeだけ存在します。時間量を表すクラス
Period
日付ベースの時間量を表すクラスです。
生成には
of~メソッドを使います。引数には年、月、日、もしくは週数を指定できます。nowメソッドはありません。「量」なので、マイナスの値も取り得ます。
参考サイトのサンプルコード
LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(1974, Month.SEPTEMBER, 15);
Period p = Period.between(birthday, today);
long p2 = ChronoUnit.DAYS.between(birthday, today);
System.out.printf("You are %d years, %d months, and %d days old (%d days total)", p.getYears(), p.getMonths(), p.getDays(), p2);実行結果
You are 43 years, 7 months, and 8 days old (15926 days total)
betweenメソッドは2つのDateTimeの差を求めます。逆に、各日時クラスの
plus(TemporalAmount),minus(TemporalAmount)の引数に渡して計算ができます。(
Periodと次に説明するDurationクラスはTemporalAmountインタフェースを実装しています)タイムゾーンを考慮する場合、
ZonedDateTime#plusなどを使う必要があります。Period自身にもplus~,minus~,with~メソッドがあり、新たな時間量を計算できます。Duration
時刻ベースの時間量を表すクラスです。秒とナノ秒で保持します。
Durationも生成にはof~メソッドを使います。nowメソッドはありません。これもマイナスの値を取り得ます。
参考サイトのサンプルコード
// Current time
Instant now1 = Instant.now();
// Wait __appropximately__ 1 second
Thread.sleep(1000);
// Current time
Instant now2 = Instant.now();
// Calculate real duration
long ns = Duration.between(now1, now2).toNanos();
System.out.println("Duration (ns) : " + ns);実行結果
Duration (ns) : 1001000000
「約」と強調している通り、自分のPCがでは1秒ぴったりではありませんでした。
前述の通り、各日時クラスの
前述の通り、各日時クラスの
plus(TemporalAmount),minus(TemporalAmount)の引数に渡して計算ができます。Duration自身の各計算メソッドがあるのもPeriodと同じです。java.util.Dateとの相互変換
java.util.Dateとの相互変換ですが、Date/TimeAPIの方にはありません。Dateクラスの方にfrom,toInstantメソッドが追加されています。public static Date from(Instant instant) public Instant toInstant()
また、
CalendarクラスにtoInstantメソッドだけが追加されています。Instantにはタイムゾーン情報がないのでCalendarは作れないということでしょう。と、い、う、わ、け、で
Date/Time APIその1は基本のクラス群の解説でした。
基本となる
Instantと3つの日時クラスLocalDateTime,ZonedDateTime,OffsetDateTimeの関係、時間量を表す2つのクラスPeriod,Durationをしっかり押さえておきましょう。今回も以下のサイトとGoldの通常試験の参考書を参考にしています。

