「Javaで日付を比較したいけど、equals()とcompareTo()とisEqual()のどれを使うのが正解?」。
結論から言うと、Java 8以降の現代では LocalDate+isBefore()/isAfter()/isEqual() が正解です。JavaでLocalDateを比較するなら、この3メソッドで足ります。
「以上・以下」は !isBefore()/!isAfter()、期間内の判定はその組み合わせで書けます。LocalDateTimeの比較、Dateとの違い、今日との比較も後半で扱います。
旧 Date と Timestamp を equals() で比べると、呼び出す向きによって結果が変わります(timestamp.equals(date) はミリ秒が同じでも false)。新人時代にこれで本番障害を起こした体験(Java歴12年)から、落とし穴まで整理しました。
この記事を読むと次のことがわかります。
この記事でわかること
- Date と LocalDate の比較メソッド一覧と「同じか/前後か/差分」3パターン別の正解コード
- LocalDateで「以上・以下」「期間内か」を判定する書き方と、文字列(yyyyMMdd)の日付を比較する方法
equals()の罠・タイムゾーンずれ・SimpleDateFormatのスレッドセーフ問題などのよくあるエラー- 新規/レガシー/パフォーマンス重視のシーン別おすすめ
PARTS LIST /目次
先に結論|Java日付比較メソッド早見表
長文に入る前に、Date と LocalDate(java.time)でどのメソッドを使えばよいかを1枚にまとめます。迷ったらここに戻ってください。
Java 8以降の新規実装は LocalDate 系で固定でOKです。レガシー保守でだけ右列を読みかえてください。
Javaで日付を比較する基本方法
Javaには、大きくわけて2つの日付APIが存在します。
Java 7以前のjava.util.Dateと、Java 8(2014年リリース)から導入された java.time パッケージ(LocalDateなど)です。
2026年現在の主流は Java 21 LTS であり、新規実装で Date を選ぶ理由はありません。
どちらを使うかによって、日付の比較方法は大きく異なります。まずは、昔からあるDateクラスでの比較の基本をみていきましょう。
Dateクラスを使った日付比較の仕組み
java.util.Dateクラスは、その名前とはうらはらに「日付」だけをあつかうものではありません。
Dateオブジェクトの実体は、「エポック」(1970年1月1日 0時0分0秒)からの経過時間をミリ秒であらわした数値(long型)です。
long time = System.currentTimeMillis();
Date date = new Date(time);
つまり、Dateオブジェクトは常に「時・分・秒・ミリ秒」までの情報をもっています。
「2025年11月1日」というDateオブジェクトを作ったつもりでも、内部的には「2025年11月1日 0時0分0秒0ミリ秒」のようなタイムスタンプとしてあつかわれます。
この「ミリ秒までのタイムスタンプである」という点が、日付比較を行ううえで最初の注意点となります。
compareTo()とequals()の違いを理解しよう
Dateオブジェクトを比較するメソッドとして、equals()とcompareTo()があります。この2つは、まったくちがう動きをします。
equals(Object obj)メソッドは、2つのオブジェクトが「等しい」かを判定します。
しかし、Dateのequals()は、比較対象がDate型であり、かつ内部のミリ秒がまったく同じである場合のみtrueを返します。
ここで大きな問題になるのが、データベースから取得したjava.sql.Timestampなど、Dateのサブクラスとの比較です。
// 2025-11-02 00:00:00 UTC のDate
Date d1 = new Date(1762041600000L);
// 同じミリ秒のTimestamp (Dateを継承)
java.sql.Timestamp t1 = new java.sql.Timestamp(1762041600000L);
System.out.println(d1.equals(t1)); // 結果: true (Date側のequals()はgetTime()だけを比べる)
System.out.println(t1.equals(d1)); // 結果: false (Timestamp側のequals()はTimestamp以外をfalseにする)
これがよくハマる罠です。Dateのequals()は、相手がDate(サブクラスのTimestampを含む)で、getTime()のミリ秒が同じならtrueを返します。ところが、データベースから取得したjava.sql.Timestamp側のequals()は、相手がTimestampでないと常にfalseを返します。つまりdate.equals(timestamp)はtrue、timestamp.equals(date)はfalseと、呼び出す向きによって結果が変わります(equals()の対称性が崩れています)。
compareTo() はミリ秒を比べる
compareTo(Date anotherDate)メソッドは、2つのDateオブジェクトの内部的なミリ秒(long値)を比較します。
date1<date2ならマイナスの値date1==date2なら 0date1>date2ならプラスの値
compareTo()はequals()と違い、Timestampのようなサブクラスであっても、ミリ秒の値だけをきちんと比較してくれます。
System.out.println(d1.compareTo(t1)); // 結果: 0 (ミリ秒が同じ)
before() と after() を使うのが基本
Dateクラスで日付の前後を比較する場合は、before()とafter()メソッドを使うのが一般的です。
date1.before(date2):date1がdate2より過去ならtruedate1.after(date2):date1がdate2より未来ならtrue
これらのメソッドは、compareTo()と同様に内部でミリ秒値(サブクラスの場合はgetTime())を比べているため、Timestampとの比較も安全におこなえます。
// 2025-11-01 00:00 UTC と 2025-11-02 00:00 UTC
Date date1 = new Date(1761955200000L);
Date date2 = new Date(1762041600000L);
System.out.println(date1.before(date2)); // 結果: true
System.out.println(date1.after(date2)); // 結果: false
ミリ秒単位の比較に注意すべき理由
before() after() compareTo() は、ミリ秒までを厳密に比較します。
これが問題になるのは、「日付(年月日)だけ」を比べたいときです。
たとえば、「2025年11月1日 8時0分」と「2025年11月1日 10時0分」は、日付は同じですが、ミリ秒の値はちがいます(ミリ秒の値と日時の対応はUnixタイムスタンプ変換ツールで確認できます)。
// 2025-11-01 08:00:00 UTC
Date dateA = new Date(1761984000000L);
// 2025-11-01 10:00:00 UTC
Date dateB = new Date(1761991200000L);
System.out.println(dateA.compareTo(dateB)); // 結果: -1 (dateAのほうが過去)
System.out.println(dateA.equals(dateB)); // 結果: false
もし「同じ日付か」を判定したい場合、Dateオブジェクトの「時・分・秒・ミリ秒」をすべて0にそろえる処理が必要でした。
// 昔の方法:Calendarをつかって時刻を切りすてる
Calendar cal = Calendar.getInstance();
cal.setTime(dateA);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Date truncatedDateA = cal.getTime();
Dateクラスで「日付だけ」をあつかうのは、手間がかかりバグを生みやすいものでした。
Java8以降のDateとLocalDateの違い
このようなDateクラスの問題点を解決するために、Java 8でjava.timeパッケージが導入されました。
dateA.compareTo(dateB) → -1dateA.equals(dateB) → false時・分・秒・ミリ秒まで持つため、同じ2025年11月1日でも2時間分の差がある
localA.isEqual(localB) → true時刻を持たないので、どちらも同じ日付になる
もしあなたがJava8以降のバージョンを使っているなら、Dateクラスではなく、java.timeパッケージのクラス(LocalDateなど)を使うべきです。
LocalDate・LocalDateTimeの登場背景
Java 8で新しい日付API(JSR-310、2014年リリース)が導入されたのには、明確な理由があります。
- DateオブジェクトはsetTime()メソッドで内部のミリ秒をあとから変えられます。オブジェクトをメソッドにわたしたとき、中で値が変えられてしまう可能性があり、安全ではありません。java.timeのクラス(LocalDateなど)は不変です。plusDays()などで日付を操作しても、元のオブジェクトは変わらず、かならず新しいオブジェクトが返されます。これにより、安全に日付をあつかえます。
- 目的が明確である:
Dateは常にタイムスタンプでした。Java 8では、目的にあわせてクラスがわかれています。
LocalDate: 日付(YYYY-MM-DD)のみ。時刻やタイムゾーンをもちません。LocalTime: 時刻(HH:mm:ss)のみ。日付やタイムゾーンをもちません。LocalDateTime: 日付と時刻(YYYY-MM-DDTHH:mm:ss)をあつかいます。ZonedDateTime: タイムゾーンをふくめた日付と時刻をあつかいます。
- APIが直感的である:
Dateでは日付の計算にCalendarクラスが必要で、月の指定が0からはじまる(1月が0)など、あつかいにくい点が多くありました。LocalDateはplusDays(1)(1日たす)のように、直感的なメソッドがそろっています。
旧Dateクラスと新APIの比較
DateとJava8のLocalDate / LocalDateTimeは、まったくの別物です。両者のちがいをまとめます。
とくに「不変(Immutable)」である点は、複雑な業務アプリケーションでのバグ防止に直結します。
isBefore()・isAfter()・isEqual()の使い方
Java8のjava.timeパッケージでは、日付比較のメソッド名が統一され、格段にわかりやすくなりました。
Dateのbefore() after() equals() compareTo()の複雑さはありません。
LocalDateもLocalDateTimeも、以下の3つのメソッドで比較します。
isBefore(): 過去かisAfter(): 未来かisEqual(): 同じか
LocalDate date1 = LocalDate.of(2025, 11, 1);
LocalDate date2 = LocalDate.of(2025, 11, 5);
LocalDate date3 = LocalDate.of(2025, 11, 1);
// date1 は date2 より過去か
System.out.println(date1.isBefore(date2)); // 結果: true
// date1 は date2 より未来か
System.out.println(date1.isAfter(date2)); // 結果: false
// date1 は date3 と同じか
System.out.println(date1.isEqual(date3)); // 結果: true
LocalDateの場合、isEqual()は「年月日」がすべて一致すればtrueを返します。Dateのequals()のような型の問題はおきません。
LocalDateTimeのisEqual()は、ミリ秒のさらに下、「ナノ秒」までが一致しないとtrueにならない点には注意が必要です。
a.isBefore(b)a.isAfter(b)a.isEqual(b)LocalDateで「以上・以下」「期間内」を判定する書き方
isBefore()とisAfter()は、どちらも同じ日付のときはfalseを返します。つまり「より前」「より後」の判定であり、同日を含みません。
start 以上
end 以下
(両端を含む)
! を付けると、start 当日・end 当日も期間内に入ります。「以上(同日を含む)」「以下(同日を含む)」を判定したいときは、反対側のメソッドを!で否定するのが定番です。期間内(開始日〜終了日、両端を含む)の判定も、この2つを&&でつなぐだけで書けます。
LocalDate target = LocalDate.of(2025, 11, 10);
LocalDate start = LocalDate.of(2025, 11, 1);
LocalDate end = LocalDate.of(2025, 11, 30);
// target が start 以上(同日を含む)か
boolean onOrAfter = !target.isBefore(start); // 結果: true
// target が end 以下(同日を含む)か
boolean onOrBefore = !target.isAfter(end); // 結果: true
// 期間内(start〜end、両端を含む)か
boolean inRange = !target.isBefore(start) && !target.isAfter(end); // 結果: true
// compareTo() は「符号」だけを見る(値は-1/0/1とは限らない)
System.out.println(target.compareTo(start) > 0); // 結果: true
// 文字列(yyyyMMdd)は LocalDate に変換してから比較する
LocalDate fromString = LocalDate.parse("20251115", DateTimeFormatter.BASIC_ISO_DATE);
System.out.println(fromString.isAfter(target)); // 結果: true
LocalDate.compareTo()は、年・月・日の順に差を返す実装のため、戻り値が-1/0/1になるとは限りません。== 1のように値そのものを比べず、> 0/< 0と符号だけで判定してください。可読性の面でも、条件分岐にはisBefore()/isAfter()を使うほうが意図が伝わります。
「20251115」のようなyyyyMMdd形式の文字列どうしも、桁数がそろっていれば文字列比較で大小は判定できます。ただし形式の崩れた値が混ざっても気づけないため、DateTimeFormatter.BASIC_ISO_DATEでLocalDateに変換してから比較するのが安全です。
LocalDateTimeの比較(時刻を含む場合・日付だけ比べたい場合)
LocalDateTimeもisBefore()/isAfter()/isEqual()で比較できます。ただし時刻・ナノ秒まで見るため、「同じ日か」を知りたいときはtoLocalDate()で日付だけに変換してから比べます。秒以下の違いを無視したいときはtruncatedTo()で精度をそろえます。
LocalDateTime a = LocalDateTime.of(2025, 11, 1, 8, 0);
LocalDateTime b = LocalDateTime.of(2025, 11, 1, 10, 0);
// 時刻まで含めて比べる
System.out.println(a.isBefore(b)); // true
System.out.println(a.isEqual(b)); // false
// 日付だけ比べる(toLocalDate()で時刻を捨てる)
System.out.println(a.toLocalDate().isEqual(b.toLocalDate())); // true
// 秒以下の違いを無視して比べる(分までに丸める)
LocalDateTime c = LocalDateTime.of(2025, 11, 1, 8, 0, 30);
LocalDateTime d = LocalDateTime.of(2025, 11, 1, 8, 0, 45);
System.out.println(c.truncatedTo(ChronoUnit.MINUTES)
.isEqual(d.truncatedTo(ChronoUnit.MINUTES))); // true
2つの値の型が違う場合(LocalDateとLocalDateTime)は、isBefore()にそのまま渡せません。LocalDate側をatStartOfDay()で0時のLocalDateTimeにするか、LocalDateTime側をtoLocalDate()で日付にそろえてから比較してください。
LocalDate比較の実装パターン別サンプルコード
Java8のLocalDateを使ったほうが、コードがどれだけスッキリするか、パターン別にみてみましょう。
2つの日付が同じか判定するコード例
旧 Date の場合(非推奨)
equals()は危険なため、compareTo()が0か、または日付をまるめて(時刻を0にして)比較する必要がありました。
// 時刻を切りすてるユーティリティ (例: Apache Commons Lang)
// DateUtils.truncate(date, Calendar.DAY_OF_MONTH) などが必要
// もし時刻が0であることが保証されているなら...
if (date1.compareTo(date2) == 0) {
System.out.println("同じ日付です");
}
新 LocalDate の場合 (推奨)
LocalDateは時刻情報をもたないため、isEqual()だけで安全に比較できます。
LocalDate date1 = LocalDate.of(2025, 11, 1);
LocalDate date2 = LocalDate.of(2025, 11, 1);
if (date1.isEqual(date2)) {
System.out.println("同じ日付です");
}
コードのシンプルさと安全性がまったくちがいます。
指定日より前後かを判定する方法
旧 Date の場合(非推奨)
before() と after() を使います。
Date date1 = new Date(1761955200000L); // 2025-11-01 UTC
Date date2 = new Date(1762041600000L); // 2025-11-02 UTC
if (date1.before(date2)) {
System.out.println("date1 は date2 より過去です");
}
新 LocalDate の場合 (推奨)
isBefore() と isAfter() を使います。
LocalDate date1 = LocalDate.of(2025, 11, 1);
LocalDate date2 = LocalDate.of(2025, 11, 2);
if (date1.isBefore(date2)) {
System.out.println("date1 は date2 より過去です");
}
どちらも書き方は似ていますが、LocalDateは時刻情報にまどわされる心配がありません。
日付の差分(日数)を求めるサンプル
旧 Date の場合(非推奨)
ミリ秒の差をもとめ、1日のミリ秒(1000 * 60 * 60 * 24)で割る必要がありました。
long diffInMillis = date2.getTime() - date1.getTime();
long diffInDays = diffInMillis / (1000 * 60 * 60 * 24);
System.out.println("差は " + diffInDays + " 日です");
この方法は、サマータイム(夏時間)がある地域では、日付の変わり目で計算がずれる危険性があります。
新 LocalDate の場合 (推奨)
ChronoUnitという便利なクラスを使います。
LocalDate date1 = LocalDate.of(2025, 11, 1);
LocalDate date2 = LocalDate.of(2025, 11, 10);
long diffInDays = ChronoUnit.DAYS.between(date1, date2);
System.out.println("差は " + diffInDays + " 日です"); // 結果: 9
between(A, B)はBからAを引いた日数を返します。安全で、一行で計算できます。
現在の日付(今日)と比較するコード例
「指定した日付が今日より前か」「今日かどうか」といった比較は、LocalDate.now() と組み合わせれば1〜2行で書けます。
LocalDate today = LocalDate.now();
LocalDate target = LocalDate.of(2026, 5, 1);
if (target.isBefore(today)) {
System.out.println("targetは過去の日付です");
} else if (target.isEqual(today)) {
System.out.println("targetは今日です");
} else {
System.out.println("targetは未来の日付です");
}
// 「今日から30日以内か」のような期間判定
long days = ChronoUnit.DAYS.between(today, target);
if (days >= 0 && days <= 30) {
System.out.println("30日以内の予定です");
}
ユニットテストで LocalDate.now() の戻り値を固定したいときは、Clock を引数で受け取り LocalDate.now(clock) と書く設計にしておくと、テスト時に Clock.fixed(...) を差し込めます。実務では必須テクニックです。
日付比較でよくあるエラーと注意点
最後に、日付比較のあつかいで、いまでもよく見かけるエラーや注意点をまとめます。
タイムゾーンの違いによるズレ
java.util.Dateは、内部的にはUTC(協定世界時)のミリ秒をもちますが、toString()などで表示するときは、Javaが動いているサーバーのデフォルトタイムゾーンを使います。
日本のサーバー(JST)と、海外のサーバー(UTC)では、同じnew Date()でも、ちがう時刻をさしているように見えるため、混乱のもとになります。
Java8のLocalDateやLocalDateTimeは、タイムゾーン情報をもちません。
タイムゾーンをあつかうときは、ZonedDateTime(または時点だけならInstant・UTCオフセット付きならOffsetDateTime)を使います。
// タイムゾーンを明確に指定する (Java8)
ZoneId zoneTokyo = ZoneId.of("Asia/Tokyo");
ZonedDateTime zdtTokyo = ZonedDateTime.now(zoneTokyo);
タイムゾーンを意識しない日付比較は、システム障害につながる可能性があります。
フォーマット変換時のParseException
文字列を日付オブジェクトに変換するとき、ParseExceptionがよく発生します。
旧 Date の場合
SimpleDateFormatを使いますが、このクラスはスレッドセーフではありません。
Java公式のAPIドキュメントでも、SimpleDateFormatは「同期されていない」、DateTimeFormatterは「不変でスレッドセーフ」と明記されています。SimpleDateFormat はレガシーコード保守時のみで利用するのが安全です。
Webアプリケーションなどで、SimpleDateFormatをstatic変数(共通の変数)にして使いまわすと、同時にリクエストが来たときに内部状態がこわれ、予期せぬエラーや間違った日付変換がおこります。
新 LocalDate の場合
DateTimeFormatter を使います。こちらはスレッドセーフのため、static final 変数にして安全に使いまわせます。
// 安全(スレッドセーフ)
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd");
public LocalDate parseDate(String text) {
return LocalDate.parse(text, FORMATTER);
}
DateとLocalDateの混在による型エラー
古いシステムを改修していると、古いDateと新しいLocalDateが混ざってしまうことがあります。
この2つはまったく別の型のため、直接比較できません。どちらかに変換します。
Date から LocalDate へDate date = new Date();
ZoneId zone = ZoneId.systemDefault();
LocalDate localDate = date.toInstant().atZone(zone).toLocalDate();
LocalDate から Date へ
LocalDate localDate = LocalDate.now();
ZoneId zone = ZoneId.systemDefault();
Date date = Date.from(localDate.atStartOfDay(zone).toInstant());
コードが複雑になるため、できるかぎりLocalDate(java.time)に統一するのが理想です。
JDBC 4.2 / JPA 2.2 の対応に加え、Jackson も jackson-datatype-jsr310(JavaTimeModule)を使えば(Spring Boot は自動で登録)、DBやJSONとも LocalDate 直結で扱えるため、APIの境界面まで java.time で揃えるのが現実的です。
とはいえレガシー混在のプロジェクトで「うっかり java.util.Date を新規追加してしまう」事故は起きがちです。
僕のチームでは Error Prone(Google製)の JavaUtilDate チェック をCIに組み込んで、新規の java.util.Date 利用を機械的に検出しています。
// build.gradle (例: Error Prone)
plugins {
id "net.ltgt.errorprone" version "3.1.0"
}
dependencies {
errorprone "com.google.errorprone:error_prone_core:2.27.0"
}
// 検出例:
// MyService.java:42: warning: [JavaUtilDate]
// Date has a bad API that leads to bugs; prefer java.time.Instant or LocalDate.
// Date d = new Date();
// ^
仕組みで強制すれば、レビュー漏れによる「気づいたら Date まみれ」を防げます。
シーン別おすすめの日付比較方法まとめ
ここまで解説した内容をふまえ、どのような場合にどの比較方法を選ぶべきかをまとめます。
まず、やりたい判定から使うメソッドを選ぶ流れを図にすると次のようになります。
isEqual()isBefore() / isAfter()!isBefore() / !isAfter()!isBefore(start) && !isAfter(end)ChronoUnit.DAYS.between()業務アプリ開発で安全な比較方法
java.time (LocalDate, LocalDateTime) が推奨されます。
Java 8以降の環境であれば、DateやCalendarを使う理由はありません。
2026年現在は Java 21 LTS(または Java 17 LTS)が業務標準のため、ほぼすべての現場で java.time を使えます。
不変(Immutable)でスレッドセーフなjava.timeパッケージを使い、isBefore() isAfter() isEqual() で比較するのが、もっとも安全でバグのない方法です。
Java 7以前のレガシー環境での比較の選び方
もし、どうしてもJava 7以前の古い環境で動かす必要がある場合、Dateクラス(または Joda-Time)を使うしかありません。
Java 7はOracleの公式サポートが2022年7月に終了しているため、本番環境ならまずJVMアップグレードを検討するのが2026年現在の定石です。
その場合は、equals()は絶対に使わず、before() after() compareTo() を使って比較します。
また、日付(年月日)だけを比べたいときは、時刻情報を切りすてる処理(Calendarを使うか、ライブラリのDateUtils.truncateなど)を忘れないようにします。
パフォーマンスを意識した比較のコツ
数百万件のデータをループで処理するなど、パフォーマンスがもとめられる場合、比較のコストが気になるかもしれません。
Dateの比較:date1.getTime() == date2.getTime()のように、long値どうしで比較するのがもっとも直接的です。compareTo()やbefore()も内部でミリ秒値を取り出して比べているだけなので、速度の差はほとんどありません。LocalDateの比較:isEqual()やcompareTo()は十分に高速です。
参考までに、僕の手元(Apple M4 / Java 21 / JMH 1.37)で計測した1回あたりの時間と、それを1億回分に換算した値は次のとおりです。JITの最適化が効くため、実際の処理の中での値は条件によって変わります。
LocalDate.isEqual() がわずかに遅いものの、1ナノ秒に満たない差なので業務アプリでは無視できる範囲です。
読みやすさと安全性のメリットの方が圧倒的に大きいので、迷わず LocalDate 系を使ってください。
ただし、パフォーマンスのボトルネックになるのは、日付の「比較」よりも、「文字列からの変換(パース)」や「オブジェクトの生成」であることがほとんどです。
ループの中でnew Date()やLocalDate.parse()をくりかえすのはさけ、比較そのものはjava.timeのメソッドをそのまま使うのが、コードの読みやすさと性能のバランスがとれたよい方法です。
私が実際にハマった日付比較バグTOP3
12年Javaで開発してきた中で、自分や周りが踏んだ日付比較バグのワースト3を共有します。同じ轍を踏まないでください。
- Date と Timestamp を equals() 比較(冒頭の体験談)—
timestamp.equals(date)の向きだと、ミリ秒が同じでも常にfalse。compareTo() または LocalDate に統一すれば回避。 - SimpleDateFormat を static で共有— 高負荷時にフォーマット結果が壊れて月が「12月」が「24月」になった事例あり。DateTimeFormatterに置き換えるだけで解決。
- UTCサーバーで
new Date()をそのまま比較— 日本時間9時前のデータが「前日扱い」されて締め処理が1日ずれた。ZonedDateTime.now(ZoneId.of("Asia/Tokyo"))でゾーンを明示すれば回避。
すべて「java.time を最初から使う」「タイムゾーンを必ず明示する」の2点を守れば防げたバグです。
まとめ:Java日付比較はLocalDateで安全に
Javaの日付比較は、Dateクラスの「ミリ秒のタイムスタンプである」という性質と、equals()のワナを理解していないと、手ごわいバグにつながります。
- 旧
Date:before()after()compareTo()で比較する。equals()は使わない。 - 新
LocalDate(Java8以降):isBefore()isAfter()isEqual()で比較する。
これから新しく書くコードでは、Java8のjava.timeパッケージ(LocalDate LocalDateTime)を使いましょう。
日付のみ、時刻のみ、といった目的が明確で、不変で安全、APIも直感的にあつかえます。
FAQ|Java日付比較によくある質問
Q. LocalDate.equals() と isEqual() は何が違う?
結果はほぼ同じですが、equals() は型まで厳密に LocalDate である必要があり、isEqual() は ChronoLocalDate 系(和暦の JapaneseDate など)でも比較できます。暦の違いを跨ぐ可能性があるなら isEqual() を選ぶのが安全です。
Q. LocalDateTime の比較はナノ秒まで一致しないとtrueにならない?
はい、LocalDateTime#isEqual() はナノ秒まで一致して初めて true です。「同じ日か」だけ判定したいなら toLocalDate().isEqual(...) に変換するか、truncatedTo(ChronoUnit.SECONDS) で精度を揃えてください。
Q. データベース(JDBC)から取った日付はDate?LocalDate?
JDBC 4.2(Java 8)以降は ResultSet#getObject(col, LocalDate.class) で LocalDate を直接取得できます。古いコードで java.sql.Timestamp と java.util.Date を equals() で比較すると、呼び出す向きで結果が変わる典型バグを踏まないよう、新規は最初から LocalDate/LocalDateTime で受けるのが鉄則です。
Q. Java 8未満(Java 7以前)でも安全に日付比較したい
Java 7以前なら、Joda-Timeライブラリを導入するのが定石です。
Joda-Timeは java.time の元になったAPIで、DateTime ・LocalDate ・isBefore()/isAfter()/isEqual() がそのまま使えます。
Java 8にアップグレード可能なら、まずアップグレードを優先してください。
| REV. | DATE | 内容 |
|---|---|---|
| 5 | 2026-10-16 | 図解を描き直し(図面のデザインにそろえた) |
| 4 | 2026-10-04 | 記載内容の事実確認と修正 |
| 3 | 2026-09-04 | 2026年9月時点の情報で全体を見直し |
| 2 | 2026-04-26 | 2026年4月時点の情報で全体を見直し |
| 1 | 2025-11-02 | 初版 |



