祝日判定のゴールイメージ
「この日付は祝日か?」「祝日は営業日から除外したい」「祝日は締切をずらしたい」
業務システムでは、祝日判定はかなりの頻度で登場します。
ここでまず押さえてほしい現実があります。
祝日は「毎年同じ」ではありません。法律改正で増減したり、日付が動いたり、特例が入ったりします。
つまり、“コードにベタ書きして終わり”にすると、必ずメンテナンス地獄になります。
だから祝日判定は、
- 「祝日データをどこから持ってくるか」
- 「そのデータをどう使って判定するか」
を分けて考えるのが、とても重要になります。
最小構成:祝日を 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 文で直接“祝日扱い”している」ような箇所があれば、
そこを一度「祝日カレンダー」という一つのユーティリティに集約できないか眺めてみてください。
その小さな整理が、
“時間とカレンダーに強いエンジニア”への、確かな一歩になります。
