C# Tips | 日付・時間処理:月初取得

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

はじめに 「月初取得」は“集計と締め処理のスタート地点”

業務システムで「今月分の売上」「今月の勤務時間」「請求書の対象期間」などを扱うとき、
必ずと言っていいほど出てくるのが「月初(その月の1日)」です。

「ある日付が属する月の月初を求める」
「今月の月初」「先月の月初」「来月の月初」
これをサッと出せるユーティリティを持っていると、集計系・締め処理系のコードが一気に読みやすくなります。

C# では、DateTime の「年」と「月」だけを使って new DateTime(year, month, 1) を作る、というシンプルな考え方で実現できます。


基本の考え方:年と月から「1日」を作る

どんな日付でも「その月の1日」にそろえる

まずは、ある DateTime が与えられたときに、
「その日付が属する月の月初」を求める一番基本的な方法です。

using System;

DateTime any = new DateTime(2026, 2, 10, 15, 30, 0); // 2026/02/10 15:30

DateTime monthStart = new DateTime(any.Year, any.Month, 1);

Console.WriteLine(any);        // 2026/02/10 15:30:00
Console.WriteLine(monthStart); // 2026/02/01 0:00:00
C#

ポイントは、any.Yearany.Month をそのまま使い、
日付だけ 1 に固定して new DateTime(year, month, 1) を作ることです。

これで「その月の1日 0:00:00」の DateTime が得られます。
時刻部分は 0:00:00 にリセットされるので、
「日付だけを見たい」集計処理などにはちょうどよい形になります。


ユーティリティメソッドとしての「月初取得」

任意の日付から月初を求めるメソッド

毎回 new DateTime(d.Year, d.Month, 1) と書くのも悪くはないですが、
「これは月初を取っているんだ」と一目で分かるように、
ユーティリティメソッドにしてしまうのがおすすめです。

public static class MonthUtil
{
    public static DateTime GetMonthStart(DateTime date)
    {
        return new DateTime(date.Year, date.Month, 1, date.Hour, date.Minute, date.Second, date.Kind);
    }
}
C#

ここでは、あえて時刻と Kind(Local / Utc / Unspecified)も引き継いでいます。
「日付だけ 1 日にそろえ、時刻と時間軸はそのまま」という方針です。

もし「必ず 0:00:00 にしたい」なら、もっとシンプルにしても構いません。

public static DateTime GetMonthStart(DateTime date)
{
    return new DateTime(date.Year, date.Month, 1, 0, 0, 0, date.Kind);
}
C#

使い方はとても直感的です。

DateTime d = new DateTime(2026, 2, 10, 15, 30, 0, DateTimeKind.Local);

DateTime monthStart = MonthUtil.GetMonthStart(d);

Console.WriteLine(monthStart); // 2026/02/01 0:00:00 (Local)
C#

「このメソッドを通せば月初が返ってくる」という約束がコード上に見えるのが大事です。


「今月」「先月」「来月」の月初を取る

今月の月初

今月の月初は、DateTime.Today(今日の日付、時刻は 0:00)を使うと分かりやすく書けます。

DateTime today = DateTime.Today;

DateTime thisMonthStart = new DateTime(today.Year, today.Month, 1);

Console.WriteLine(today);          // 2026/02/10 0:00:00 など
Console.WriteLine(thisMonthStart); // 2026/02/01 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);
    }
}
C#

先月の月初・来月の月初

「先月」「来月」は、AddMonths を組み合わせるときれいに書けます。

public static class MonthUtil
{
    public static DateTime GetThisMonthStartLocal()
    {
        DateTime today = DateTime.Today;
        return new DateTime(today.Year, today.Month, 1, 0, 0, 0, DateTimeKind.Local);
    }

    public static DateTime GetNextMonthStartLocal()
    {
        DateTime thisMonthStart = GetThisMonthStartLocal();
        return thisMonthStart.AddMonths(1);
    }

    public static DateTime GetPreviousMonthStartLocal()
    {
        DateTime thisMonthStart = GetThisMonthStartLocal();
        return thisMonthStart.AddMonths(-1);
    }
}
C#

使い方の例です。

DateTime thisMonth = MonthUtil.GetThisMonthStartLocal();
DateTime nextMonth = MonthUtil.GetNextMonthStartLocal();
DateTime prevMonth = MonthUtil.GetPreviousMonthStartLocal();

Console.WriteLine(thisMonth); // 2026/02/01 0:00:00
Console.WriteLine(nextMonth); // 2026/03/01 0:00:00
Console.WriteLine(prevMonth); // 2026/01/01 0:00:00
C#

「今月の月初を基準に、AddMonths(±1) でずらす」というパターンを覚えておくと、
月単位の期間計算がとても書きやすくなります。


月初とセットでよく使う「月末取得」

月末は「翌月の月初 − 1日」で求める

月初ユーティリティを作ると、
ほぼ必ず「月末も欲しい」という話になります。

月末は、「翌月の月初から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);
        return nextMonthStart.AddDays(-1);
    }
}
C#

使い方の例です。

DateTime d = new DateTime(2026, 2, 10);

DateTime monthStart = MonthUtil.GetMonthStart(d);
DateTime monthEnd   = MonthUtil.GetMonthEnd(d);

Console.WriteLine(monthStart); // 2026/02/01
Console.WriteLine(monthEnd);   // 2026/02/28(うるう年でなければ)
C#

「月初を取る → 翌月の月初を取る → 1日戻す」という流れにしておくと、
2月やうるう年も含めて、すべての月で正しい月末が求められます。

月初ユーティリティを軸に、月末も一緒に設計しておくと、
月次集計や締め処理のコードがとてもスッキリします。


UTCとローカル、どちらの月初を扱うか

内部処理はUTC、表示はローカル、月初はどっち基準?

日時処理全般と同じく、月初取得でも
「UTC基準なのか、ローカル(例えば日本時間)基準なのか」を意識する必要があります。

例えば、「日本時間での今月の月初」を求めたい場合は、
まず UTC から日本時間に変換してから月初を取る、という流れになります。

using System;

public static class MonthUtil
{
    public static DateTime GetThisMonthStartJstFromUtcNow()
    {
        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);

        return monthStartJst;
    }
}
C#

「どのタイムゾーンの“月初”なのか」を要件として決めておき、
ユーティリティの名前やコメントにもそれを反映しておくと、
後から読んだときに迷いにくくなります。


まとめ 「月初取得ユーティリティ」は“月次ロジックの基準点”

月初取得は、
売上集計、勤怠集計、請求締め、サブスク更新など、
月次ロジックのほぼすべてのスタート地点になります。

押さえておきたいポイントは、次のようなイメージです。

任意の日付の月初は new DateTime(date.Year, date.Month, 1, …) で作れる。
「今月」「先月」「来月」の月初は、「今月の月初」を基準に AddMonths(±1) でずらすときれいに書ける。
月末は「翌月の月初 − 1日」で求めると、全ての月で正しく計算できる。
UTC基準かローカル基準か(どのタイムゾーンの月初か)を意識し、ユーティリティ名にもそれを反映しておくと安全。

このあたりをきちんと押さえておくと、
「その場しのぎで日付をいじっている」状態から一歩進んで、
“月次処理の基準点をきれいに定義した、実務で使える日付ユーティリティ”を
自分のC#コードの中に自然に組み込めるようになります。

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