C# Tips | 日付・時間処理:営業日加算

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

はじめに 「営業日加算」は“ビジネスの締切をコードに落とす技”

「3営業日以内に対応」「5営業日後が締切」「翌営業日に処理」――
こういう要件を“ちゃんと”満たすには、
単純な「日数加算」ではなく「営業日加算」が必要になります。

DateTime.AddDays(3) は「3日後」ですが、
土日や祝日を飛ばしてくれたりはしません。
そこで登場するのが「営業日加算ユーティリティ」です。

ここでは、
土日だけを飛ばすシンプル版から始めて、
祝日対応・設計のポイントまで、
初心者向けにかみ砕いて説明していきます。


基本の考え方:1日ずつ進めて「営業日だけカウントする」

まずは「営業日とは何か」を決める

営業日加算の前に、
「営業日とは何か」をコードで定義しておく必要があります。

一番シンプルな定義はこうです。

  • 土日以外の日 → 営業日

この定義をそのままメソッドにすると、こうなります。

using System;

public static class BusinessDayUtil
{
    public static bool IsWeekend(DateTime date)
    {
        DayOfWeek w = date.DayOfWeek;
        return w == DayOfWeek.Saturday || w == DayOfWeek.Sunday;
    }

    public static bool IsBusinessDay(DateTime date)
    {
        return !IsWeekend(date);
    }
}
C#

この「営業日判定」を土台にして、
「営業日加算」を組み立てていきます。

営業日を n 日進める基本ロジック

考え方はシンプルで、

  • 日付を1日ずつ進める
  • 進めた日が営業日ならカウンタを1減らす
  • カウンタが0になったところが「n営業日後」

という流れです。

public static class BusinessDayUtil
{
    public static bool IsWeekend(DateTime date)
    {
        DayOfWeek w = date.DayOfWeek;
        return w == DayOfWeek.Saturday || w == DayOfWeek.Sunday;
    }

    public static bool IsBusinessDay(DateTime date)
    {
        return !IsWeekend(date);
    }

    public static DateTime AddBusinessDays(DateTime start, int businessDays)
    {
        if (businessDays < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(businessDays), "負の営業日はこのメソッドでは扱いません。");
        }

        DateTime current = start;
        int remaining = businessDays;

        while (remaining > 0)
        {
            current = current.AddDays(1);

            if (IsBusinessDay(current))
            {
                remaining--;
            }
        }

        return current;
    }
}
C#

ここでのポイントは、

  • 「営業日かどうか」の判定を IsBusinessDay に閉じ込めていること
  • 加算ロジックは「1日ずつ進める」だけにして、シンプルさを保っていること

です。


具体例でイメージを固める

金曜日から3営業日後を求める

例えば、2026/2/13(金)から3営業日後を求めてみます。

DateTime start = new DateTime(2026, 2, 13); // 金

DateTime after3 = BusinessDayUtil.AddBusinessDays(start, 3);

Console.WriteLine(after3); // 2026/02/18 (水)
C#

頭の中で追ってみると、こうなります。

  • 2/14(土) → 営業日ではない → カウントしない
  • 2/15(日) → 営業日ではない → カウントしない
  • 2/16(月) → 営業日 → 1日目
  • 2/17(火) → 営業日 → 2日目
  • 2/18(水) → 営業日 → 3日目 → ここで終了

このように、
土日を自動で飛ばしてくれるので、
「3営業日後」のような要件をそのままコードにできます。

すでに土日の場合はどうなるか

例えば、開始日が土曜日だった場合。

DateTime start = new DateTime(2026, 2, 14); // 土

DateTime after1 = BusinessDayUtil.AddBusinessDays(start, 1);

Console.WriteLine(after1); // 2026/02/16 (月)
C#
  • 2/15(日) → 営業日ではない
  • 2/16(月) → 営業日 → 1日目 → 終了

「開始日が営業日かどうか」はカウントに含めず、
「翌日以降の営業日を数える」仕様になっていることに注意してください。

「開始日が営業日なら、それも1日目に含めたい」
という要件なら、ロジックを少し変える必要があります。


仕様の違い:「開始日を含めるかどうか」をはっきり決める

2つのよくあるパターン

営業日加算には、よくある2つの解釈があります。

  1. 「開始日の翌日から数えて n 営業日後」
  2. 「開始日を1日目として数えて n 営業日目」

今の実装は 1 のパターンです。

もし 2 のパターンにしたいなら、
「開始日が営業日なら、最初から1日消費しておく」
という形に変えられます。

public static DateTime AddBusinessDaysInclusive(DateTime start, int businessDays)
{
    if (businessDays <= 0)
    {
        throw new ArgumentOutOfRangeException(nameof(businessDays), "1以上を指定してください。");
    }

    DateTime current = start;
    int remaining = businessDays;

    if (IsBusinessDay(current))
    {
        remaining--;
    }

    while (remaining > 0)
    {
        current = current.AddDays(1);

        if (IsBusinessDay(current))
        {
            remaining--;
        }
    }

    return current;
}
C#

この仕様の違いは、
締切や SLA(サービスレベル)の解釈に直結します。

実務では、

  • どちらの解釈にするかを要件として明文化する
  • メソッド名やコメントで「開始日を含む/含まない」をはっきり書く

ことがとても重要です。


祝日を含めた「実務的な営業日加算」にする

営業日=平日かつ祝日ではない

多くの日本の業務システムでは、

  • 土日
  • 祝日

を休業日とみなします。

その場合、営業日の定義はこうなります。

  • 営業日=土日ではない かつ 祝日ではない

この定義をコードに落とし込むと、こうです。

public interface IHolidayProvider
{
    bool IsHoliday(DateTime date);
}

public static class BusinessDayUtil
{
    private static IHolidayProvider? _holidayProvider;

    public static void SetHolidayProvider(IHolidayProvider provider)
    {
        _holidayProvider = provider;
    }

    public static bool IsWeekend(DateTime date)
    {
        DayOfWeek w = date.DayOfWeek;
        return w == DayOfWeek.Saturday || w == DayOfWeek.Sunday;
    }

    public static bool IsHoliday(DateTime date)
    {
        if (_holidayProvider == null)
        {
            return false;
        }

        return _holidayProvider.IsHoliday(date);
    }

    public static bool IsBusinessDay(DateTime date)
    {
        return !IsWeekend(date) && !IsHoliday(date);
    }

    public static DateTime AddBusinessDays(DateTime start, int businessDays)
    {
        if (businessDays < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(businessDays));
        }

        DateTime current = start;
        int remaining = businessDays;

        while (remaining > 0)
        {
            current = current.AddDays(1);

            if (IsBusinessDay(current))
            {
                remaining--;
            }
        }

        return current;
    }
}
C#

祝日の中身(どの日が祝日か)は、
IHolidayProvider の実装側に任せます。

例えば「固定の祝日リストを持つ簡易版」はこうです。

using System;
using System.Collections.Generic;

public class FixedHolidayProvider : IHolidayProvider
{
    private readonly HashSet<DateOnly> _holidays;

    public FixedHolidayProvider(IEnumerable<DateOnly> holidays)
    {
        _holidays = new HashSet<DateOnly>(holidays);
    }

    public bool IsHoliday(DateTime date)
    {
        var d = DateOnly.FromDateTime(date);
        return _holidays.Contains(d);
    }
}
C#

初期化時にこう設定します。

var holidays = new[]
{
    new DateOnly(2026, 1, 1),
    new DateOnly(2026, 1, 13),
    // 必要な祝日を追加
};

BusinessDayUtil.SetHolidayProvider(new FixedHolidayProvider(holidays));
C#

こうしておくと、

  • 祝日の定義を変えたい
  • 国や会社ごとに祝日を変えたい
  • テスト用にダミー祝日を使いたい

といったときに、
IHolidayProvider の実装を差し替えるだけで済みます。


タイムゾーンと「どの国の営業日か」を意識する

営業日は“カレンダーの国とタイムゾーン”に依存する

営業日加算は、
「どの国のカレンダーか」「どのタイムゾーンか」に強く依存します。

日本の営業日なのか、
アメリカの営業日なのか、
グローバル共通の営業日なのか。

ここを曖昧にしたまま DateTime を扱うと、
UTCとローカルの変換で日付がズレて、
「サーバーでは前日扱いになっていた」
といった事故が起きます。

実務的には、

  • サーバー内部では UTC で日時を持つ
  • 営業日判定・営業日加算をするときは、対象国のタイムゾーン(例: 日本なら “Tokyo Standard Time”)に変換してから日付部分を使う

という方針を決めておくと安全です。


まとめ 「営業日加算ユーティリティ」は“ビジネスの約束を守るための道具”

営業日加算は、
単なる日付計算ではなく、
「いつまでに対応するか」「いつが締切か」という
ビジネス上の約束をコードに落とし込むための重要な仕組みです。

押さえておきたいポイントは次の通りです。

  • 営業日加算の土台は「営業日判定」であり、IsBusinessDay にロジックを集約しておくと、加算処理がシンプルになる。
  • 基本ロジックは「1日ずつ進めて、営業日だけカウントする」形で書くと、初心者でも理解しやすく、バグも少ない。
  • 「開始日を含めるかどうか」は仕様として必ず決め、メソッド名やコメントで明示する。
  • 祝日を含めた実務的な営業日加算にするには、祝日判定を IHolidayProvider のような差し替え可能な仕組みに切り出す。
  • どの国・どのタイムゾーンの営業日かを最初に決め、UTCとローカルの変換を意識して設計することで、日付のズレを防げる。

ここまで押さえておけば、
「なんとなく日数を足している」状態から一歩進んで、
“ビジネスの締切と約束をきちんと守れる、実務で使える営業日加算ユーティリティ”を
自分のC#コードの中に自信を持って組み込めるようになります。

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