C# Tips | 日付・時間処理:月跨ぎ判定

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

はじめに:「月跨ぎ判定」は“どの月のデータとして扱うか”を決めるための鍵

業務システムでは、
「勤務が 1/31 22:00〜2/1 06:00 のとき、どの月の勤務として集計するか」
「売上期間が 3/25〜4/5 のとき、月次レポートをどう分けるか」
のように、“期間が月をまたいでいるかどうか”がとても重要になります。

ここでいう「月跨ぎ判定」は、
「2つの日付(または日時)が同じ月の中に収まっているか」
「それとも別の月にまたがっているか」
を判定するための小さなユーティリティです。

まずは一番シンプルな「日付同士の月跨ぎ判定」から入り、
そのあと「日時(DateTime)」「勤務時間のような期間」への応用までかみ砕いて説明します。


基本形:2つの日付が“同じ年月かどうか”で判定する

発想:「月が変わっているかどうか」だけを見る

月跨ぎかどうかを判定する一番シンプルな考え方は、
「開始日と終了日の“年と月”が同じかどうか」を見ることです。

年と月が同じなら「月跨ぎではない」
年か月のどちらかが違えば「月跨ぎである」

というルールにできます。

public static bool IsCrossingMonth(DateTime start, DateTime end)
{
    return start.Year != end.Year || start.Month != end.Month;
}
C#

このメソッドは「月をまたいでいるか?」を判定するので、
戻り値が true なら「月跨ぎ」、false なら「同じ月の中」と読みます。

例:月跨ぎかどうかを試してみる

DateTime a1 = new DateTime(2026, 1, 10);
DateTime a2 = new DateTime(2026, 1, 31);

DateTime b1 = new DateTime(2026, 1, 31);
DateTime b2 = new DateTime(2026, 2, 1);

Console.WriteLine(IsCrossingMonth(a1, a2)); // false(1月の中で完結)
Console.WriteLine(IsCrossingMonth(b1, b2)); // true(1月→2月にまたいでいる)
C#

ここでの重要ポイントは、
「日付の“大小”ではなく、“年と月が変わっているかどうか”だけを見ている」ことです。
これだけで、月跨ぎかどうかの判定はほぼ足ります。


応用:DateTime(時刻付き)でも考え方は同じ

時刻がついていても「Year と Month だけを見る」

勤務時間などでは、DateTime に時刻も含まれますが、
月跨ぎ判定のロジック自体はまったく同じです。

public static bool IsCrossingMonth(DateTimeOffset start, DateTimeOffset end)
{
    return start.Year != end.Year || start.Month != end.Month;
}
C#

DateTime でも DateTimeOffset でも、
「どのタイムゾーンで見るか」を決めたうえで、
YearMonth だけを比較すればOKです。

例:夜勤の勤務時間での月跨ぎ

DateTime shiftStart = new DateTime(2026, 1, 31, 22, 0, 0); // 1/31 22:00
DateTime shiftEnd   = new DateTime(2026, 2, 1,  6, 0, 0);  // 2/1  06:00

Console.WriteLine(IsCrossingMonth(shiftStart, shiftEnd)); // true
C#

ここでの重要ポイントは、
「時刻が何時であっても、“月が変わっているかどうか”という観点では Year/Month だけ見ればよい」
という割り切りです。


「月跨ぎしていない」ことを前提にした処理もよく書く

「同じ月の中に収まっているか?」を判定する

逆に、「この期間は同じ月の中で完結していることが前提」という処理も多いです。
その場合は、「月跨ぎしていないか?」をチェックするユーティリティを用意しておくと安全です。

public static bool IsWithinSameMonth(DateTime start, DateTime end)
{
    return start.Year == end.Year && start.Month == end.Month;
}
C#

例として、「同じ月の中でしか登録してはいけない期間入力」のバリデーションを考えます。

if (!IsWithinSameMonth(start, end))
{
    Console.WriteLine("期間は同じ月の中で指定してください。");
}
C#

ここでの重要ポイントは、
「月跨ぎ判定」と「同じ月かどうか判定」は表裏一体で、
どちらも Year/Month の比較だけで書ける、ということです。


もう一歩:月跨ぎした場合に“どの月に属するか”を決める

夜勤などで「どっちの月の勤務とみなすか」を決める

実務では、「月跨ぎかどうか」だけでなく、
「月跨ぎしたとき、どの月のデータとして扱うか」を決める必要があります。

例えば夜勤の場合、

開始日の月に属する(1/31 22:00〜2/1 6:00 を“1月分”とみなす)
終了日の月に属する(同じ勤務を“2月分”とみなす)

など、会社ごと・システムごとにルールが違います。

これもユーティリティにしておくと分かりやすくなります。

public enum MonthBelongRule
{
    StartMonth,
    EndMonth
}

public static (int Year, int Month) GetBelongYearMonth(
    DateTime start,
    DateTime end,
    MonthBelongRule rule)
{
    return rule switch
    {
        MonthBelongRule.StartMonth => (start.Year, start.Month),
        MonthBelongRule.EndMonth   => (end.Year,   end.Month),
        _ => throw new ArgumentOutOfRangeException(nameof(rule))
    };
}
C#

例として、夜勤の勤務を「開始月に属する」とみなす場合です。

DateTime shiftStart = new DateTime(2026, 1, 31, 22, 0, 0);
DateTime shiftEnd   = new DateTime(2026, 2, 1,  6, 0, 0);

var belong = GetBelongYearMonth(shiftStart, shiftEnd, MonthBelongRule.StartMonth);
Console.WriteLine($"{belong.Year}/{belong.Month:D2}"); // 2026/01
C#

ここでの重要ポイントは、
「月跨ぎ判定」と「どの月に属するかのルール」はセットで考えると設計がきれいになる、ということです。
判定だけで終わらせず、「属する月」を決めるところまでユーティリティ化しておくと、集計処理が書きやすくなります。


実務で意識してほしいポイント

「日付の大小」と「月跨ぎ」は別物として考える

start <= end かどうかのチェック(期間として正しいか)と、
「月をまたいでいるかどうか」のチェックは、目的が違います。

期間として正しいか → 開始日と終了日の前後関係
月跨ぎかどうか → 年と月が変わっているかどうか

この2つを混ぜずに、別々のユーティリティとして用意しておくと、
バリデーションロジックがとても読みやすくなります。

「年月単位で集計する処理」と相性が良い

月跨ぎ判定は、
「年月のみ比較」「YearMonth 型」などと組み合わせるとさらに威力を発揮します。

例えば、

開始・終了から YearMonth を作る
同じ YearMonth なら「月跨ぎなし」、違えば「月跨ぎあり」

という形にすると、年月単位の集計処理と自然につながります。


まとめ:「月跨ぎ判定ユーティリティ」は“月次の境界線をはっきりさせる道具”

月跨ぎ判定の本質は、
「この期間は一つの月の中で完結しているのか、それとも月をまたいでいるのか」
という問いに、シンプルかつ一貫した答えを出すことです。

押さえておきたいポイントを言葉でまとめると、こうなります。

開始と終了の YearMonth を比較し、違っていれば「月跨ぎ」と判定できる。
「月跨ぎしていないこと」を前提にした処理では、先に IsWithinSameMonth でチェックしておくと安全。
夜勤など「月跨ぎが前提」のケースでは、「どの月に属するか」のルールもユーティリティとして決めておく。
期間の前後関係チェックと、月跨ぎ判定は目的が違うので、別メソッドとして分けておく。

ここまで理解できれば、
「なんとなく DateTime を比較している」状態から一歩進んで、
“月次集計や勤怠計算で迷わない、意図のある日付・時間ユーティリティ”を書けるようになります。

C#C#
スポンサーリンク
シェアする
@lifehackerをフォローする
スポンサーリンク
タイトルとURLをコピーしました