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

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

はじめに 「深夜時間判定」は“割増賃金の境界線をコードにする作業”

勤務時間の中でも、
「22:00〜翌5:00」は“深夜労働”として割増賃金の対象になる、というルールがよくあります。

この“深夜時間”を正しく判定できないと、
残業代の計算が狂ったり、法令違反になったりと、かなり重い問題につながります。

でも、やることの本質はシンプルで、
「勤務時間帯」と「深夜時間帯(22:00〜翌5:00)」の“重なっている部分”を取り出すだけです。

ここでは、
まず「1日の中の深夜時間帯の考え方」から始めて、
「勤務時間との重なりを計算するユーティリティ」まで、
初心者向けにかみ砕いて説明していきます。


深夜時間帯の基本イメージを整理する

深夜時間は「22:00〜翌5:00」という“日付をまたぐ帯”

深夜時間のややこしさは、
「22:00〜24:00」と「0:00〜5:00」にまたがっていることです。

例えば「2026/02/18 21:00〜2026/02/19 02:00」の勤務なら、
深夜時間は「22:00〜24:00」と「0:00〜2:00」の合計 4時間です。

つまり、深夜時間判定の本質は、
「ある勤務時間帯が、22:00〜翌5:00のどこにどれだけ重なっているか」を求めることです。


1日分の深夜時間帯を DateTime で表現する

「その日の22:00〜翌5:00」を作る

まずは、「ある日付に対する深夜時間帯」を DateTime で表現してみます。

public static (DateTime NightStart, DateTime NightEnd) GetNightRange(DateTime anyTime)
{
    DateTime date = anyTime.Date; // その日の 0:00

    DateTime nightStart = date.AddHours(22);      // 当日 22:00
    DateTime nightEnd   = date.AddDays(1).AddHours(5); // 翌日 5:00

    return (nightStart, nightEnd);
}
C#

例えば、anyTime2026/02/18 10:00 なら、

  • nightStart = 2026/02/18 22:00
  • nightEnd = 2026/02/19 05:00

という“その日の深夜時間帯”が取れます。

ここでのポイントは、
「深夜時間帯は“日付をまたぐ1本の帯”として扱う」
というイメージを持つことです。


勤務時間と深夜時間の“重なり”を計算する

1日の深夜帯と勤務時間の重なりを求める

次に、「ある勤務時間帯が、その日の深夜帯とどれだけ重なるか」を計算します。

public static TimeSpan GetNightWorkDurationForOneDay(DateTime workStart, DateTime workEnd)
{
    if (workEnd <= workStart)
        return TimeSpan.Zero;

    var (nightStart, nightEnd) = GetNightRange(workStart);

    DateTime overlapStart = workStart > nightStart ? workStart : nightStart;
    DateTime overlapEnd   = workEnd   < nightEnd   ? workEnd   : nightEnd;

    if (overlapEnd <= overlapStart)
        return TimeSpan.Zero;

    return overlapEnd - overlapStart;
}
C#

使い方の例です。

DateTime workStart = new DateTime(2026, 2, 18, 21, 0, 0); // 21:00
DateTime workEnd   = new DateTime(2026, 2, 19, 2, 0, 0);  // 翌 2:00

TimeSpan night = GetNightWorkDurationForOneDay(workStart, workEnd);

Console.WriteLine(night.TotalHours); // 4 (22:00〜2:00)
C#

ここでの重要ポイントは、
「勤務時間帯」と「深夜時間帯」の“重なり”を、
overlapStartoverlapEnd で切り出していることです。

overlapStart = max(workStart, nightStart)
overlapEnd = min(workEnd, nightEnd)

という形は、「2つの時間帯の共通部分」を求める定番パターンなので、ぜひ覚えておいてください。


勤務が複数日にまたがる場合への対応

日をまたぐ勤務を“日ごとに分割して”考える

「20:00〜翌6:00」のように、勤務自体が深夜帯をまたぐこともあります。
この場合は、勤務時間を“日ごとに分割して”考えるのが安全です。

public static TimeSpan GetNightWorkDuration(DateTime workStart, DateTime workEnd)
{
    if (workEnd <= workStart)
        return TimeSpan.Zero;

    TimeSpan totalNight = TimeSpan.Zero;

    DateTime current = workStart;

    while (current < workEnd)
    {
        DateTime dayEnd = current.Date.AddDays(1); // その日の 24:00

        DateTime segmentEnd = workEnd < dayEnd ? workEnd : dayEnd;

        TimeSpan nightForDay = GetNightWorkDurationForOneDay(current, segmentEnd);

        totalNight += nightForDay;

        current = dayEnd;
    }

    return totalNight;
}
C#

使い方の例です。

DateTime workStart = new DateTime(2026, 2, 18, 20, 0, 0); // 20:00
DateTime workEnd   = new DateTime(2026, 2, 19, 6, 0, 0);  // 翌 6:00

TimeSpan night = GetNightWorkDuration(workStart, workEnd);

Console.WriteLine(night.TotalHours); // 7 (22:00〜5:00 + 0:00〜5:00 のうち重なる部分)
C#

このように、
「勤務時間を日ごとに区切り、その日ごとの深夜帯との重なりを足し合わせる」
という構造にしておくと、どんな長さの勤務にも対応できます。


「深夜時間が含まれているか?」の判定

単純なブール判定ユーティリティ

「この勤務に深夜時間が1分でも含まれているか?」だけ知りたい場合は、
合計深夜時間が 0 より大きいかどうかを見るだけでOKです。

public static bool HasNightWork(DateTime workStart, DateTime workEnd)
{
    return GetNightWorkDuration(workStart, workEnd) > TimeSpan.Zero;
}
C#

使い方の例です。

Console.WriteLine(HasNightWork(
    new DateTime(2026, 2, 18, 9, 0, 0),
    new DateTime(2026, 2, 18, 18, 0, 0))); // false

Console.WriteLine(HasNightWork(
    new DateTime(2026, 2, 18, 21, 30, 0),
    new DateTime(2026, 2, 19, 0, 30, 0))); // true
C#

ここでの重要ポイントは、
「“含まれているかどうか”と“何時間か”を分けて考える」ことです。
まずは TimeSpan で量を出し、その上にブール判定を載せると、ロジックが整理されます。


実務での注意点:タイムゾーンと日付の基準

DateTimeKind とローカル時間の扱い

深夜時間判定は、「どのタイムゾーンの 22:00〜5:00 か」が非常に重要です。

日本の就業規則なら「日本時間の 22:00〜5:00」
海外拠点なら、その国のローカル時間での 22:00〜5:00

というように、前提が変わります。

そのため、実務では次のような方針を決めておくと安全です。

アプリ内部では DateTime をローカル時間(Kind = Local)で扱う
あるいは DateTimeOffset を使って、タイムゾーンを明示的に持つ

そして、「深夜時間判定は必ずローカル時間に変換してから行う」
というルールにしておくと、UTC とのズレで事故る可能性を減らせます。


まとめ 「深夜時間判定ユーティリティ」は“割増対象時間を正確に切り出す道具」

深夜時間判定は、
単に「22時以降かどうか」ではなく、
「勤務時間帯と 22:00〜翌5:00 の重なりを正確に取り出す」処理です。

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

深夜時間帯は「その日の 22:00〜翌5:00」という“日付をまたぐ1本の帯”として表現する。
勤務時間帯との重なりは、overlapStart = max(workStart, nightStart)overlapEnd = min(workEnd, nightEnd) で求める。
勤務が複数日にまたがる場合は、勤務を日ごとに区切り、各日の深夜帯との重なりを合計する。
「含まれているか?」の判定は、まず合計深夜時間を TimeSpan で出してから、0より大きいかどうかで判断する。
タイムゾーン(どのローカル時間の 22:00〜5:00 か)を明確にし、その前提をコード全体で統一する。

ここまで理解できれば、
“なんとなく 22時以降を深夜扱いしている”状態から抜け出して、
“割増賃金や法令に耐えうる、実務で使える深夜時間判定ユーティリティ”を
自分の C# プロジェクトにしっかり組み込めるようになります。

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