なぜ新しい DateTime API が必要だったのか
まず一番大事な背景からいきます。
Java 8 で導入された java.time(DateTime API)は、「古い日付 API(java.util.Date / java.util.Calendar)がつらすぎたから」生まれました。
古い API は、
- 月が 0 始まりで分かりにくい
- ミュータブルで、いつどこで値が変わるか追いにくい
- スレッドセーフではない
- タイムゾーンや夏時間を扱うのが難しい
といった問題を抱えていて、「日付・時刻」という本来シンプルであってほしいものが、コード上ではやたらと壊れやすく、読みにくい存在になっていました。
新しい DateTime API の設計思想は、ざっくり言うとこうです。
「人間にとって自然な日付・時刻の概念を、
安全で、読みやすく、間違えにくい形でコードに落とし込む」
このゴールに向けて、いくつかの重要な考え方が組み込まれています。
設計思想1:不変(immutable)であること
なぜ「不変」がそんなに大事なのか
LocalDate や LocalDateTime、ZonedDateTime など、java.time のクラスは基本的にすべて不変(immutable)です。
一度作ったインスタンスの中身は変わりません。
例えば、こうです。
LocalDate today = LocalDate.of(2025, 1, 18);
LocalDate tomorrow = today.plusDays(1);
System.out.println(today); // 2025-01-18
System.out.println(tomorrow); // 2025-01-19
JavaplusDays は「今日を書き換える」のではなく、「新しい日付を返す」メソッドです。
これがなぜ大事かというと、
- どこか別のメソッドで勝手に日付が変えられる心配がない
- 複数スレッドから同じインスタンスを使っても安全
- 「いつ値が変わるのか」を追いかけなくてよくなる
からです。
古い Date / Calendar はミュータブルだったので、
Calendar cal = ...;
cal.add(Calendar.DAY_OF_MONTH, 1); // ここで cal 自体が書き換わる
Javaのように、「元のオブジェクトがいつの間にか変わっている」状態が簡単に起きました。
新しい API は、「日付・時刻は“値”として扱うべき」という思想に立って、すべて不変にしています。
設計思想2:概念ごとにクラスを分ける
「日付だけ」「時刻だけ」「タイムゾーン付き」を分ける
java.time には、いろいろなクラスがありますが、それぞれ「何を表しているか」がはっきり分かれています。
代表的なものだけ挙げると、
LocalDate:日付だけ(年・月・日)LocalTime:時刻だけ(時・分・秒)LocalDateTime:日付+時刻(タイムゾーンなし)ZonedDateTime:日付+時刻+タイムゾーンInstant:UTC 基準の「瞬間」(機械向けの時刻)
という感じです。
例えば、「誕生日」はタイムゾーン関係ないので LocalDate が自然です。
LocalDate birthday = LocalDate.of(1990, 4, 1);
Java一方、「会議の開始時刻(日本時間)」はタイムゾーン込みで考えたいので、ZonedDateTime が適しています。
ZonedDateTime meeting =
ZonedDateTime.of(2025, 1, 18, 10, 0, 0, 0, ZoneId.of("Asia/Tokyo"));
Java古い Date は「何を表しているのか」が曖昧でした。Date 1 個で「ローカル時間なのか UTC なのか」「タイムゾーンはどこなのか」がコードから読み取れません。
新しい API は、「概念ごとにクラスを分けることで、意図を型で表現する」という設計になっています。
設計思想3:人間向けの時間と機械向けの時間を分ける
Instant と LocalDateTime / ZonedDateTime の役割分担
Instant は、「1970-01-01T00:00:00Z からの経過秒数」という、機械的な時刻を表します。
Instant now = Instant.now();
Javaこれは「世界共通の一点」を表すのに向いていますが、人間にとっては読みにくいです。
一方で、LocalDateTime や ZonedDateTime は、「カレンダー上の日時」を表します。
LocalDateTime localNow = LocalDateTime.now();
ZonedDateTime tokyoNow = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
Java設計思想としては、
- 保存・通信・ログなど「システム内部のやり取り」には
Instant(+必要ならタイムゾーン情報) - 画面表示やビジネスロジックなど「人間の世界の時間」には
LocalDateTime/ZonedDateTime
という役割分担を想定しています。
これにより、
「DB には UTC の Instant で保存し、
アプリ側でユーザーのタイムゾーンに変換して表示する」
といった設計がやりやすくなります。
設計思想4:操作を「宣言的」に書ける API
plus / minus / with で「何をしたいか」を表現する
新しい DateTime API は、操作メソッドの名前がとても素直です。
LocalDate today = LocalDate.now();
LocalDate nextWeek = today.plusWeeks(1);
LocalDate firstDayOfMonth = today.withDayOfMonth(1);
LocalDate nextMonthSameDay = today.plusMonths(1);
JavaplusXxx / minusXxx / withXxx というパターンで、
- 何を足したいのか(plusDays, plusWeeks, plusMonths, …)
- 何を変えたいのか(withYear, withMonth, withDayOfMonth, …)
がそのままメソッド名に出ています。
古い Calendar だと、こうでした。
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DAY_OF_MONTH, 7);
cal.set(Calendar.DAY_OF_MONTH, 1);
Java「何をしているか」は分かりますが、
- ミュータブルである
- 定数の指定が分かりにくい
- メソッドチェーンしづらい
など、宣言的とは言い難い書き味でした。
新しい API は、「日付・時刻の操作を“文章のように”読めるようにする」という方向で設計されています。
設計思想5:フォーマットとパースを明示的に扱う
DateTimeFormatter で「どの形式か」をはっきりさせる
日付文字列との変換も、設計がかなり整理されています。
LocalDate date = LocalDate.of(2025, 1, 18);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String text = date.format(formatter); // "2025/01/18"
LocalDate parsed = LocalDate.parse("2025/01/18", formatter);
Javaポイントは、
- フォーマットのパターンを
DateTimeFormatterとして明示的に持つ format/parseの両方で同じフォーマッタを使える
というところです。
古い SimpleDateFormat はミュータブルでスレッドセーフではなく、
- static で使い回すとバグる
- 毎回 new するとパフォーマンスが悪い
という厄介な存在でした。
新しい DateTimeFormatter は不変でスレッドセーフなので、
static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern("yyyy/MM/dd");
Javaのように定数として安全に使い回せます。
設計思想6:「期間」と「瞬間」を分けて考える
Period と Duration の違い
java.time には、「期間」を表すクラスも用意されています。
Period:年・月・日単位の期間(人間のカレンダー感覚)Duration:秒・ナノ秒単位の期間(機械的な時間)
例えば、「2 日後」は Period で表現できます。
Period twoDays = Period.ofDays(2);
LocalDate today = LocalDate.now();
LocalDate twoDaysLater = today.plus(twoDays);
Java一方、「30 秒後」は Duration です。
Duration thirtySeconds = Duration.ofSeconds(30);
Instant now = Instant.now();
Instant later = now.plus(thirtySeconds);
Java「日付の差」と「時刻の差」を、
- カレンダー的に扱いたいのか
- 純粋な時間として扱いたいのか
でクラスを分けているのがポイントです。
まとめ:DateTime API の設計思想を自分の言葉で言うなら
あなたの言葉でまとめると、こうなります。
「新しい DateTime API(java.time)は、
古い Date / Calendar の“分かりにくい・壊れやすい・スレッドセーフでない”世界から脱出するために、
- すべて不変にして安全にし
- 日付・時刻・タイムゾーン・瞬間・期間などの概念をクラスごとに分け
- 人間向けの時間と機械向けの時間を分離し
- plus / with / format / parse などのメソッドで“何をしたいか”をそのまま書けるようにした
“日付・時刻を、型と API で正しく表現するための設計”になっている。」

