広告

Java : Date, CalendarではなくLocalDateTime, ZonedDateTimeを使おう

Java 8 から日付・時刻の新しいAPI が追加されました。
Instant, LocalTime, LocalDate, LocalDateTime, ZonedDateTime などです。
これらは古いAPI である Date, Calendar などを改善したものです。


用語の定義

本記事では、時間に関する用語を以下のように定義します。

用語 定義
日付 年と月と日 2021/5/4
2000年12月31日
時刻 時と分と秒 12:30:59
7時30分45秒
日時 日付と時刻 2021/5/4 12:30:59
2000年12月31日 7時30分45秒
時点 時間軸上の一点。
(具体的には、Dateは1970年1月1日00:00:00 GMTからのミリ秒数、Instantは1970-01-01T00:00:00Zからの秒とナノ秒)
1609504200.123 (2021-01-01T12:30:00.123Z)

新APIと旧APIの機能比較

新/旧 API 時点 日付 時刻 タイムゾーン 不変 スレッドセーフ
Instant × × ×
LocalDate × × ×
LocalTime × × ×
LocalDateTime × ×
ZonedDateTime ×
ZoneId × × ×
Date × × × × ×
Calendar × × ×
TimeZone × × × × ×

新API では、全体的に機能が細分化されました。
そして、タイムゾーンのあり・なしが明確に分離されました。

日付だけしか使わない場合は LocalDate、時刻だけしか使わない場合は LocalTime で済みます。
日時にタイムゾーンが不要であれば LocalDateTime を使いましょう。

旧API だと、なんでもかんでも Calendar でした。

1つのクラスはなるべく最小限の機能だけにして小さくする…設計の基本ですね。

あと、新API はすべてのクラスが不変です。
不変であるということはスレッドセーフでもあります。

不変のメリットについては下記の記事でも解説していますので、よろしければご参照ください。

関連記事:不変オブジェクト(イミュータブル) とは

新APIの簡単な紹介

Java : 日付・時刻の基本」で、新APIの基本的な使い方を紹介しています。
そちらを参照していただけたら幸いです。


旧APIの簡単な紹介

Date

クラスDateは、特定の時点を表します(精度はミリ秒)。

新API の Instant に相当するクラスです。
Java 1.0 ではカレンダー的な機能もありましたが、Java 1.1 で非推奨になりました。

Calendar

Calendarクラスは、特定の時点とYEAR、MONTH、DAY_OF_MONTH、HOURなどのカレンダ・フィールド・セット間の変換、および次週の日付の取得などのカレンダ・フィールド操作を行うための抽象クラスです。

新API の ZonedDateTime に相当するクラスです。
日付・時刻の操作、計算、取得などひととおりのことができます。

TimeZone

TimeZoneは、タイムゾーン・オフセットを表します。また、サマー・タイムを認識します。

新API の ZoneId(とZoneOffset) に相当するクラスです。
サマータイムも考慮さています。

相互運用も可能

古いライブラリなどでは、Date や Calendarクラスをパラメータに要求するものがあるかもしれません。
その場合は、新旧API の変換を行いましょう。

旧API → 新API

// Date(旧API) → Instant(新API)
final var date = new Date(4102444800123L);
System.out.println(date.getTime()); // 4102444800123

final var instant = date.toInstant();
System.out.println(instant); // 2100-01-01T00:00:00.123Z
System.out.println(instant.getEpochSecond()); // 4102444800
System.out.println(instant.getNano()); // 123000000
// TimeZone(旧API) → ZoneId(新API)
final var timeZone = TimeZone.getDefault();
System.out.println(timeZone.getID()); // Asia/Tokyo

final var zoneId = timeZone.toZoneId();
System.out.println(zoneId); // Asia/Tokyo

(Calendar から直接 ZonedDateTime に変換する API はなさそう?)

// Calendar(旧API) → ZonedDateTime(新API)
final var calendar = Calendar.getInstance();
calendar.setTimeInMillis(4102444800000L);
System.out.printf("%tc%n", calendar); // 金 1月 01 09:00:00 JST 2100

final var instant = calendar.toInstant();
System.out.println(instant); // 2100-01-01T00:00:00Z

final var zoneId = calendar.getTimeZone().toZoneId();
System.out.println(zoneId); // Asia/Tokyo

final var zonedDateTime = ZonedDateTime.ofInstant(instant, zoneId);
System.out.println(zonedDateTime); // 2100-01-01T09:00+09:00[Asia/Tokyo]

新API → 旧API

// Instant(新API) → Date(旧API)
final var instant = Instant.ofEpochSecond(4102444800L, 123456789L);
System.out.println(instant); // 2100-01-01T00:00:00.123456789Z

final var date = Date.from(instant);

// ミリ秒より小さい桁は切り捨てられます。
System.out.println(date.getTime()); // 4102444800123
// ZoneId(新API) → TimeZone(旧API)
final var zoneId = ZoneId.systemDefault();
System.out.println(zoneId); // Asia/Tokyo

final var timeZone = TimeZone.getTimeZone(zoneId);
System.out.println(timeZone.getID()); // Asia/Tokyo

(ZonedDateTime から直接 Calendar に変換する API はなさそう?)

// ZonedDateTime(新API) -> Calendar(旧API)
final var zonedDateTime = ZonedDateTime.of(
        2100, 1, 1, 9, 0, 0, 0, ZoneId.systemDefault());
System.out.println(zonedDateTime); // 2100-01-01T09:00+09:00[Asia/Tokyo]

final var instant = zonedDateTime.toInstant();
System.out.println(instant); // 2100-01-01T00:00:00Z

final var timeZone = TimeZone.getTimeZone(zonedDateTime.getZone());
System.out.println(timeZone.getID()); // Asia/Tokyo

final var calendar = Calendar.getInstance(timeZone);
calendar.setTime(Date.from(instant));

System.out.printf("%tc%n", calendar); // 金 1月 01 09:00:00 JST 2100

まとめ

本記事では、日付・時刻の新しい API と古い API の比較を簡単にご紹介しました。

使い慣れた古い API を、つい使ってしまう…気持ちはわかります。

とはいえ、古い API に問題があったからこそ新しい API が生まれたわけです。
いろいろと改善はされているはずです。

新しい API を使うことで、結果としてプログラムの品質があがり、テスト期間を短縮できる…かもしれません。
ぜひ有効に活用していきたいですね。


関連記事

ページの先頭へ