- 1 1. この記事で分かること
- 2 2. Javaの日付・日時型を整理(比較の前提)
- 3 3. java.time(LocalDate / LocalDateTime)で日付を比較する【最重要】
- 4 4. java.util.Date で日付を比較する(レガシー対応)
- 5 5. 文字列(String)の日付を比較するなら「必ずパースしてから」
- 6 6. 範囲チェック(期間内か?締切を過ぎたか?)の定番実装
- 7 7. 差分計算:日数差・経過時間を正しく求める(Period / Duration)
- 8 8. タイムゾーンの落とし穴:ズレる比較・ズレない比較
- 9 9. まとめ:迷ったときの選び方チートシート
- 10 FAQ
1. この記事で分かること
Javaで「日付を比較したい」と思ったとき、最初に押さえるべき結論はシンプルです。
- 基本は
java.time(LocalDate / LocalDateTime / Instant / ZonedDateTime)を使う - 「日付だけ」なのか「日時」なのかで選ぶ型が変わる
- 比較は
isBefore / isAfter / isEqualが最も分かりやすい Date(java.util.Date)はレガシー。触るなら早めにjava.timeに変換して扱う
ここを外すと、初心者がよく踏む「ズレる」「一致しない」「思った判定にならない」というトラブルに繋がりやすいです。
1.1 まず最短で理解:日付比較の正解ルート
「日付比較」という言葉は一見同じでも、実際には次の4パターンがあります。
- 前後を判定したい(締切より前か?/未来か?)
- 同じかどうかを判定したい(同日か?/同一時刻か?)
- 期間内かを判定したい(開始〜終了に含まれるか?)
- 差分を出したい(何日後?何分後?)
そして、これらを安全に書くためには「型選び」が重要です。
1.2 「日付」なのか「日時」なのかで型が変わる
初心者が一番混乱しやすいのがここです。あなたが比較したいのはどちらでしょうか?
日付だけ(カレンダーの日付)を比較したい場合
例:
- 2026-01-09 が 2026-01-10 より前か?
- 誕生日が今日と同じか?
この場合は LocalDate が最適です。
- 時刻を持たない(= 00:00 などの概念でぶれない)
- 「同日判定」が直感的に書ける
日時(時刻まで含める)を比較したい場合
例:
- 2026-01-09 18:30 が 19:00 より前か?
- 予約時間を過ぎたか?
この場合は LocalDateTime が基本になります。
- 日付+時刻を持つ
- 「同じ日だけど時刻が違う」を区別できる
タイムゾーンが絡む(世界対応・サーバとユーザーが違う)場合
例:
- 日本時間での「今日」と、海外ユーザーの「今日」が違う
- サマータイムで1日が24時間じゃない日がある
この場合は ZonedDateTime または Instant を使うのが安全です。
1.3 比較メソッドはこれだけ覚えればOK(java.time)
java.time での比較は、まずこの3つが基本です。
isBefore():前か?isAfter():後か?isEqual():同じか?
たとえば LocalDate なら次のように書けます。
import java.time.LocalDate;
LocalDate a = LocalDate.of(2026, 1, 9);
LocalDate b = LocalDate.of(2026, 1, 10);
System.out.println(a.isBefore(b)); // true
System.out.println(a.isAfter(b)); // false
System.out.println(a.isEqual(b)); // falseこの書き方は「読みやすさ」が強いです。初心者でも、英語の意味のまま理解できます。
1.4 compareTo は「並び替え」や「大小判定」に向いている
日付を比較して「どっちが先か」を数値で扱いたい場合は compareTo() が便利です。
- 戻り値が 負数:左が小さい(=前)
- 戻り値が 0:同じ
- 戻り値が 正数:左が大きい(=後)
int result = a.compareTo(b);
if (result < 0) {
System.out.println("aはbより前");
} else if (result == 0) {
System.out.println("aとbは同じ");
} else {
System.out.println("aはbより後");
}ただし、初心者向けに「前後判定」を書くなら、基本は isBefore/isAfter の方が直感的です。compareTo は、ソート(並び替え)や、結果を数値として扱いたいときに真価を発揮します。
1.5 旧APIの Date は「比較できるけど、落とし穴が多い」
java.util.Date でも比較はできます。たとえば before() / after() があります。
ですが、初心者がつまずきやすい点があります。
- ミリ秒まで含めた「日時」として扱われる
- 「同日判定」のつもりでも、時刻が違えば一致しない
- タイムゾーンや変換が絡むと解釈ミスが起きやすい
そのため、この記事では次の方針で進めます。
- まず java.time を中心に日付比較を説明する
Dateは現場で避けられない場面に絞って、安全な扱い方(変換)も含めて説明する
1.6 この記事の読み方(あなたの目的別ガイド)
あなたが知りたいことに合わせて、読みどころを先に案内しておきます。
- とにかく日付の前後判定をしたい
→ 3章(LocalDate / LocalDateTime の比較)が最優先 - Dateしか使えない古いコードに苦しんでいる
→ 4章(Date比較と変換)を重点的に - 文字列(String)の”2026-01-09″を比較したい
→ 5章(パースして比較)が必読 - 開始日〜終了日で範囲チェックしたい
→ 6章(期間内判定)のテンプレを使う - 日数差、経過時間を出したい
→ 7章(Period/Duration) - タイムゾーンでズレて困ったことがある
→ 8章(Instant/ZonedDateTime)
2. Javaの日付・日時型を整理(比較の前提)
日付比較で混乱が起きる最大の原因は、「どの型が、何を表しているのかを曖昧なまま使ってしまうこと」です。
ここでは比較の前提として、Javaに存在する主な日付・日時型を整理します。
2.1 Javaには日付・日時の型が複数存在する
Javaで使われる日付・日時関連の型は、大きく分けて次の2系統があります。
- 旧API(レガシー)
java.util.Datejava.util.Calendar
- 新API(java.time パッケージ)
LocalDateLocalDateTimeZonedDateTimeInstant
現在のJava開発では、原則として java.time を使うのが正解です。
ただし、既存システムや外部ライブラリの都合で旧APIが残っている現場も多いため、両方を理解しておく必要があります。
2.2 「日付だけ」か「日時」かを最初に決める
まず重要なのは、比較したい対象が何かを明確にすることです。
カレンダー上の日付だけを扱いたい場合
例:
- 今日が締切日より前か
- 誕生日が同じ日か
この場合は LocalDate を使います。
LocalDate today = LocalDate.now();
LocalDate deadline = LocalDate.of(2026, 1, 31);特徴は次の通りです。
- 年・月・日のみを保持
- 時刻の概念を持たない
- 「同日判定」が直感的にできる
「日付比較」と言われたら、まず LocalDate を疑う
これを覚えておくと失敗が減ります。
2.3 日時(時刻まで)を比較したい場合
例:
- 予約時刻を過ぎたか
- ログイン時間の前後関係
この場合は LocalDateTime を使います。
LocalDateTime start = LocalDateTime.of(2026, 1, 9, 18, 30);
LocalDateTime end = LocalDateTime.of(2026, 1, 9, 19, 0);特徴:
- 年・月・日・時・分・秒を保持
- タイムゾーンの情報は含まない
- 同じ「日」でも時刻が違えば別物として扱える
「同日だけど時間が違う」という区別が必要な場合に必須です。
2.4 タイムゾーンが絡む場合は要注意
次のようなケースでは、LocalDateTime だけでは不十分です。
- サーバーは日本、ユーザーは海外
- 世界共通の締切時刻を扱う
- サマータイムが存在する地域を考慮する
このような場合は ZonedDateTime または Instant を使います。
ZonedDateTime:どこの日時かを含める
ZonedDateTime tokyoTime =
ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));- 日時+タイムゾーンを保持
- 表示用・ユーザー向け処理に向いている
Instant:世界共通の「瞬間」
Instant now = Instant.now();- タイムゾーンを持たない
- UTC基準の「一点の時刻」
- 比較や保存(DB・ログ)に非常に強い
保存や比較は Instant、表示は ZonedDateTime
これは実務でよく使われる安全な設計パターンです。
2.5 java.util.Date がややこしい理由
java.util.Date は一見シンプルに見えますが、初心者には扱いづらい特徴があります。
Date date = new Date();問題点は次の通りです。
- 中身は「日時(ミリ秒)」なのに、クラス名が Date
- 日付だけを扱っているつもりでも、時刻が必ず含まれる
- 同日判定をしたいときに失敗しやすい
たとえば「2026-01-09」のつもりでも、内部的には
「2026-01-09 00:00:00.000」や「2026-01-09 12:34:56.789」
として扱われます。
このため、Date同士の equals / compareTo は直感とズレやすいのです。
2.6 現代Javaでのおすすめ指針(まとめ)
ここまでを踏まえた、実務向けの指針を整理します。
- 日付だけ → LocalDate
- 日時(TZ不要) → LocalDateTime
- 世界共通の比較・保存 → Instant
- 表示・ユーザー向け → ZonedDateTime
- Dateを受け取ったら → すぐ java.time に変換
この前提を押さえておけば、次章の「具体的な日付比較」はスムーズに理解できます。
3. java.time(LocalDate / LocalDateTime)で日付を比較する【最重要】
ここからが、この記事の核心です。
現在のJava開発において、日付比較はほぼ java.time で完結します。
まずは LocalDate と LocalDateTime の比較方法を確実に押さえましょう。
3.1 前後判定の基本:isBefore / isAfter / isEqual
最もよく使うのが「前か後か」「同じか」の判定です。
LocalDate の前後比較
LocalDate d1 = LocalDate.of(2026, 1, 9);
LocalDate d2 = LocalDate.of(2026, 1, 10);
System.out.println(d1.isBefore(d2)); // true
System.out.println(d1.isAfter(d2)); // false
System.out.println(d1.isEqual(d2)); // falseポイントは次の通りです。
isBefore:左が右より「前」isAfter:左が右より「後」isEqual:同じ日付
メソッド名がそのまま意味を表しているため、初心者でも読みやすく、安全です。
3.2 LocalDateTime でも比較方法は同じ
日時を扱う LocalDateTime でも、使い方は変わりません。
LocalDateTime t1 = LocalDateTime.of(2026, 1, 9, 18, 30);
LocalDateTime t2 = LocalDateTime.of(2026, 1, 9, 19, 0);
System.out.println(t1.isBefore(t2)); // true
System.out.println(t1.isAfter(t2)); // false
System.out.println(t1.isEqual(t2)); // falseただし注意点があります。
- 同じ日でも、時刻が違えば isEqual は false
- 「同日かどうか」を見たい場合は、次の方法を使います
3.3 「同日か?」を判定したいときの正しい書き方
LocalDateTime で「同じ日かどうか」を判定したい場合、
日付部分だけを切り出すのが安全です。
boolean sameDay =
t1.toLocalDate().isEqual(t2.toLocalDate());この書き方のメリットは、
- 時刻の違いに影響されない
- 意図がコードから明確に読み取れる
「同日判定 = LocalDate に落としてから比較」
これは実務で非常によく使われるパターンです。
3.4 compareTo を使った大小比較と並び替え
compareTo() は、比較結果を数値として扱いたい場合に便利です。
int result = d1.compareTo(d2);
if (result < 0) {
System.out.println("d1はd2より前");
} else if (result == 0) {
System.out.println("同じ日付");
} else {
System.out.println("d1はd2より後");
}この特性を利用すると、コレクションの並び替えが簡単に書けます。
List<LocalDate> dates = List.of(
LocalDate.of(2026, 1, 10),
LocalDate.of(2026, 1, 8),
LocalDate.of(2026, 1, 9)
);
dates.stream()
.sorted()
.forEach(System.out::println);LocalDate や LocalDateTime は 自然順序(昇順)を持っているため、特別な Comparator を書かなくても並び替えできます。
3.5 equals と isEqual の違いを理解する
LocalDate では equals() と isEqual() の結果は基本的に同じです。
d1.equals(d2);
d1.isEqual(d2);しかし、読みやすさの観点では次のように使い分けると良いです。
- 条件判定 →
isEqual - コレクション操作・キー比較 →
equals
意図が明確になることで、後から読む人が理解しやすくなります。
3.6 よくある失敗例:null と比較してしまう
日付比較で意外と多いのが null による例外です。
LocalDate date = null;
date.isBefore(LocalDate.now()); // NullPointerException対策としては、
- 事前に null チェックを行う
- Optional を使う
- 「未設定=無期限」など、仕様を明確に決める
比較ロジックを書く前に、null の意味を設計で決めておくことが重要です。
3.7 まとめ:java.time での比較はこれだけ覚えればOK
- 前後判定 →
isBefore/isAfter - 同一判定 →
isEqual - 並び替え →
compareTo - 同日判定 →
toLocalDate()して比較
これらを押さえれば、日付比較の8割以上は対応可能です。
4. java.util.Date で日付を比較する(レガシー対応)
現場によっては、今でも java.util.Date を避けられないケースがあります。
古いシステム、既存ライブラリ、外部APIとの連携などが理由です。
ここでは 「Dateをどう安全に扱うか」 に絞って解説します。
4.1 Date 同士の前後比較:before / after
Date には、前後を判定するためのメソッドが用意されています。
Date d1 = new Date(126, 0, 9); // 2026-01-09
Date d2 = new Date(126, 0, 10); // 2026-01-10
System.out.println(d1.before(d2)); // true
System.out.println(d1.after(d2)); // false一見すると問題なさそうですが、ここには注意点があります。
- 内部的には 日時(ミリ秒単位)で比較される
- 「同日かどうか」の比較には向かない
4.2 compareTo / equals の落とし穴
Date は Comparable を実装しているため、compareTo() も使えます。
int result = d1.compareTo(d2);また、equals() も存在します。
boolean same = d1.equals(d2);しかし初心者がよく勘違いするのがここです。
- 同じ日付でも、時刻が違えば equals は false
- compareTo も「日」ではなく「日時」で比較される
つまり、次のようなケースでは一致しません。
- 2026-01-09 00:00
- 2026-01-09 12:00
人間の感覚では「同じ日」ですが、Date では別物です。
4.3 Date をそのまま使い続けない方がいい理由
Date を使い続けると、次のような問題が起きやすくなります。
- 同日判定が直感通りに書けない
- タイムゾーンの影響がコードから見えない
- バグの原因が分かりにくい
そのため、比較や判定を行う前に java.time に変換するのが安全です。
4.4 Date → java.time への安全な変換方法
Date は Instant に変換できます。
Date date = new Date();
Instant instant = date.toInstant();そこから目的に応じて型を選びます。
日付として扱いたい場合
LocalDate localDate =
instant.atZone(ZoneId.systemDefault()).toLocalDate();日時として扱いたい場合
LocalDateTime localDateTime =
instant.atZone(ZoneId.systemDefault()).toLocalDateTime();ここで重要なのは、
- どのタイムゾーンで解釈するかを明示している点
これにより、「いつの間にかズレていた」という事故を防げます。
4.5 変換後は java.time の比較メソッドを使う
一度 LocalDate や LocalDateTime に変換してしまえば、
比較はこれまで説明してきた方法で安全に行えます。
LocalDate a = localDate;
LocalDate b = LocalDate.now();
if (a.isBefore(b)) {
System.out.println("aは今日より前");
}Dateは入口だけ、ロジックはjava.time
これが現代Javaのベストプラクティスです。

4.6 どうしても Date 同士で比較する場合の指針
やむを得ず Date のまま扱う場合は、次を守ると事故が減ります。
- 「同日判定」はしない(日時として割り切る)
- 比較は
before / afterのみに限定する - 仕様書やコメントで「日時比較である」ことを明示する
4.7 まとめ:Date を使うなら最短距離で抜ける
- DateはレガシーAPI
- 比較はできるが、直感とズレやすい
- 早めに java.time に変換するのが最善
5. 文字列(String)の日付を比較するなら「必ずパースしてから」
日付比較で非常に多い失敗が、「文字列のまま比較してしまう」ことです。
見た目が日付でも、Stringは日付ではありません。ここを誤ると、静かにバグが混入します。
5.1 文字列のまま比較してはいけない理由
たとえば、次の2つの文字列を比較するとどうなるでしょうか。
String a = "2026-1-9";
String b = "2026-01-10";
System.out.println(a.compareTo(b));一見すると「aの方が前」になりそうですが、文字列比較は辞書順です。
"2026-1-9""2026-01-10"
この2つは フォーマットが揃っていないため、結果は直感通りになりません。
さらに、
"2026-10-01""2026-2-01"
のようなケースでは、ほぼ確実に破綻します。
結論:日付の比較を、Stringの比較で行ってはいけない。
5.2 正しい手順は「String → 日付型 → 比較」
文字列の日付を比較したい場合、正しい手順は常に同じです。
- 文字列を 日付型に変換(パース)
- 日付型同士で比較
ISO形式(yyyy-MM-dd)の場合
String s1 = "2026-01-09";
String s2 = "2026-01-10";
LocalDate d1 = LocalDate.parse(s1);
LocalDate d2 = LocalDate.parse(s2);
System.out.println(d1.isBefore(d2)); // trueISO形式であれば、LocalDate.parse() だけで安全に変換できます。
5.3 フォーマットが異なる場合は DateTimeFormatter を使う
実務では、次のような形式をよく見かけます。
2026/01/092026-01-09 18:3009-01-2026
この場合は、フォーマットを明示してパースします。
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy/MM/dd");
LocalDate date =
LocalDate.parse("2026/01/09", formatter);日時を含む場合は LocalDateTime を使います。
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime =
LocalDateTime.parse("2026-01-09 18:30", formatter);5.4 フォーマットが複数ある場合の考え方
外部入力やCSV、API連携では、日付フォーマットが揺れることがあります。
この場合の基本方針は次のいずれかです。
- 事前にフォーマットを正規化する
- 複数の
DateTimeFormatterを用意して順に試す - 入力仕様としてフォーマットを固定する(最善)
例(順に試す簡易例):
List<DateTimeFormatter> formatters = List.of(
DateTimeFormatter.ofPattern("yyyy-MM-dd"),
DateTimeFormatter.ofPattern("yyyy/MM/dd")
);
LocalDate parse(String text) {
for (DateTimeFormatter f : formatters) {
try {
return LocalDate.parse(text, f);
} catch (Exception ignored) {}
}
throw new IllegalArgumentException("不正な日付形式");
}5.5 例外処理と入力チェックの重要性
parse() は、形式が合わないと例外を投げます。
LocalDate.parse("2026-99-99"); // 例外そのため、次の点を意識しましょう。
- ユーザー入力は必ず例外を想定する
- 「不正な日付」をどう扱うかを仕様として決める
- ログに残して原因追跡できるようにする
5.6 「同日判定」「前後判定」はパース後に行う
文字列を日付型に変換した後は、
これまで解説した isBefore / isAfter / isEqual を使います。
if (d1.isEqual(d2)) {
System.out.println("同じ日付");
}ここまで来て、ようやく安全な日付比較になります。
5.7 まとめ:String比較は近道に見えて遠回り
- Stringのまま日付を比較しない
- 必ず
LocalDate/LocalDateTimeに変換する - フォーマットは明示する
- 例外処理を前提に設計する
6. 範囲チェック(期間内か?締切を過ぎたか?)の定番実装
実務で非常によく使われるのが、「ある日付(日時)が、指定した期間内に含まれるか」という判定です。
予約、キャンペーン、契約期間、権限の有効期限など、用途は多岐にわたります。
6.1 基本形:「開始 ≤ 対象 ≤ 終了」を明確に書く
まず大切なのは、含める/含めない(inclusive / exclusive)をはっきり決めることです。
ここが曖昧だと、後から仕様バグになります。
日付(LocalDate)の範囲チェック例(両端含む)
LocalDate start = LocalDate.of(2026, 1, 1);
LocalDate end = LocalDate.of(2026, 1, 31);
LocalDate target = LocalDate.of(2026, 1, 15);
boolean inRange =
!target.isBefore(start) && !target.isAfter(end);この書き方の意味は次の通りです。
!isBefore(start)→ start より前ではない(start 以上)!isAfter(end)→ end より後ではない(end 以下)
「開始以上、終了以下」という意図がそのままコードに現れます。
6.2 片側を含めない場合(よくある締切判定)
「締切を過ぎていないか」のような判定では、
終了日は含めないケースもよくあります。
boolean beforeDeadline = target.isBefore(deadline);この場合、
- deadline 当日は 締切後
- deadline より前だけが有効
という仕様になります。
inclusive / exclusive をコメントやメソッド名で明示するのがベストです。
6.3 LocalDateTime の範囲チェック
日時の場合も考え方は同じです。
LocalDateTime start =
LocalDateTime.of(2026, 1, 9, 18, 0);
LocalDateTime end =
LocalDateTime.of(2026, 1, 9, 20, 0);
LocalDateTime target =
LocalDateTime.of(2026, 1, 9, 19, 0);
boolean inRange =
!target.isBefore(start) && target.isBefore(end);この例では、
- 開始時刻は含む
- 終了時刻は含まない
という、予約・利用時間でよく使われる仕様です。
6.4 null を含む期間の扱い(実務の落とし穴)
現場では、次のようなデータをよく見かけます。
- 開始日が未設定(いつからでも有効)
- 終了日が未設定(無期限)
この場合、null をどう解釈するかを先に決める必要があります。
例:null は制限なしとする
boolean inRange(LocalDate target,
LocalDate start,
LocalDate end) {
if (start != null && target.isBefore(start)) {
return false;
}
if (end != null && target.isAfter(end)) {
return false;
}
return true;
}このように関数化すると、
- 仕様が一箇所に集約される
- 判定ロジックの再利用ができる
- バグが入りにくくなる
というメリットがあります。
6.5 「今日」を含めるかどうかの注意点
「今日が期間内か?」という判定は、意外とバグりやすいです。
LocalDate today = LocalDate.now();ここで重要なのは、
- サーバーのタイムゾーンはどこか
- ユーザー基準の日付か、システム基準か
必要に応じて、ZoneId を明示しましょう。
LocalDate today =
LocalDate.now(ZoneId.of("Asia/Tokyo"));6.6 範囲チェックをメソッド化するメリット
範囲チェックは、そのまま書き散らすより、メソッド化がおすすめです。
boolean isWithinPeriod(LocalDate target,
LocalDate start,
LocalDate end) {
return (start == null || !target.isBefore(start))
&& (end == null || !target.isAfter(end));
}- 名前で仕様が分かる
- 条件の変更に強い
- テストしやすい
実務では、この形が最も安定します。
6.7 まとめ:範囲チェックは「仕様をコードに刻む」
- inclusive / exclusive を明確にする
- null の意味を決める
- メソッド化して再利用する
- タイムゾーンを意識する
7. 差分計算:日数差・経過時間を正しく求める(Period / Duration)
日付比較の次に多い要件が、「どれくらい離れているか」を求める処理です。
Javaでは目的に応じて Period と Duration を使い分けます。
7.1 日付ベースの差分:Period(年・月・日)
カレンダー上の差を求めたい場合は Period を使います。
LocalDate start = LocalDate.of(2026, 1, 1);
LocalDate end = LocalDate.of(2026, 1, 31);
Period period = Period.between(start, end);
System.out.println(period.getDays()); // 30
System.out.println(period.getMonths()); // 0
System.out.println(period.getYears()); // 0Period の特徴は次の通りです。
- 年・月・日という 人間の感覚に近い単位
- 月の日数(28〜31日)を考慮する
- 「何日後か」「何か月後か」を表現しやすい
日数だけが欲しい場合の注意点
long days = ChronoUnit.DAYS.between(start, end);こちらは 純粋な日数を返します。
「月をまたぐかどうか」を気にしない場合はこちらが便利です。
7.2 時間ベースの差分:Duration(時・分・秒)
時刻を含む差分を求めたい場合は Duration を使います。
LocalDateTime t1 =
LocalDateTime.of(2026, 1, 9, 18, 0);
LocalDateTime t2 =
LocalDateTime.of(2026, 1, 9, 20, 30);
Duration duration = Duration.between(t1, t2);
System.out.println(duration.toHours()); // 2
System.out.println(duration.toMinutes()); // 150特徴は次の通りです。
- 秒・分・時間といった 時間ベース
- 1日は常に 24時間として扱われる
7.3 Durationの落とし穴:1日は常に24時間とは限らない?
Duration は「時間の長さ」を表すため、
サマータイム(DST)のある地域では注意が必要です。
- 1日が23時間になる日
- 1日が25時間になる日
このような日は、ZonedDateTime + Duration で扱うと結果が直感とズレることがあります。
7.4 差分計算の使い分け指針
目的別に整理すると、次のようになります。
- 日付の差(年/月/日) →
Period - 純粋な日数 →
ChronoUnit.DAYS.between - 時間の差(時/分/秒) →
Duration - 世界共通の経過時間 →
Instant+Duration
7.5 まとめ:差分は「何を知りたいか」で決める
- 人間向け表示 → Period
- システム処理 → Duration
- 日数カウント → ChronoUnit
8. タイムゾーンの落とし穴:ズレる比較・ズレない比較
日付・日時のバグで最も厄介なのが、タイムゾーンによるズレです。
「ローカルでは正しいのに、本番で壊れる」典型例でもあります。
8.1 LocalDateTime は「どこの時間か」を知らない
LocalDateTime now = LocalDateTime.now();この時刻は、
- サーバーがどこにあるか
- 実行環境の設定
に依存します。
LocalDateTime にはタイムゾーンの概念がありません。
そのため、国をまたぐ処理には向きません。
8.2 ZonedDateTime:どこの日時かを明示する
ZonedDateTime tokyo =
ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
ZonedDateTime newYork =
ZonedDateTime.now(ZoneId.of("America/New_York"));- 同じ瞬間でも、表示される日時は異なる
- 表示・UI・ユーザー向け処理に最適
8.3 Instant:比較・保存に最強の型
Instant a = Instant.now();
Instant b = Instant.now();
System.out.println(a.isBefore(b));Instant は、
- タイムゾーンを持たない
- UTC基準の「瞬間」
という性質があります。
比較・DB保存・ログ記録は Instant
これは実務で非常に強力な設計指針です。
8.4 安全な設計パターン
よく使われる安全な流れは次の通りです。
- 入力 → ZonedDateTime(ユーザーのタイムゾーン)
- 内部処理・保存 → Instant
- 表示 → ZonedDateTime に変換
これにより、タイムゾーン起因のバグを大幅に減らせます。
9. まとめ:迷ったときの選び方チートシート
最後に、この記事全体の要点を整理します。
- 日付だけを比較したい
→LocalDate - 日時を比較したい
→LocalDateTime - 世界共通で比較・保存したい
→Instant - ユーザー向け表示
→ZonedDateTime
比較方法の基本
- 前後判定 →
isBefore/isAfter - 同一判定 →
isEqual - 並び替え →
compareTo
よくある失敗を避けるために
- Stringのまま比較しない
- Dateは早めに java.time に変換
- タイムゾーンを意識する
- inclusive / exclusive を明確にする
FAQ
Q1. Javaで日付比較は Date と LocalDate のどちらを使うべき?
基本は LocalDate / LocalDateTime です。Date はレガシーで、比較の意図がコードに表れにくいため、可能な限り java.time を使いましょう。
Q2. 同じ日かどうかを判定したい場合は?
LocalDateTime を toLocalDate() に変換してから isEqual() で比較するのが安全です。
Q3. 文字列の日付を比較したいときの最短ルートは?
必ず パースしてから比較してください。LocalDate.parse() または DateTimeFormatter を使います。
Q4. タイムゾーンが違う環境で比較するには?
内部処理・保存は Instant、表示は ZonedDateTime が最も安全です。

