概要

Java には Calendar や LocalDate はあっても、「日本の祝日」を返す標準 API はありません。営業日計算や帳票の日付処理で祝日判定が必要になった途端、祝日データを自前で持つ設計を迫られます。ここで悩ましいのがデータの持ち方です。固定日の祝日だけならハードコードでも動きますが、成人の日のようなハッピーマンデー、日曜と重なったときの振替休日、天文計算で決まる春分・秋分が絡むと、単純な定数リストでは翌年に破綻します。この記事では、固定祝日とハッピーマンデーを LocalDate のリストとして組み立てるコードを軸に、振替休日の算出ルール、年次更新を回しやすくするデータ構成、営業日ロジックへのつなぎ方を整理します。

使いどころ

経理システムの支払予定日計算で使う祝日マスタを、年初に一括生成して DB に登録する

帳票の出力予定日が祝日に当たったとき、前営業日へ自動スライドさせる判定に使う

シフト管理画面のカレンダー表示で、祝日セルの背景色を変えるためのデータソースにする

コード例

祝日一覧を LocalDate リストで管理する
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.Month;
import java.util.ArrayList;
import java.util.List;

public class JapanHolidays {

    // 固定日の祝日
    public static List<LocalDate> getFixedHolidays(int year) {
        return List.of(
            LocalDate.of(year, Month.JANUARY, 1),   // 元日
            LocalDate.of(year, Month.FEBRUARY, 11),  // 建国記念の日
            LocalDate.of(year, Month.FEBRUARY, 23),  // 天皇誕生日
            LocalDate.of(year, Month.APRIL, 29),     // 昭和の日
            LocalDate.of(year, Month.MAY, 3),        // 憲法記念日
            LocalDate.of(year, Month.MAY, 4),        // みどりの日
            LocalDate.of(year, Month.MAY, 5),        // こどもの日
            LocalDate.of(year, Month.AUGUST, 11),    // 山の日
            LocalDate.of(year, Month.NOVEMBER, 3),   // 文化の日
            LocalDate.of(year, Month.NOVEMBER, 23)   // 勤労感謝の日
        );
    }

    // 第 n 月曜日を返す(Happy Monday 制度)
    public static LocalDate nthMonday(int year, Month month, int n) {
        var first = LocalDate.of(year, month, 1);
        var offset = (DayOfWeek.MONDAY.getValue()
            - first.getDayOfWeek().getValue() + 7) % 7;
        return first.plusDays(offset + 7L * (n - 1));
    }

    // 固定日 + ハッピーマンデー + 振替休日を結合
    public static List<LocalDate> getHolidays(int year) {
        var holidays = new ArrayList<>(getFixedHolidays(year));
        holidays.add(nthMonday(year, Month.JANUARY, 2));   // 成人の日
        holidays.add(nthMonday(year, Month.JULY, 3));       // 海の日
        holidays.add(nthMonday(year, Month.SEPTEMBER, 3));  // 敬老の日
        holidays.add(nthMonday(year, Month.OCTOBER, 2));    // スポーツの日

        // 振替休日: 日曜に当たった祝日の直後の平日を追加する
        var substitutes = new ArrayList<LocalDate>();
        for (var holiday : holidays) {
            if (holiday.getDayOfWeek() == DayOfWeek.SUNDAY) {
                var next = holiday.plusDays(1);
                while (holidays.contains(next)) {
                    next = next.plusDays(1);  // 連休中は後ろへずらす
                }
                substitutes.add(next);
            }
        }
        holidays.addAll(substitutes);
        // 春分・秋分は天文計算のため、近似式か外部データで補完する
        holidays.sort(LocalDate::compareTo);
        return holidays;
    }

    public static void main(String[] args) {
        for (var h : getHolidays(2025)) {
            System.out.println(h + " (" + h.getDayOfWeek() + ")");
        }
        // 2025-01-01 (WEDNESDAY), 2025-01-13 (MONDAY), ... と並ぶ

        // 振替休日の確認: 2026年は憲法記念日(5/3)が日曜。
        // 5/4 みどりの日・5/5 こどもの日を飛ばして 5/6(水) が振替休日
        var h2026 = getHolidays(2026);
        System.out.println("2026-05-06 は休日か: "
            + h2026.contains(LocalDate.of(2026, 5, 6)));
        // → true
    }
}

Java 8 / 17 / 21 の完全なサンプルコードは GitHub リポジトリ で確認できます。

Version Coverage

Set.of() でイミュータブルな祝日セットを簡潔に初期化できる。var と toList() で記述量が減る。

Java 17
// Java 17: Set.of() + var + Set.copyOf で簡潔に
var base = Set.of(
    LocalDate.of(2024, 1, 1),
    LocalDate.of(2024, 1, 8)
);
// 振替休日を追加してイミュータブルな Set を返す
var result = new TreeSet<>(base);
// ... 振替休日の追加処理 ...
return Set.copyOf(result);

Library Comparison

標準 API(LocalDate + Set)祝日データを自前で管理し、振替休日・国民の休日のルールもコードで表現したいとき。依存ゼロで動作する。祝日データの年次更新や春分・秋分の計算は自前で行う必要がある。
ICU4J JapaneseCalendar国際化対応で和暦・祝日を扱う場合。JAR サイズが大きく(約11MB)、祝日の独自定義は別途必要。
Jollyday各国の祝日データをライブラリに委ねたいとき。日本の法改正への追従が遅れる場合がある。会社休業日は結局自前で追加が必要。

注意点

春分の日・秋分の日は天文計算に基づくため、数年先の日付は暫定値になる。

祝日法の改正で祝日が追加・変更される可能性がある。

振替休日は「祝日が日曜のとき、その後の最初の祝日でない日が休日になる」ルール。連休中は翌月曜とは限らない。

「山の日」(2016年制定)など比較的新しい祝日が漏れているコードを保守案件でよく見かける。法定祝日の追加・変更は内閣府のウェブサイトで確認し、年次でリストを見直す運用ルールをチームで決めておくこと。

FAQ

祝日データの更新はいつ・誰がやるべきですか?

内閣府の公開 CSV は例年2月頃に翌年分が確定します。年次バッチで取り込むか、運用カレンダーに更新タスクを登録して担当者を決めておくのが現実的です。

振替休日はコードで自動算出できますか?

「祝日が日曜なら直後の平日を休日にする」という祝日法第3条のルールなので実装できます。2つの祝日に挟まれた国民の休日も忘れずに扱う必要があります。

祝日リストは DB と定数のどちらで持つべきですか?

更新が年1回で参照するサービスが1つなら定数で十分です。複数システムで共有する場合や期中の変更に即応したい場合は DB やマスタ API に寄せます。

関連書籍

この記事のテーマをさらに深く学びたい方へ。

※ Amazon アソシエイトリンクを含みます