Java Tips | 日付・時間:日付比較

Java Java
スポンサーリンク

日付比較ユーティリティは何のために必要か

業務システムでは、
「締め日を過ぎているか?」「有効期限内か?」「開始日 ≤ 対象日 ≤ 終了日か?」
といった“日付の比較”が、あらゆるところに出てきます。

このとき、毎回 if 文の中でゴチャゴチャ書いていると、
条件が読みにくくなり、バグも入りやすくなります。

そこで役に立つのが「日付比較ユーティリティ」です。
よく使う比較パターンを小さなメソッドにまとめておくことで、
「何を判定しているのか」が一目で分かるコードにできます。


Java で日付を比較する基本(LocalDate 編)

before / after / equals の感覚をつかむ

LocalDate には、日付を比較するためのメソッドが用意されています。

import java.time.LocalDate;

public class LocalDateCompareBasic {

    public static void main(String[] args) {
        LocalDate d1 = LocalDate.of(2025, 3, 10);
        LocalDate d2 = LocalDate.of(2025, 3, 15);

        System.out.println(d1.isBefore(d2)); // true
        System.out.println(d1.isAfter(d2));  // false
        System.out.println(d1.isEqual(d2));  // false
    }
}
Java

ここで大事なのは、
isBefore は「より前の日付か?」
isAfter は「より後の日付か?」
isEqual は「同じ日付か?」
という、直感的なメソッドが用意されていることです。

compareTo で数値比較のように書くこともできますが、
初心者のうちは isBefore / isAfter / isEqual を素直に使った方が読みやすくなります。


「今日より前か?」「今日以降か?」を判定するユーティリティ

締め切りチェックの典型パターン

よくあるのが、「今日が締め切りを過ぎているかどうか」の判定です。

import java.time.LocalDate;

public class DateCompareUtils {

    public static boolean isExpired(LocalDate today, LocalDate deadline) {
        return today.isAfter(deadline);
    }

    public static boolean isOnOrBefore(LocalDate target, LocalDate base) {
        return !target.isAfter(base);
    }

    public static boolean isOnOrAfter(LocalDate target, LocalDate base) {
        return !target.isBefore(base);
    }
}
Java

使い方の例です。

LocalDate today = LocalDate.of(2025, 3, 20);
LocalDate deadline = LocalDate.of(2025, 3, 18);

System.out.println(DateCompareUtils.isExpired(today, deadline));      // true
System.out.println(DateCompareUtils.isOnOrBefore(today, deadline));   // false
System.out.println(DateCompareUtils.isOnOrAfter(today, deadline));    // true
Java

ここで深掘りしたいのは、「否定をうまく使って“以上”“以下”を表現している」点です。

target <= base を表現したいときは「target が base より後ではない」
つまり !target.isAfter(base) と書けます。

target >= base は「target が base より前ではない」
つまり !target.isBefore(base) です。

このパターンを覚えておくと、「以上」「以下」の条件をきれいに書けます。


「範囲内かどうか」を判定するユーティリティ

開始日 ≤ 対象日 ≤ 終了日 のチェック

業務で本当によく出るのが、「この日付が期間内かどうか」の判定です。

import java.time.LocalDate;

public class DateRangeUtils {

    public static boolean isBetweenInclusive(LocalDate target,
                                             LocalDate startInclusive,
                                             LocalDate endInclusive) {
        if (endInclusive.isBefore(startInclusive)) {
            throw new IllegalArgumentException("終了日は開始日以降である必要があります");
        }

        boolean notBeforeStart = !target.isBefore(startInclusive);
        boolean notAfterEnd    = !target.isAfter(endInclusive);
        return notBeforeStart && notAfterEnd;
    }

    public static boolean isBetweenHalfOpen(LocalDate target,
                                            LocalDate startInclusive,
                                            LocalDate endExclusive) {
        if (!endExclusive.isAfter(startInclusive)) {
            throw new IllegalArgumentException("終了日は開始日より後である必要があります");
        }

        boolean notBeforeStart = !target.isBefore(startInclusive);
        boolean beforeEnd      = target.isBefore(endExclusive);
        return notBeforeStart && beforeEnd;
    }
}
Java

使い方の例です。

LocalDate target = LocalDate.of(2025, 3, 15);
LocalDate start  = LocalDate.of(2025, 3, 10);
LocalDate end    = LocalDate.of(2025, 3, 20);

System.out.println(DateRangeUtils.isBetweenInclusive(target, start, end)); // true
Java

ここで特に重要なのは、「範囲の定義をユーティリティで固定する」ことです。

開始・終了を「両方含む(閉区間)」で扱うのか、
「開始を含み、終了は含まない(半開区間)」で扱うのか。

これがあちこちでバラバラだと、バグの温床になります。
プロジェクトとして「日付範囲は基本的に半開区間で扱う」などのルールを決め、
それをユーティリティに閉じ込めておくと安全です。


LocalDateTime / Instant の比較と「タイムゾーン」の罠

LocalDateTime の比較は「同じタイムゾーン前提」

LocalDateTimeisBefore / isAfter / isEqual を持っています。

import java.time.LocalDateTime;

public class LocalDateTimeCompareBasic {

    public static void main(String[] args) {
        LocalDateTime t1 = LocalDateTime.of(2025, 3, 10, 10, 0);
        LocalDateTime t2 = LocalDateTime.of(2025, 3, 10, 15, 0);

        System.out.println(t1.isBefore(t2)); // true
    }
}
Java

ただし、ここで深掘りしておきたい大事なポイントがあります。

LocalDateTime は「タイムゾーンを持たない日時」です。
つまり、「同じタイムゾーンで解釈される前提」で比較する必要があります。

もし「JST の 10:00」と「UTC の 10:00」を LocalDateTime としてそのまま比較すると、
本当は 9時間の差があるのに「同じ時刻」とみなされてしまいます。

タイムゾーンをまたいで“絶対的な瞬間”を比較したいときは、
InstantZonedDateTime を使うのが安全です。

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class InstantCompareBasic {

    public static void main(String[] args) {
        ZonedDateTime jst = ZonedDateTime.of(2025, 3, 10, 10, 0, 0, 0, ZoneId.of("Asia/Tokyo"));
        ZonedDateTime utc = ZonedDateTime.of(2025, 3, 10, 1, 0, 0, 0, ZoneId.of("UTC"));

        Instant i1 = jst.toInstant();
        Instant i2 = utc.toInstant();

        System.out.println(i1.equals(i2)); // true(同じ瞬間)
    }
}
Java

業務システムでは、「日付だけ比較したいのか」「瞬間として比較したいのか」を意識して、
LocalDate / LocalDateTime / Instant を使い分けることがとても重要です。


「同じ日か?」を判定するユーティリティ

LocalDateTime から日付だけを取り出して比較する

「同じ日付のデータかどうか」を判定したい場面もよくあります。
このとき、LocalDateTime 同士をそのまま比較すると、時間まで一致しないと true になりません。

日付だけで比較したいなら、toLocalDate() で日付部分を取り出してから比較します。

import java.time.LocalDate;
import java.time.LocalDateTime;

public class SameDayUtils {

    public static boolean isSameDay(LocalDateTime t1, LocalDateTime t2) {
        LocalDate d1 = t1.toLocalDate();
        LocalDate d2 = t2.toLocalDate();
        return d1.isEqual(d2);
    }
}
Java

使い方の例です。

LocalDateTime t1 = LocalDateTime.of(2025, 3, 10, 9, 0);
LocalDateTime t2 = LocalDateTime.of(2025, 3, 10, 18, 30);

System.out.println(SameDayUtils.isSameDay(t1, t2)); // true
Java

ここでのポイントは、「何をもって“同じ”とみなすか」をユーティリティで明示していることです。
「日付が同じなら同じ日」「タイムゾーン込みで同じ瞬間なら同じ」など、
意味をはっきりさせておくと、後から読んだときに迷いません。


セキュリティ・運用の観点から見た日付比較

「境界条件」のバグはそのまま障害になる

日付比較のバグで一番多いのが、「境界条件」の間違いです。

締め切り日を「<」で判定すべきところを「≤」にしてしまう。
有効期限の終了日を含めるべきかどうかを勘違いする。
開始日と終了日の大小チェックをしておらず、逆転した期間を受け入れてしまう。

これらは、請求・支払・契約・SLA などに直結するため、
そのまま「お金のトラブル」「契約違反」「インシデント」につながります。

だからこそ、
「日付比較のパターンをユーティリティにまとめる」
「開始・終了の関係を必ずチェックする」
「閉区間/半開区間のルールをチームで統一する」
といった設計が、とても重要になります。


まとめ:日付比較ユーティリティで身につけてほしい感覚

日付比較ユーティリティは、
「前か後か」「範囲内か」「同じ日か」といった判定を、
読みやすく・間違えにくく書くための小さな道具です。

isBefore / isAfter / isEqual を素直に使う。
「以上」「以下」は !isAfter / !isBefore で表現する。
開始日・終了日と対象日の関係をユーティリティで統一する(閉区間か半開区間かを決める)。
LocalDate / LocalDateTime / Instant を、「何を比較したいか」に応じて使い分ける。

もしあなたのコードの中に、
if (a.compareTo(b) <= 0 && a.compareTo(c) >= 0)」のような、
パッと見て意味が分かりにくい条件があれば、
それを一度「日付比較ユーティリティ」に置き換えられないか眺めてみてください。

それだけで、コードの意図がはっきりし、
境界条件のバグも減り、“業務で戦える”日付・時間まわりの設計に近づきます。

タイトルとURLをコピーしました