C# Tips | 日付・時間処理:祝日判定

C# C#
スポンサーリンク
スポンサーリンク

祝日判定は「営業日・締め日・勤怠を正しく扱うための“前提条件”になるユーティリティです」

業務システムでは、次のような場面で「その日が祝日かどうか」を判定したくなります。

  • 締め日や支払期日を「営業日ベース」で計算したい
  • 勤怠で「祝日は出勤扱いにしない」ルールを入れたい
  • 予約システムで「祝日は受付停止」にしたい
  • バッチ処理を「平日だけ動かす」ようにしたい

ここで祝日判定を間違えると、 支払期日がずれたり、勤怠が誤集計されたり、予約が取れてはいけない日に取れてしまったりと、 業務的なインパクトが非常に大きくなります。 だからこそ、祝日判定ユーティリティは日付・時間処理の中でも重要度が高い機能になります。

祝日判定の基本は「祝日データをどこからどう持つか」を決めることです

固定祝日と“動く祝日”の両方を扱う必要があります

日本の祝日は、大きく次の2種類に分かれます。

  • 固定祝日:元日(1/1)、建国記念の日(2/11)、憲法記念日(5/3)など、毎年同じ日付
  • 動く祝日:成人の日(1月第2月曜)、海の日(7月第3月曜)など、“ハッピーマンデー”で動く祝日

さらに、「振替休日」「国民の休日」「天皇即位関連」など、 年によって変わるものもあります。

このため、祝日判定を自前で完全に実装するのはかなり大変で、 「祝日データをどう管理するか」が設計の肝になります。

シンプルな方法:自前で「祝日一覧」を持って判定するユーティリティです

小規模・限定的なシステムなら「テーブル+検索」で十分なこともあります

まずは、考え方が分かりやすい「自前テーブル方式」を見てみます。 「このシステムでは、この期間だけ祝日を扱えればよい」という前提なら、 固定の一覧を持っておくだけでも実務的に使えます。

using System;
using System.Collections.Generic;
using System.Linq;

public static class SimpleHolidayUtility
{
    // 例: 2024年だけを対象にした簡易版
    private static readonly HashSet<DateTime> Holidays2024 = new HashSet<DateTime>
    {
        new DateTime(2024, 1, 1),  // 元日
        new DateTime(2024, 1, 8),  // 成人の日
        new DateTime(2024, 2, 11), // 建国記念の日
        new DateTime(2024, 2, 12), // 振替休日
        // …必要な分だけ追加…
    };

    public static bool IsHoliday(DateTime date)
    {
        if (date.Year != 2024) return false; // 対象外はとりあえず平日扱い

        return Holidays2024.Contains(date.Date);
    }
}
C#

使い方は次の通りです。

Console.WriteLine(SimpleHolidayUtility.IsHoliday(new DateTime(2024, 1, 1))); // true
Console.WriteLine(SimpleHolidayUtility.IsHoliday(new DateTime(2024, 1, 2))); // false
C#

この方法のメリット・デメリットは次の通りです。

  • メリット:実装が簡単で、初心者にも分かりやすい
  • デメリット:年ごとにデータを更新する必要があり、長期運用には向かない

ここでの重要ポイントは、 「祝日判定は“どこまでの期間・精度が必要か”によって実装方針が変わる」 ということです。

実務的な方法:公式祝日データを扱うライブラリを利用する設計です

日本の祝日を扱うライブラリを使うと、精度と保守性が一気に上がります

日本の祝日を扱う .NET ライブラリとして、 内閣府が公開している「国民の祝日」CSVを扱う JpPublicHolidays があります。

このライブラリは、NuGet からインストールして次のように使えます。

using JpPublicHolidays;

// 祝日一覧を取得(非同期)
var holidays = await PublicHolidays.Get();

// 特定の日付が祝日かどうか
bool isHoliday = holidays.Any(h => h.Date.Date == new DateTime(2025, 1, 1).Date);
C#

さらに、同ライブラリにはインメモリデータベース機能があり、 db.IsHoliday(date) のように高速に判定できます。

using JpPublicHolidays;

// APIから祝日データベースを作成
var db = await HolidayDatabase.CreateFromApiAsync();

// 祝日判定
bool isHolidayJan1 = db.IsHoliday(new DateTime(2025, 1, 1)); // true
bool isHolidayJan2 = db.IsHoliday(new DateTime(2025, 1, 2)); // false
C#

ここでの重要ポイントは、 「祝日ルールは毎年変わることがあるため、公式データを扱うライブラリを使う方が保守性が高い」 ということです。

業務で使いやすい「祝日判定ユーティリティ」のテンプレートです

ライブラリをラップして「業務コードからはシンプルに呼べる形」にします

外部ライブラリをそのまま業務コードに散らすのではなく、 自前のユーティリティでラップしておくと、後から差し替えやすくなります。

using System;
using System.Threading.Tasks;
using JpPublicHolidays;

public static class JapanHolidayUtility
{
    private static HolidayDatabase? _db;

    public static async Task InitializeAsync()
    {
        // 一度だけロードしてキャッシュ
        _db ??= await HolidayDatabase.CreateFromApiAsync();
    }

    public static bool IsHoliday(DateTime date)
    {
        if (_db == null)
        {
            throw new InvalidOperationException("JapanHolidayUtility is not initialized.");
        }

        return _db.IsHoliday(date.Date);
    }

    public static bool IsBusinessDay(DateTime date)
    {
        if (_db == null)
        {
            throw new InvalidOperationException("JapanHolidayUtility is not initialized.");
        }

        // 土日 or 祝日は営業日ではない
        if (date.DayOfWeek == DayOfWeek.Saturday ||
            date.DayOfWeek == DayOfWeek.Sunday)
        {
            return false;
        }

        return !_db.IsHoliday(date.Date);
    }
}
C#

使い方は次の通りです。

await JapanHolidayUtility.InitializeAsync();

DateTime d1 = new DateTime(2025, 1, 1);
DateTime d2 = new DateTime(2025, 1, 2);

Console.WriteLine(JapanHolidayUtility.IsHoliday(d1));     // true
Console.WriteLine(JapanHolidayUtility.IsBusinessDay(d1)); // false
Console.WriteLine(JapanHolidayUtility.IsBusinessDay(d2)); // true
C#

ここでの重要ポイントは、

  • 初期化処理を一箇所にまとめていること
  • 祝日判定と営業日判定を分けていること

です。 これにより、業務コード側では「営業日かどうか」をシンプルに呼び出せます。

祝日判定を「締め日・期日計算」と組み合わせます

「期日が祝日なら翌営業日にずらす」といった業務ルールを自然に書けます

例えば、支払期日を「月末の翌営業日」にしたい場合、 次のようなユーティリティが書けます。

public static DateTime GetNextBusinessDay(DateTime date)
{
    DateTime current = date;

    while (!JapanHolidayUtility.IsBusinessDay(current))
    {
        current = current.AddDays(1);
    }

    return current;
}

public static DateTime GetPaymentDueDate(int year, int month)
{
    // 月末を求める
    int days = DateTime.DaysInMonth(year, month);
    DateTime endOfMonth = new DateTime(year, month, days);

    // 月末の翌営業日
    DateTime nextDay = endOfMonth.AddDays(1);
    return GetNextBusinessDay(nextDay);
}
C#

使い方は次の通りです。

await JapanHolidayUtility.InitializeAsync();

Console.WriteLine(GetPaymentDueDate(2025, 1)); // 2025年1月の支払期日(祝日・土日を避けた日)
C#

ここでの重要ポイントは、 祝日判定ユーティリティがあることで、「営業日ベースの期日計算」を業務コードに自然な形で落とし込める ということです。

祝日判定を「勤怠・予約・バッチ」のルールと組み合わせます

「祝日は出勤不可」「祝日は予約不可」「祝日はバッチ停止」などを一箇所で表現できます

例えば、勤怠入力で「祝日は原則出勤不可」としたい場合、 次のようなチェックができます。

public static bool CanWorkOn(DateTime date)
{
    // 祝日・土日は原則不可
    return JapanHolidayUtility.IsBusinessDay(date);
}
C#

予約システムで「祝日は予約不可」にしたい場合はこうです。

public static bool CanReserve(DateTime date)
{
    // 営業日のみ予約可能
    return JapanHolidayUtility.IsBusinessDay(date);
}
C#

バッチ処理で「祝日は動かさない」場合は、 スケジューラ側で次のような条件を入れます。

if (!JapanHolidayUtility.IsBusinessDay(DateTime.Today))
{
    // 今日は祝日 or 土日なのでバッチをスキップ
    return;
}
C#

ここでの重要ポイントは、 祝日判定ユーティリティを“業務ルールの入り口”として使うことで、 勤怠・予約・バッチなど複数の機能を一貫したカレンダールールで動かせる ということです。

まとめ:祝日判定は「日本のカレンダーを業務ロジックに正しく埋め込むためのユーティリティ」です

祝日判定の本質は、

  • 祝日データを正しく管理し
  • その日が祝日かどうかを安全に判定し
  • 営業日・期日・勤怠・予約・バッチなどの業務ルールに一貫して反映すること

にあります。

押さえておきたいポイントは、

  • 祝日は固定日だけでなく“動く祝日”や振替休日があるため、自前実装は慎重にすべきこと
  • 小規模なら自前テーブル方式でもよいが、長期運用なら公式データを扱うライブラリ利用が現実的であること
  • ユーティリティ化しておくことで、祝日判定と営業日判定を業務コードからシンプルに呼べること
  • 期日計算・勤怠・予約・バッチなど、複数機能を同じカレンダールールで動かせること

多くの購読者のみなさんが日本向けの業務コードを書くとき、 この「祝日判定ユーティリティ」を導入しておくだけで、 カレンダーまわりのロジックが安定し、 支払期日・勤怠・予約・バッチ処理のトラブルを大幅に減らすことができます。

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