Java Tips | 日付・時間:祝日判定

Java Java
スポンサーリンク

祝日判定のゴールイメージ

「この日付は祝日か?」「祝日は営業日から除外したい」「祝日は締切をずらしたい」
業務システムでは、祝日判定はかなりの頻度で登場します。

ここでまず押さえてほしい現実があります。
祝日は「毎年同じ」ではありません。法律改正で増減したり、日付が動いたり、特例が入ったりします。
つまり、“コードにベタ書きして終わり”にすると、必ずメンテナンス地獄になります。

だから祝日判定は、

  • 「祝日データをどこから持ってくるか」
  • 「そのデータをどう使って判定するか」

を分けて考えるのが、とても重要になります。


最小構成:祝日を LocalDate の集合で持つ

Set<LocalDate> に祝日を入れて判定する

まずは一番シンプルな形から始めます。
「祝日の日付一覧を Set<LocalDate> として持ち、その中に含まれているかどうかで判定する」やり方です。

import java.time.LocalDate;
import java.util.HashSet;
import java.util.Set;

public class SimpleHolidayChecker {

    private final Set<LocalDate> holidays = new HashSet<>();

    public SimpleHolidayChecker(Set<LocalDate> holidays) {
        this.holidays.addAll(holidays);
    }

    public boolean isHoliday(LocalDate date) {
        return holidays.contains(date);
    }

    public static void main(String[] args) {
        Set<LocalDate> holidays = new HashSet<>();
        holidays.add(LocalDate.of(2025, 1, 1));  // 元日
        holidays.add(LocalDate.of(2025, 2, 11)); // 建国記念の日(例)

        SimpleHolidayChecker checker = new SimpleHolidayChecker(holidays);

        LocalDate d1 = LocalDate.of(2025, 1, 1);
        LocalDate d2 = LocalDate.of(2025, 1, 2);

        System.out.println(d1 + " は祝日? " + checker.isHoliday(d1)); // true
        System.out.println(d2 + " は祝日? " + checker.isHoliday(d2)); // false
    }
}
Java

ここで重要なのは二つです。

一つ目は、「祝日判定のロジック自体はめちゃくちゃシンプルでよい」ということです。
contains で「その日付が祝日一覧に含まれているか」を見るだけです。

二つ目は、「祝日データの中身は外から注入できるようにしておく」ことです。
コンストラクタで Set<LocalDate> を受け取るようにしておけば、
DB・設定ファイル・外部 API など、どこからでも祝日情報を持ってこられます。


祝日データの置き場所をどうするか

ハードコードは“最終手段”にする

「とりあえず動かしたい」段階では、
クラスの中に祝日をベタ書きしてしまうこともあります。

private static Set<LocalDate> defaultHolidays() {
    Set<LocalDate> set = new HashSet<>();
    set.add(LocalDate.of(2025, 1, 1));
    set.add(LocalDate.of(2025, 1, 13));
    // …以下略
    return set;
}
Java

ただし、これは本番運用ではほぼ確実に破綻します。
祝日が変わるたびにソースコードを修正・リリースしなければならないからです。

実務では、次のような方向を目指すのが現実的です。

  • DB の祝日マスタテーブルから読み込む
  • 設定ファイル(CSV, JSON など)から起動時に読み込む
  • 外部の祝日 API から取得してキャッシュする

どれを選ぶかはシステムの規模や要件次第ですが、
「祝日データはコードから切り離しておく」という発想がとても大事です。


営業日判定との組み合わせ

「土日+祝日」を休みとするパターン

祝日判定は、単体で使うよりも「営業日判定」とセットで使うことが多いです。
先に作った isHoliday を、営業日判定に組み込んでみます。

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.util.Set;

public class BusinessDayWithHolidayChecker {

    private final SimpleHolidayChecker holidayChecker;

    public BusinessDayWithHolidayChecker(SimpleHolidayChecker holidayChecker) {
        this.holidayChecker = holidayChecker;
    }

    public boolean isBusinessDay(LocalDate date) {
        DayOfWeek dow = date.getDayOfWeek();

        if (dow == DayOfWeek.SATURDAY || dow == DayOfWeek.SUNDAY) {
            return false;
        }
        if (holidayChecker.isHoliday(date)) {
            return false;
        }
        return true;
    }

    public static void main(String[] args) {
        Set<LocalDate> holidays = Set.of(
                LocalDate.of(2025, 1, 1),
                LocalDate.of(2025, 1, 13)
        );

        SimpleHolidayChecker holidayChecker = new SimpleHolidayChecker(holidays);
        BusinessDayWithHolidayChecker businessChecker =
                new BusinessDayWithHolidayChecker(holidayChecker);

        LocalDate d1 = LocalDate.of(2025, 1, 1);  // 祝日
        LocalDate d2 = LocalDate.of(2025, 1, 4);  // 土曜
        LocalDate d3 = LocalDate.of(2025, 1, 6);  // 月曜

        System.out.println(d1 + " 営業日? " + businessChecker.isBusinessDay(d1)); // false
        System.out.println(d2 + " 営業日? " + businessChecker.isBusinessDay(d2)); // false
        System.out.println(d3 + " 営業日? " + businessChecker.isBusinessDay(d3)); // true
    }
}
Java

ここで深掘りしたいのは、
「祝日判定」と「営業日判定」をきれいに分離していることです。

祝日ロジックを変えたいときは SimpleHolidayChecker を差し替えればよく、
営業日の定義(たとえば「土曜も営業にする」など)を変えたいときは
BusinessDayWithHolidayChecker 側だけを触れば済みます。


「振替休日」「国民の休日」などの扱い

ルールで計算するか、結果だけを持つか

日本の祝日は、「ハッピーマンデー」「振替休日」「国民の休日」など、
ルールベースで決まるものも多く、しかも法律改正で変わることがあります。

ここで選択肢は二つです。

一つは、「ルールをコードで実装して、祝日を計算する」やり方。
もう一つは、「計算済みの祝日一覧(カレンダー)をどこかから取得する」やり方です。

初心者向け・現実的な観点で言うと、
業務システムでは後者(一覧を持つ)を選ぶことが多いです。

理由はシンプルで、
「法律が変わったときに、ルール実装を直すのはリスクが高い」からです。
一方、祝日カレンダーを外部から取得する方式なら、
カレンダーの更新だけで済むことが多くなります。

大事なのは、
「祝日ロジックを自前で賢くやりすぎない」という感覚です。
“祝日カレンダー”という外部の真実を信頼し、それをどう使うかに集中する方が安全です。


祝日判定ユーティリティのインターフェース設計

isHoliday(LocalDate) を“中心”に置く

祝日判定は、営業日判定・締切計算・カレンダー表示など、
システムのあちこちから呼ばれます。

だからこそ、
「祝日かどうか」を判定するメソッドを、ドメインの中心に置く
という設計が効いてきます。

例えば、こんなインターフェースを切っておくイメージです。

public interface HolidayCalendar {
    boolean isHoliday(LocalDate date);
}
Java

実装クラスの中で、
DB から祝日を読むのか、設定ファイルから読むのか、外部 API を叩くのか、
そういった“取得方法”を隠蔽してしまいます。

業務ロジック側は、
「この日付が祝日かどうか知りたい」という意図だけを isHoliday に投げればよくなります。


祝日判定を使った実務的な例

例1:祝日の一覧を画面に表示する

「今年の祝日一覧を画面に出したい」という要件です。
祝日カレンダーを Set<LocalDate> で持っているなら、
単純にソートして表示すればよいだけです。

import java.time.LocalDate;
import java.util.Set;
import java.util.TreeSet;

public class HolidayListExample {

    public static void main(String[] args) {
        Set<LocalDate> holidays = Set.of(
                LocalDate.of(2025, 1, 1),
                LocalDate.of(2025, 2, 11),
                LocalDate.of(2025, 3, 20)
        );

        Set<LocalDate> sorted = new TreeSet<>(holidays);

        for (LocalDate d : sorted) {
            System.out.println(d);
        }
    }
}
Java

ここではロジックというより、
「祝日を“日付の集合”として扱うと、いろいろな用途に使い回せる」
という感覚を持ってほしいところです。

例2:祝日を除いた営業日カレンダーを作る

「今月の営業日だけを一覧にしたい」という要件です。

import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
import java.util.Set;

public class MonthlyBusinessCalendarExample {

    public static void main(String[] args) {
        Set<LocalDate> holidays = Set.of(
                LocalDate.of(2025, 3, 20)
        );

        SimpleHolidayChecker holidayChecker = new SimpleHolidayChecker(holidays);
        BusinessDayWithHolidayChecker businessChecker =
                new BusinessDayWithHolidayChecker(holidayChecker);

        LocalDate today = LocalDate.of(2025, 3, 1);
        LocalDate start = today.with(TemporalAdjusters.firstDayOfMonth());
        LocalDate end   = today.with(TemporalAdjusters.lastDayOfMonth());

        LocalDate d = start;
        while (!d.isAfter(end)) {
            if (businessChecker.isBusinessDay(d)) {
                System.out.println(d + " は営業日");
            }
            d = d.plusDays(1);
        }
    }
}
Java

祝日判定と営業日判定を組み合わせることで、
「カレンダー上のどの日が実際に業務で使える日か」を
きれいに表現できるようになります。


まとめ:祝日判定で絶対に覚えてほしいこと

祝日判定は、「日付が祝日かどうか」を知るだけの話に見えて、
実は「祝日データをどう管理するか」という設計の話でもあります。

祝日そのものは Set<LocalDate> のような“日付の集合”として扱う。
判定ロジックは isHoliday(LocalDate) に閉じ込める。
祝日データの取得方法(DB・設定ファイル・外部 API)は、ロジックから切り離す。
営業日判定とは分離しつつ、組み合わせて使えるように設計する。

もしあなたのコードのどこかに、
「特定の日付を if 文で直接“祝日扱い”している」ような箇所があれば、
そこを一度「祝日カレンダー」という一つのユーティリティに集約できないか眺めてみてください。

その小さな整理が、
“時間とカレンダーに強いエンジニア”への、確かな一歩になります。

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