はじめに 「経過日数計算」は“ビジネスの距離感”を数値にする
「申込から何日経った?」「納品から何日以内?」「障害発生から何日経過?」
こういう“日数の距離”は、業務システムで本当によく出てきます。
C# では、DateTime や DateOnly の差を取ることで、
簡単に「経過日数」を計算できます。
ただし、「端を含めるか」「時刻をどう扱うか」で結果が変わるので、
そこをきちんと意識して設計することが大事です。
ここでは、
基本の 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つの解釈があります。
- 「開始日から終了日までの“間”の長さ」
- 「開始日と終了日を“日数として数える”」
例えば、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」
という状況が普通に起こります。
経過日数を「日本のカレンダー」で考えたいなら、
- UTC の日時を日本時間に変換する
- その上で
.DateやDateOnlyを使って日付差を取る
という流れにする必要があります。
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)を取り、日付ベースか時間ベースかを決めて使う」。
日付だけで考えたい場合は、.Date や DateOnly を使って「日付差」を取るとシンプルで安全。
「端を含む/含まない」で結果が 1 日変わるので、メソッドを分けるか、名前で明示しておく。
締切や残り日数など、業務でよく使うパターンはユーティリティ化しておくと、画面やロジックがスッキリする。
UTC とローカルのズレを意識し、「どのタイムゾーンのカレンダーで数えているか」を最初に設計しておくと、後からのトラブルを防げる。
ここを押さえておけば、
「なんとなく日付を引き算している」状態から一歩進んで、
“ビジネスの距離感を正しく表現できる、実務で使える経過日数計算ユーティリティ”を
自分の C# コードの中に自然に組み込めるようになります。
