ThreeTen Extra と ThreeTen Backport

この記事は Java Advent Calendar 2016 の 20 日目です。昨日はひらおかゆみ (@yumix_h) の「自作のJavaMailラッパーでiCloudメールにアクセスする」です。

本日は、Date and Time API の拡張ライブラリである ThreeTen Extra と Java SE 6/7 向け互換ライブラリである ThreeTen Backport についてご紹介します。

1. JSR 310 と 3 つの成果物

JSR 310 : Date and Time API は、java.util.Date と java.util.Calendar を代替する API を提供するというものでした。スペック・リードは Stephen Colebourne (Joda-Time 開発者) と Michael Nascimento Santos という 2 人の Java Champion が務めました。ベンダー (特に Oracle) の開発者がスペック・リードを務める JSR が多い中で、JSR 310 は数少ないコミュニティ主体の JSR だったのも特徴的です。

JSR 310 は OpenJDK 8 入りが決まってから Oracle の Roger Riggs が 3 人目のスペック・リードとして加わり、現在は Date and Time API のメンテナンス・リードを務めています。

JSR 310 の基本概念は JavaOne 2008 のセッションの時点でほぼ固まっており (SourceForge に当時の資料がアップロードされています)、そこでは 4 つの特徴―(1) 日付と時刻の包括的なモデル、(2) タイプ・セーフ (プリミティブ型を極力避け、読み書きしやすく)、(3) 既存クラスとの連携 (既存の Date を JSR 310 に合わせて改修)、(4) XML と JDBC の考慮―を掲げ、また人間向け表現として ISO 8601 を採用することが明言されました。

Early Draft Review の頃は Java SE 6 で実装され、当初は Java SE 7 を目標に掲げていました。しかし、Early Draft Review の段階でクラス数が 102 というとち狂った非常に大きな仕様だったため、OpenJDK 8 へのマージに先だって大胆なダイエットを敢行し、Early Draft Review 2 でクラス数を 75 まで削減、Public Review ではクラス数を 69 にまで絞り込みました。これに小さな改修を加えてリリースしたものが Java SE 8 の Date and Time API です。

Early Draft Review 2 で JSR 310 の仕様から除外した UTC/TAI 対応 (うるう秒の考慮)、日付フィールド、暦 (ユリウス暦とコプト暦) などの機能を再構成し、ユーティリティといくつかの暦を追加したものが ThreeTen Extra です。Date and Time API と ThreeTen Extra を合わせると、JSR 310 が当初目指した姿に近いものとなります。

JSR 310 は Java SE 7 が目標だったことから、完成に近い段階まで Java SE 7 ベースで実装していました。OpenJDK 8 の Mercurial リポジトリに移ってからインタフェースのデフォルト実装の利用やラムダ式への対応がなされましたが、プロジェクトの GitHub リポジトリに残された実装を Java SE 8 の Date and Time API とほぼ同じ使い勝手となるように改修したものが ThreeTen Backport です。正式リリース直前に Java SE 6 へのバックポートが行われています。

まとめると、JSR 310 は 3 つの成果物を生みました。

  • Date and Time API -- Java SE 8 以降の標準 API
  • ThreeTen Extra -- Date and Time API から除外された機能 + α
  • ThreeTen Backport -- Java SE 6/7 向けの Date and Time API 互換ライブラリ

2. ThreeTen Extra

2.1. ThreeTen Extra のインストール

Maven Central に登録されています。Java SE 8 以降の他に依存するものはありません。

<dependency>
  <groupId>org.threeten</groupId>
  <artifactId>threeten-extra</artifactId>
  <version>1.0</version>
</dependency>

2.2. ThreeTen Extra の機能

ThreeTen Extra の機能はいくつかのカテゴリに分類することができます。

TAI/UTC 対応

JSR 310 の Early Draft Review 2 で「凝り過ぎた」として真っ先に削除された TAI/UTC 対応 (いわゆるうるう秒の考慮) がなされています。

  • TimeSource - UTC/TAI にも対応した Clock のためのインタフェース
  • TaiInstant - 国際原子時 (TAI) に準拠した時刻のものさし (Instant)
  • UtcInstant - 協定世界時 (UTC) に準拠した時刻のものさし (Instant)
  • UtcRules - うるう秒の管理、Instant / TaiInstant / UtcInstant の相互変換などを提供する

現在 Java が動作するプラットフォームのシステムクロックは、これらを十分に生かせるほどの精度を持ち合わせていませんが、高精度な外部クロックと接続できれば活用の機会はあるかと思われます。

日付・時刻要素

JSR 310 は日付・時刻の要素をプリミティブ型ではなくクラスで持たせて、バリデーションを厳格にする方針を採っていました。しかし、クラス数超過のため Early Draft Review 2 の段階で現在の日付・時刻クラスと YearYearMonthMonthDayMonthDayOfWeek に絞り込まざるを得ませんでした。ThreeTen Extra では JSR 310 から削除された日付・時刻の要素を復活させる形で実装しています。

  • YearQuarter - 年 + 四半期
  • YearWeek - 年の週; これに日が加わると暦週日付 (week date) となる
  • DayOfMonth - 月の日; Date and Time API では int として扱っている
  • DayOfYear - 年の日 = 年間通算日 (ordinal date)
  • AmPm - 午前 / 午後
  • Quarter - 四半期
  • PackedFields - ISO 8601 標準形式 (区切り記号を使わない) の日付・時刻フィールド

時間量

Date and Time API では時間量を PeriodDuration の 2 クラスで表現しますが、元々の JSR 310 ではより直感的な時間量を表すクラスも用意されていました。こちらも Early Draft Review 2 で一旦削除されたものがほぼそのままの形で復活したものです。

ローカル暦

JSR 310 から除外されたユリウス暦とコプト暦の他に、多数のローカル暦が追加されています。ディスコルディア暦とパックス暦は早い時期に実装されていますが、その他のローカル暦は後発の部類となります。

ユーティリティ・クラス

3. ThreeTen Backport

3.1. ThreeTen Backport のインストール

Maven Central に登録されています。Java SE 6 以降の他に依存するものはありません。

<dependency>
  <groupId>org.threeten</groupId>
  <artifactId>threetenbp</artifactId>
  <version>1.3.2</version>
</dependency>

3.2. ThreeTen Backport の機能

ThreeTen Backport は可能な限り Date and Time API と同じ振る舞いをするように考慮されています。Javadoc は http://www.threeten.org/threetenbp/apidocs/ にありますが、Date and Time API とほぼ同様であることが見て取れます。

Date and Time API からの相違点は以下の通りです。

  • パッケージ名 -- java.time ではなく org.threeten.bp
  • Java SE 8 を模倣するためのクラスを追加 -- org.threeten.bp.jdk8 パッケージ (4 クラス)
  • tz database 周りを独自に実装 -- org.threeten.bp.zone.TzdbZoneRulesProvider クラスおよび tzdb のデータファイル (tzdb は JDK ではなく ThreeTen Backport のバージョンアップ時に更新されるので注意)
  • デフォルト実装や static メソッドを持つインタフェースについて、抽象クラスに置き換え (暦周りを中心に)
  • org.threeten.bp.DateTimeUtils -- java.util.Date や java.util.Calendar などとの相互変換ユーティリティ

DateTimeUtils は本家 Date and Time API にはない、ThreeTen Backport 独自のユーティリティ・クラスです。Date and Time API と java.util.Date などの相互変換メソッドを作成する際には、DateTimeUtils の実装を参考にすると良いかもしれません。

明日は rysh(r.ishibashi) さん (@cactaceae) です。