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

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

はじめに 「日付加算」は“締切・有効期限・リマインド”の土台になる

業務システムで「◯日後」「◯ヶ月後」「◯時間後」は、
締切、有効期限、リマインド日時、サブスクリプションの更新日など、あらゆるところに出てきます。

C# では DateTimeDateTimeOffset に対して

  • 日を足す(AddDays)
  • 月を足す(AddMonths)
  • 時間・分・秒を足す(AddHours / AddMinutes / AddSeconds)
  • 任意の時間量を足す(TimeSpan)

といったメソッドが用意されています。

ここでは、初心者向けに

現在日時からの加算
特定日付からの加算
月加算のクセ(末日の扱い)
業務でよくある「営業日加算」の考え方

を、例題を交えながらかみ砕いて説明します。


基本:DateTime に日・時間を足す

現在日時から「◯日後」「◯時間後」を求める

一番シンプルな形です。

using System;

DateTime now = DateTime.Now;

DateTime threeDaysLater = now.AddDays(3);
DateTime twoHoursLater  = now.AddHours(2);
DateTime tenMinutesLater = now.AddMinutes(10);

Console.WriteLine(now);            // 2026/02/10 20:00:00 など
Console.WriteLine(threeDaysLater); // 2026/02/13 20:00:00
Console.WriteLine(twoHoursLater);  // 2026/02/10 22:00:00
Console.WriteLine(tenMinutesLater);// 2026/02/10 20:10:00
C#

AddDays(3) のように、
「元の日時に対して、指定した日数を足した新しい DateTime を返す」イメージです。

元の DateTime は変更されません(DateTime はイミュータブル=不変型)。
常に「新しい値が返ってくる」と覚えておいてください。

マイナスを渡せば「◯日前」「◯時間前」

引数にマイナスを渡すと、逆方向になります。

DateTime fiveDaysAgo = now.AddDays(-5);
DateTime oneHourAgo  = now.AddHours(-1);

Console.WriteLine(fiveDaysAgo); // 5日前
Console.WriteLine(oneHourAgo);  // 1時間前
C#

「◯日前」「◯時間前」も、同じメソッドで書けるのがポイントです。


月加算:AddMonths の“末日ルール”を理解する

「1ヶ月後」は単純な日数加算ではない

「1ヶ月後」は、単純に 30 日足せばいいわけではありません。
月によって日数が違うからです。

C# では、AddMonths が「カレンダー上の月」を意識して計算してくれます。

DateTime d1 = new DateTime(2026, 2, 10); // 2026/02/10

DateTime oneMonthLater = d1.AddMonths(1);

Console.WriteLine(oneMonthLater); // 2026/03/10
C#

ここまでは直感通りですが、
重要なのは「末日近辺」の挙動です。

1月31日の1ヶ月後はどうなるか

DateTime d2 = new DateTime(2026, 1, 31); // 2026/01/31

DateTime oneMonthLater2 = d2.AddMonths(1);

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

2月には31日がないので、
「存在しない日付」になってしまいます。

このとき AddMonths は、
「その月の末日に丸める」というルールで動きます。

つまり、

1月31日 + 1ヶ月 → 2月の末日(28日 or 29日)
3月31日 + 1ヶ月 → 4月30日

のようになります。

この“末日ルール”は、
サブスクリプションの更新日や請求日など、
「毎月◯日」というロジックを書くときにとても重要です。


TimeSpan を使った柔軟な加算

「◯日と◯時間と◯分」をまとめて扱う

AddDaysAddHours を連続で呼んでもいいのですが、
「2日と3時間と15分後」のような複合的な加算には TimeSpan が便利です。

DateTime now = DateTime.Now;

TimeSpan span = new TimeSpan(days: 2, hours: 3, minutes: 15, seconds: 0);

DateTime target = now.Add(span);

Console.WriteLine(now);
Console.WriteLine(target); // 2日と3時間15分後
C#

TimeSpan は「時間の長さ」を表す型で、
DateTime に対して Add(TimeSpan) で足し算できます。

TimeSpan.FromDays(…)FromHours(…) などのヘルパーもあります。

TimeSpan span2 = TimeSpan.FromDays(10) + TimeSpan.FromHours(5);

DateTime target2 = now.Add(span2);
C#

「期間を変数として持っておきたい」場合は、
TimeSpan を使うとコードが整理しやすくなります。


実務ユーティリティとしてのまとめ方

「現在から◯日後/◯ヶ月後」を返すユーティリティ

あちこちで

DateTime.Now.AddDays(3)
DateTime.Now.AddMonths(1)
C#

と書いていると、
テストしづらかったり、「ローカルかUTCか」が曖昧になったりします。

そこで、「現在時刻の取得」と「加算」をユーティリティにまとめておくと便利です。

using System;

public static class DateAddUtil
{
    public static DateTime NowLocalPlusDays(int days)
        => DateTime.Now.AddDays(days);

    public static DateTime NowUtcPlusDays(int days)
        => DateTime.UtcNow.AddDays(days);

    public static DateTime NowLocalPlusMonths(int months)
        => DateTime.Now.AddMonths(months);

    public static DateTime NowUtcPlusMonths(int months)
        => DateTime.UtcNow.AddMonths(months);

    public static DateTime AddBusinessDays(DateTime start, int businessDays)
    {
        int direction = businessDays >= 0 ? 1 : -1;
        int remaining = Math.Abs(businessDays);
        DateTime current = start;

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

            if (current.DayOfWeek != DayOfWeek.Saturday &&
                current.DayOfWeek != DayOfWeek.Sunday)
            {
                remaining--;
            }
        }

        return current;
    }
}
C#

使い方の例です。

DateTime threeDaysLater = DateAddUtil.NowLocalPlusDays(3);
DateTime oneMonthLaterUtc = DateAddUtil.NowUtcPlusMonths(1);

Console.WriteLine(threeDaysLater);
Console.WriteLine(oneMonthLaterUtc);

// 営業日加算の例:今日から3営業日後
DateTime today = DateTime.Today;
DateTime threeBizDaysLater = DateAddUtil.AddBusinessDays(today, 3);

Console.WriteLine(threeBizDaysLater);
C#

ここで AddBusinessDays は、
「土日を飛ばして◯営業日後を求める」簡易版です(祝日は考慮していません)。

こうした「業務ルールを含んだ加算」をユーティリティに閉じ込めておくと、
呼び出し側のコードがとても読みやすくなります。


業務でよくある「営業日加算」の考え方

土日を飛ばすだけでも立派なユーティリティ

先ほどの AddBusinessDays のように、
「土日を除いてカウントする」だけでも、
多くの業務では十分役に立ちます。

例えば、「3営業日以内に返信する」というルールなら、

DateTime deadline = DateAddUtil.AddBusinessDays(DateTime.Today, 3);
C#

のように書けます。

祝日も考慮したい場合

祝日も除外したい場合は、
別途「祝日カレンダー」を持っておき、
ループの中で「その日が祝日かどうか」を判定する必要があります。

イメージとしてはこうです。

public static DateTime AddBusinessDaysWithHolidays(
    DateTime start,
    int businessDays,
    Func<DateTime, bool> isHoliday)
{
    int direction = businessDays >= 0 ? 1 : -1;
    int remaining = Math.Abs(businessDays);
    DateTime current = start;

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

        bool isWeekend =
            current.DayOfWeek == DayOfWeek.Saturday ||
            current.DayOfWeek == DayOfWeek.Sunday;

        if (!isWeekend && !isHoliday(current))
        {
            remaining--;
        }
    }

    return current;
}
C#

isHoliday には、
「その日が祝日なら true を返す関数」を渡します。

祝日ロジック自体は別のユーティリティに切り出しておくと、
テストやメンテナンスがしやすくなります。


UTCとローカルのどちらで加算するかを意識する

内部処理はUTC、表示はローカル、加算はどっち?

日時加算でも、
「UTCで加算するのか」「ローカルで加算するのか」を意識する必要があります。

例えば、
「有効期限は“日本時間で”3日後の23:59まで」といった要件なら、
日本時間(JST)に変換してから加算するほうが自然です。

DateTime utcNow = DateTime.UtcNow;

// まずJSTに変換(例)
TimeZoneInfo jst = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");
DateTime nowJst = TimeZoneInfo.ConvertTimeFromUtc(utcNow, jst);

// 日本時間で3日後の23:59
DateTime expireJst = nowJst.Date.AddDays(3).AddHours(23).AddMinutes(59);

// 保存はUTCに戻してから
DateTime expireUtc = TimeZoneInfo.ConvertTimeToUtc(expireJst, jst);
C#

「どの時間軸で加算するか」を間違えると、
サマータイムやタイムゾーンをまたいだときにズレが出ます。

業務要件として、
「どの国・どのタイムゾーンの“◯日後”なのか」を
最初に決めておくことがとても大事です。


まとめ 「日付加算ユーティリティ」は“時間のルールをコードに落とし込む器”

日付加算は、一見シンプルですが、
締切、有効期限、営業日、タイムゾーンなど、
業務ルールがぎゅっと詰まるポイントです。

押さえておきたいのは次のようなことです。

AddDays / AddHours / AddMinutes で、現在や任意の日付から簡単に「◯日後/◯時間後」を求められること。
AddMonths は「末日ルール」があり、存在しない日付はその月の末日に丸められること。
複合的な加算には TimeSpan を使うと、期間を変数として扱いやすいこと。
営業日加算のような業務ルールを含む処理は、ユーティリティメソッドに閉じ込めておくと、呼び出し側がすっきりすること。
UTCかローカルか、どのタイムゾーンの“◯日後”なのかを意識して加算しないと、時間のズレが起きること。

ここを押さえておくと、
「なんとなく AddDays している」状態から一歩進んで、
“業務ルールをきちんと反映した、信頼できる日付加算ユーティリティ”を
自分のC#コードの中に組み込めるようになっていきます。

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