はじめに 「月末取得」は“締めと集計のゴール地点”
月初が「スタート」だとしたら、月末は「ゴール」です。
売上の締め、勤怠の締め、請求期間の終わり、サブスクの課金期間の終わり――
どれも「この月の月末はどこか?」が決まっていないと、ロジックが書けません。
C# では、「翌月の月初から1日引く」という考え方で、
どんな月でも安全に月末を求めることができます。
ここをユーティリティとして押さえておくと、月次処理のコードが一気にスッキリします。
基本の考え方:翌月の月初 − 1日 で月末を出す
なぜ「翌月の月初 − 1日」が安全なのか
2月は28日だったり29日だったり、
4月は30日、7月は31日…と、月ごとに日数が違います。
「その月が何日まであるか」を自分で数えるのは現実的ではありません。
そこで使う定番パターンがこれです。
- 対象日付の「月初」を求める
- そこから
AddMonths(1)で「翌月の月初」を求める - そこから
AddDays(-1)で1日戻る → それが「その月の月末」
コードにするとこうなります。
using System;
DateTime any = new DateTime(2026, 2, 10); // 2026/02/10
DateTime monthStart = new DateTime(any.Year, any.Month, 1);
DateTime nextMonthStart = monthStart.AddMonths(1);
DateTime monthEnd = nextMonthStart.AddDays(-1);
Console.WriteLine(monthStart); // 2026/02/01
Console.WriteLine(nextMonthStart); // 2026/03/01
Console.WriteLine(monthEnd); // 2026/02/28(うるう年でなければ)
C#このやり方なら、
2月・うるう年・30日までの月・31日までの月、すべて正しく扱えます。
ユーティリティメソッドとしての「月末取得」
任意の日付から月末を求めるメソッド
毎回「月初 → 翌月 → 1日戻す」を手書きするのは面倒なので、
素直にユーティリティにしてしまいましょう。
public static class MonthUtil
{
public static DateTime GetMonthStart(DateTime date)
{
return new DateTime(date.Year, date.Month, 1, 0, 0, 0, date.Kind);
}
public static DateTime GetMonthEnd(DateTime date)
{
DateTime monthStart = GetMonthStart(date);
DateTime nextMonthStart = monthStart.AddMonths(1);
DateTime monthEnd = nextMonthStart.AddDays(-1);
return monthEnd;
}
}
C#使い方はとても直感的です。
DateTime d = new DateTime(2026, 2, 10, 15, 30, 0, DateTimeKind.Local);
DateTime monthStart = MonthUtil.GetMonthStart(d);
DateTime monthEnd = MonthUtil.GetMonthEnd(d);
Console.WriteLine(monthStart); // 2026/02/01 0:00:00
Console.WriteLine(monthEnd); // 2026/02/28 0:00:00
C#「このメソッドを通せば月末が返ってくる」という約束が、
コード上にハッキリ見えるのが大事です。
「今月」「先月」「来月」の月末を取る
今月の月末
今月の月末は、「今月の月初」から組み立てるときれいに書けます。
public static class MonthUtil
{
public static DateTime GetThisMonthStartLocal()
{
DateTime today = DateTime.Today; // Local, 0:00
return new DateTime(today.Year, today.Month, 1, 0, 0, 0, DateTimeKind.Local);
}
public static DateTime GetThisMonthEndLocal()
{
DateTime thisMonthStart = GetThisMonthStartLocal();
DateTime nextMonthStart = thisMonthStart.AddMonths(1);
DateTime thisMonthEnd = nextMonthStart.AddDays(-1);
return thisMonthEnd;
}
}
C#使い方はこうです。
DateTime thisMonthEnd = MonthUtil.GetThisMonthEndLocal();
Console.WriteLine(thisMonthEnd); // 2026/02/28 0:00:00 など
C#先月・来月の月末
「今月の月初」を基準に AddMonths(±1) すれば、
先月・来月の月末も簡単に求められます。
public static class MonthUtil
{
public static DateTime GetNextMonthEndLocal()
{
DateTime thisMonthStart = GetThisMonthStartLocal();
DateTime nextMonthStart = thisMonthStart.AddMonths(1);
DateTime nextNextStart = thisMonthStart.AddMonths(2);
DateTime nextMonthEnd = nextNextStart.AddDays(-1);
return nextMonthEnd;
}
public static DateTime GetPreviousMonthEndLocal()
{
DateTime thisMonthStart = GetThisMonthStartLocal();
DateTime prevMonthStart = thisMonthStart.AddMonths(-1);
DateTime thisMonthStartAgain = thisMonthStart; // 読みやすさのため
DateTime prevMonthEnd = thisMonthStartAgain.AddDays(-1);
return prevMonthEnd;
}
}
C#実際には、
「任意の日付の月末を取る GetMonthEnd」を軸にして、GetMonthEnd(DateTime.Today.AddMonths(-1)) のように組み合わせてもOKです。
月末と「期間」の組み合わせ(集計・締め処理での使い方)
「今月の期間」を [月初, 月末] で表現する
売上や勤怠などの集計では、
「今月の期間」をひとまとめで扱いたいことが多いです。
例えば、こんな小さな型を用意しておくと便利です。
public readonly struct MonthRange
{
public DateTime Start { get; }
public DateTime End { get; }
public MonthRange(DateTime start, DateTime end)
{
Start = start;
End = end;
}
}
public static class MonthUtil
{
public static MonthRange GetMonthRange(DateTime any)
{
DateTime start = GetMonthStart(any);
DateTime end = GetMonthEnd(any);
return new MonthRange(start, end);
}
}
C#使い方の例です。
DateTime target = new DateTime(2026, 2, 10);
MonthRange range = MonthUtil.GetMonthRange(target);
Console.WriteLine($"開始: {range.Start}"); // 2026/02/01
Console.WriteLine($"終了: {range.End}"); // 2026/02/28
C#この MonthRange をそのまま
「SQLのWHERE条件」「APIのリクエスト」「レポートのヘッダ」などに使えるようにしておくと、
月次処理のコードがかなり整理されます。
UTCとローカル、どの時間軸の月末を扱うか
「どのタイムゾーンの月末か」を最初に決める
月末も、UTCとローカルで意味が変わります。
例えば「日本時間での月末」と「UTCでの月末」は、
タイムゾーンによっては日付がずれることがあります。
日本時間の月末を基準にしたい場合は、
まずUTCから日本時間に変換してから月末を取る、という流れになります。
using System;
public static class MonthUtil
{
public static DateTime GetThisMonthEndJstFromUtcNow()
{
DateTime utcNow = DateTime.UtcNow;
TimeZoneInfo jst = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");
DateTime nowJst = TimeZoneInfo.ConvertTimeFromUtc(utcNow, jst);
DateTime monthStartJst = new DateTime(nowJst.Year, nowJst.Month, 1, 0, 0, 0, DateTimeKind.Unspecified);
DateTime nextMonthStartJst = monthStartJst.AddMonths(1);
DateTime monthEndJst = nextMonthStartJst.AddDays(-1);
return monthEndJst;
}
}
C#「どのタイムゾーンの月末か」をユーティリティ名やコメントに明示しておくと、
後から読んだ人が迷わずに済みます。
まとめ 「月末取得ユーティリティ」は“月次ロジックの終点を固定するもの”
月末取得は、
締め処理・集計・請求・サブスクなど、
月単位のロジックの「終点」を決めるための重要なピースです。
押さえておきたいポイントは、次のようなイメージです。
任意の日付の月末は「その月の月初 → 翌月の月初 → 1日戻す」で安全に求められる。
今月・先月・来月の月末は、「今月の月初」を基準に AddMonths(±1) と AddDays(-1) を組み合わせるときれいに書ける。
月初と月末をセットにした「月の期間」型(MonthRange など)を用意しておくと、集計や締め処理のコードが読みやすくなる。
UTC基準かローカル基準か(どのタイムゾーンの月末か)を意識し、ユーティリティ名にもそれを反映しておくと安全。
ここを押さえておくと、
「その場しのぎで日付をいじっている」状態から一歩進んで、
“月次処理の始点(月初)と終点(月末)をきれいに定義した、実務で使える日付ユーティリティ”を
自分のC#コードの中に自然に組み込めるようになります。
