C# Tips | 日付・時間処理:経過日数計算

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

はじめに 「経過日数計算」は“ビジネスの距離感”を数値にする

「申込から何日経った?」「納品から何日以内?」「障害発生から何日経過?」
こういう“日数の距離”は、業務システムで本当によく出てきます。

C# では、DateTimeDateOnly の差を取ることで、
簡単に「経過日数」を計算できます。
ただし、「端を含めるか」「時刻をどう扱うか」で結果が変わるので、
そこをきちんと意識して設計することが大事です。

ここでは、
基本の TimeSpan、日付だけの計算、端の含み方の違い、
実務でよくあるパターンまで、初心者向けにかみ砕いて説明していきます。


基本:DateTime の引き算で TimeSpan を得る

2つの日時の差は TimeSpan になる

C# では、DateTime 同士を引き算すると TimeSpan が返ってきます。

using System;

DateTime start = new DateTime(2026, 2, 10, 9, 0, 0);
DateTime end   = new DateTime(2026, 2, 13, 18, 0, 0);

TimeSpan diff = end - start;

Console.WriteLine(diff);           // 3.09:00:00 (3日と9時間)
Console.WriteLine(diff.TotalDays); // 3.375
Console.WriteLine(diff.Days);      // 3
C#

ここで重要なのは、TimeSpan には「日」「時間」「分」などが含まれていて、
TotalDays は「日+時間を含めた“日数”」、
Days は「切り捨てられた日数部分」だということです。

例えば、3日と9時間の差なら、

  • TotalDays は 3.375(日)
  • Days は 3(日)

になります。

「経過日数」をどう定義するかによって、
どちらを使うかが変わります。


日付だけで経過日数を計算する(時刻を無視する)

「○日から○日までで何日経った?」という素直なパターン

多くの業務では、
「2026/02/10 から 2026/02/13 までで何日経過?」
のように、日付単位で考えたいことが多いです。

この場合、時刻は無視して、
「日付だけ」で差を取るのがシンプルです。

DateTime d1 = new DateTime(2026, 2, 10);
DateTime d2 = new DateTime(2026, 2, 13);

TimeSpan diff = d2.Date - d1.Date;

Console.WriteLine(diff.Days); // 3
C#

DateTime.Date は「その日の 0:00:00」にそろえた DateTime を返します。
これを使うことで、「日付だけの差」を安全に取ることができます。

DateOnly を使うとさらに意図がはっきりする

.NET 6 以降なら、日付だけを扱う DateOnly が使えます。

DateOnly d1 = new DateOnly(2026, 2, 10);
DateOnly d2 = new DateOnly(2026, 2, 13);

int diffDays = d2.DayNumber - d1.DayNumber;

Console.WriteLine(diffDays); // 3
C#

DayNumber は「ある基準日からの通し日数」なので、
その差を取れば「経過日数」がそのまま出てきます。

年をまたいでも、うるう年でも、
この差分計算は正しく動きます。


「端を含めるかどうか」で結果が変わる

よくある2つの解釈

経過日数には、よくある2つの解釈があります。

  1. 「開始日から終了日までの“間”の長さ」
  2. 「開始日と終了日を“日数として数える”」

例えば、2026/02/10 から 2026/02/13 の場合。

1 の解釈だと、10→11→12→13 で「3日経過」。
2 の解釈だと、10, 11, 12, 13 の「4日分」。

コードでいうと、

int diff1 = (d2.Date - d1.Date).Days;     // 3
int diff2 = (d2.Date - d1.Date).Days + 1; // 4
C#

どちらが正しい、という話ではなく、
「業務上どちらの数え方をしたいか」を決める必要があります。

例えば、

  • 「納品から何日経過?」 → 1 の解釈が多い
  • 「利用可能日数(開始日と終了日を含む)」 → 2 の解釈が多い

といった感じです。

ここを曖昧にしたまま実装すると、
「ユーザーの感覚とシステムの表示がズレる」原因になります。


経過日数計算のユーティリティ化

日付だけの経過日数を求めるメソッド

まずは、日付だけを使ったシンプルなユーティリティを作ってみます。

public static class ElapsedDaysUtil
{
    public static int GetElapsedDays(DateTime from, DateTime to)
    {
        DateTime d1 = from.Date;
        DateTime d2 = to.Date;

        if (d2 < d1)
        {
            throw new ArgumentException("終了日が開始日より前です。");
        }

        TimeSpan diff = d2 - d1;
        return diff.Days;
    }

    public static int GetElapsedDaysInclusive(DateTime from, DateTime to)
    {
        return GetElapsedDays(from, to) + 1;
    }
}
C#

使い方の例です。

DateTime from = new DateTime(2026, 2, 10, 9, 0, 0);
DateTime to   = new DateTime(2026, 2, 13, 18, 0, 0);

int days1 = ElapsedDaysUtil.GetElapsedDays(from, to);          // 3
int days2 = ElapsedDaysUtil.GetElapsedDaysInclusive(from, to); // 4

Console.WriteLine(days1);
Console.WriteLine(days2);
C#

ここでの重要ポイントは、

  • 時刻を .Date で切り捨てて、日付だけで比較していること
  • 「端を含む/含まない」をメソッド名で分けていること

です。

「どっちの数え方か」がメソッド名から分かるようにしておくと、
後から見たときに混乱しません。


実務でよくあるパターン:締切までの残り日数

「今日から締切日まで、あと何日?」

例えば、締切日が 2026/02/20 のとき、
「今日(2026/02/10)から締切まであと何日?」
を出したいとします。

DateTime today    = new DateTime(2026, 2, 10);
DateTime deadline = new DateTime(2026, 2, 20);

int remaining = ElapsedDaysUtil.GetElapsedDays(today, deadline);

Console.WriteLine(remaining); // 10
C#

この場合、

  • 10日後が締切
  • 今日を 0 日目としたときの「距離」が 10

という意味になります。

もし「締切日も含めた残り日数」を出したいなら、
GetElapsedDaysInclusive を使えばよいです。

int remainingInclusive = ElapsedDaysUtil.GetElapsedDaysInclusive(today, deadline);

Console.WriteLine(remainingInclusive); // 11
C#

どちらを画面に出すかは、
ユーザーにどう説明するかとセットで決める必要があります。


実務での注意点1:時刻を含めた「正確な日数」が欲しい場合

「丸3日経過したかどうか」を判定したいケース

障害対応や SLA などでは、
「発生から丸3日経過したか?」
のように、時刻まで含めた経過時間が重要になることがあります。

この場合は、TotalDays を使うのが素直です。

DateTime start = new DateTime(2026, 2, 10, 9, 0, 0);
DateTime now   = new DateTime(2026, 2, 13, 8, 59, 59);

TimeSpan diff = now - start;

Console.WriteLine(diff.TotalDays); // 2.999988...
C#

「丸3日経過したか?」を判定するなら、

bool passed3Days = diff.TotalDays >= 3.0;
C#

のように書けます。

ここでは、「日付」ではなく「時間の長さ」として扱っていることがポイントです。
日付ベースの経過日数とは別物として考えましょう。


実務での注意点2:タイムゾーンと UTC の扱い

「どのタイムゾーンの“日付”で数えるか」

サーバー内部で UTC を使っている場合、
「日本時間で見ると 2/10 だが、UTC では 2/9」
という状況が普通に起こります。

経過日数を「日本のカレンダー」で考えたいなら、

  1. UTC の日時を日本時間に変換する
  2. その上で .DateDateOnly を使って日付差を取る

という流れにする必要があります。

public static int GetElapsedDaysInTimeZone(DateTime fromUtc, DateTime toUtc, TimeZoneInfo timeZone)
{
    DateTime fromLocal = TimeZoneInfo.ConvertTimeFromUtc(fromUtc, timeZone);
    DateTime toLocal   = TimeZoneInfo.ConvertTimeFromUtc(toUtc, timeZone);

    return ElapsedDaysUtil.GetElapsedDays(fromLocal, toLocal);
}
C#

「どの国・どのタイムゾーンの“日付”で数えているか」を
ユーティリティ名やコメントに明示しておくと、
後から読んだ人が誤解しにくくなります。


まとめ 「経過日数計算ユーティリティ」は“距離の定義をコードに固定するもの」

経過日数計算は、一見シンプルですが、
「端を含めるか」「時刻をどう扱うか」「どのタイムゾーンか」で
結果が変わる、奥の深いテーマです。

押さえておきたいポイントを整理すると、こうなります。

経過日数の基本は「2つの日時の差(TimeSpan)を取り、日付ベースか時間ベースかを決めて使う」。
日付だけで考えたい場合は、.DateDateOnly を使って「日付差」を取るとシンプルで安全。
「端を含む/含まない」で結果が 1 日変わるので、メソッドを分けるか、名前で明示しておく。
締切や残り日数など、業務でよく使うパターンはユーティリティ化しておくと、画面やロジックがスッキリする。
UTC とローカルのズレを意識し、「どのタイムゾーンのカレンダーで数えているか」を最初に設計しておくと、後からのトラブルを防げる。

ここを押さえておけば、
「なんとなく日付を引き算している」状態から一歩進んで、
“ビジネスの距離感を正しく表現できる、実務で使える経過日数計算ユーティリティ”を
自分の C# コードの中に自然に組み込めるようになります。

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