はじめに 「経過時間計算」は“ビジネスのリアルタイム感”を数値にする
「処理に何秒かかった?」「障害発生から何時間経過?」「滞在時間は何分?」
こういう“時間の距離”は、業務・実務の世界でかなり頻繁に出てきます。
C# では、2つの DateTime の差を取ることで、TimeSpan という「時間の長さ」を表す型が手に入ります。
この TimeSpan をどう読むか、どうフォーマットするかを押さえると、
経過時間計算は一気に怖くなくなります。
ここでは、
基本の TimeSpan、秒・分・時間の取り方、フォーマット、
実務でよくある「処理時間計測」「滞在時間」「しきい値判定」まで、
初心者向けにかみ砕いて説明していきます。
基本:DateTime の引き算で TimeSpan を得る
2つの日時の差は「時間の長さ」になる
まずは一番基本の形から。
using System;
DateTime start = new DateTime(2026, 2, 10, 9, 0, 0); // 2026/02/10 09:00:00
DateTime end = new DateTime(2026, 2, 10, 12, 30, 0); // 2026/02/10 12:30:00
TimeSpan elapsed = end - start;
Console.WriteLine(elapsed); // 03:30:00
Console.WriteLine(elapsed.TotalHours); // 3.5
Console.WriteLine(elapsed.TotalMinutes); // 210
Console.WriteLine(elapsed.TotalSeconds); // 12600
C#ここで押さえておきたいのは、次の2点です。
TimeSpan は「時間の長さ」を表す型であり、「何時間何分何秒」という情報を持っている。TotalHours や TotalMinutes は「全体を時間・分・秒に換算した値」で、小数も含む。
例えば、3時間30分なら、TotalHours は 3.5、TotalMinutes は 210 になります。
「何時間かかった?」と聞かれているのか、
「何分かかった?」と聞かれているのかで、
どのプロパティを使うかが変わります。
経過時間を「秒」「分」「時間」で取り出す
秒単位で欲しいとき
ログやメトリクスでは、「処理時間(秒)」で記録したいことが多いです。
DateTime start = DateTime.Now;
// 何か重い処理
System.Threading.Thread.Sleep(1234);
DateTime end = DateTime.Now;
TimeSpan elapsed = end - start;
double seconds = elapsed.TotalSeconds;
Console.WriteLine($"処理時間: {seconds:F3} 秒");
C#{seconds:F3} は小数第3位まで表示する指定です。
例えば「1.234 秒」のように出せます。
「1秒未満の差も知りたい」場合は、TotalMilliseconds を使うこともあります。
double ms = elapsed.TotalMilliseconds;
Console.WriteLine($"処理時間: {ms:F0} ミリ秒");
C#分単位・時間単位で欲しいとき
例えば「滞在時間(分)」を出したい場合。
DateTime enter = new DateTime(2026, 2, 10, 9, 15, 0);
DateTime leave = new DateTime(2026, 2, 10, 11, 5, 0);
TimeSpan stay = leave - enter;
double minutes = stay.TotalMinutes;
Console.WriteLine($"滞在時間: {minutes:F1} 分"); // 110.0 分
C#「3時間半」を「3.5時間」として扱いたいなら TotalHours、
「210分」として扱いたいなら TotalMinutes、
というように、用途に応じて選びます。
経過時間を「時:分:秒」の文字列で表示する
TimeSpan を人間向けにフォーマットする
画面やログで「03:25:10」のように表示したいことも多いです。TimeSpan は ToString でもそれなりに表示されますが、
フォーマット指定を使うと、よりコントロールしやすくなります。
TimeSpan elapsed = new TimeSpan(3, 25, 10); // 3時間25分10秒
string hms = elapsed.ToString(@"hh\:mm\:ss");
Console.WriteLine(hms); // 03:25:10
C#ポイントは、@"hh\:mm\:ss" のように、バックスラッシュで : をエスケープしているところです。TimeSpan のカスタム書式では、: は特別な意味を持つので、
「文字としてのコロン」を出したいときは \: と書きます。
「時間が24時間を超える」場合もあります。
例えば「27時間30分」のようなケースです。
TimeSpan longElapsed = TimeSpan.FromHours(27.5);
string hms = longElapsed.ToString(@"hh\:mm\:ss");
string dhms = longElapsed.ToString(@"d\.hh\:mm\:ss");
Console.WriteLine(hms); // 03:30:00 (24時間を超えた分は日としてカウントされる)
Console.WriteLine(dhms); // 1.03:30:00 (1日と3時間30分)
C#「24時間を超える可能性があるかどうか」で、
日を含めた書式にするかどうかを決めるとよいです。
実務でよくあるパターン1:処理時間計測ユーティリティ
「この処理に何秒かかったか」を簡単に測りたい
毎回 DateTime.Now を2回書くのは面倒なので、
小さなユーティリティにしてしまうのもよくあります。
最もシンプルな形は、Stopwatch を使う方法です。
using System;
using System.Diagnostics;
public static class MeasureUtil
{
public static TimeSpan Measure(Action action)
{
var sw = Stopwatch.StartNew();
action();
sw.Stop();
return sw.Elapsed;
}
}
C#使い方の例です。
TimeSpan elapsed = MeasureUtil.Measure(() =>
{
// 計測したい処理
System.Threading.Thread.Sleep(1234);
});
Console.WriteLine($"処理時間: {elapsed.TotalMilliseconds:F0} ms");
C#Stopwatch は高精度なタイマーで、DateTime.Now よりも「経過時間計測」に向いています。
「業務ロジックのどこが重いか」を調べるときに、
このようなユーティリティを使うと、
計測コードをきれいに保てます。
実務でよくあるパターン2:滞在時間・利用時間の計算
入室・退室時刻から滞在時間を出す
例えば、会議室の利用時間を計算したいとします。
DateTime enter = new DateTime(2026, 2, 10, 9, 0, 0);
DateTime leave = new DateTime(2026, 2, 10, 10, 45, 0);
TimeSpan stay = leave - enter;
double minutes = stay.TotalMinutes;
Console.WriteLine($"利用時間: {minutes:F0} 分"); // 105 分
C#「30分単位で課金したい」などの要件がある場合は、
切り上げ・切り捨てのロジックを足します。
double unitMinutes = 30.0;
int units = (int)Math.Ceiling(minutes / unitMinutes);
Console.WriteLine($"課金単位数: {units}"); // 4(= 120分分)
C#ここでのポイントは、
「経過時間そのもの」と「課金・評価に使う時間」は別物として扱うことです。
まずは正確な TimeSpan を出し、その上でビジネスルールを適用します。
実務での注意点1:マイナスの経過時間
終了時刻が開始時刻より前だった場合
end - start で TimeSpan を取るとき、end が start より前だと、マイナスの TimeSpan になります。
DateTime start = new DateTime(2026, 2, 10, 12, 0, 0);
DateTime end = new DateTime(2026, 2, 10, 9, 0, 0);
TimeSpan elapsed = end - start;
Console.WriteLine(elapsed); // -03:00:00
Console.WriteLine(elapsed.TotalHours); // -3
C#これをどう扱うかは、要件次第です。
「マイナスはありえないはず」という仕様なら、
ユーティリティ側でチェックして例外を投げるのも一つの手です。
public static TimeSpan GetElapsedNonNegative(DateTime start, DateTime end)
{
if (end < start)
{
throw new ArgumentException("終了時刻が開始時刻より前です。");
}
return end - start;
}
C#「絶対値だけ欲しい」なら、Duration() を使うこともできます。
TimeSpan abs = elapsed.Duration(); // 03:00:00
C#ただし、「本来ありえないマイナス」を絶対値でごまかすと、
バグを見逃すことになるので、
業務ロジックとしてどうあるべきかを先に決めておくのが大事です。
実務での注意点2:タイムゾーンと UTC の扱い
「どの時間軸での経過時間か」を意識する
サーバー内部で UTC を使っている場合、
「日本時間で見ると 9:00→12:00 だが、UTC では 0:00→3:00」
のように、見た目の時刻が変わります。
経過時間そのものは、
UTC でもローカルでも同じ長さになりますが、
「日付をまたぐかどうか」「日をまたいだように見えるか」は変わります。
例えば、
日本時間 23:00 → 翌日 1:00 の2時間は、
UTC だと 14:00 → 16:00 で同じ2時間です。
経過時間計算そのものは end - start で問題ありませんが、
「どのタイムゾーンの時刻を画面に出すか」
「どのタイムゾーンで日付をまたいだとみなすか」
は別途設計が必要です。
実務的には、
サーバー内部では UTC で持つ。
画面表示やユーザーの感覚に合わせたいときは、対象タイムゾーンに変換してから表示する。
という方針にしておくと、
経過時間計算と表示ロジックをきれいに分離できます。
まとめ 「経過時間計算ユーティリティ」は“時間の長さを正しく測る物差し”
経過時間計算は、
「2つの時刻の差を取る」というシンプルな操作の中に、
秒・分・時間、フォーマット、マイナス、タイムゾーンなど、
実務で大事な要素がぎゅっと詰まっています。
押さえておきたいポイントをコンパクトにまとめると、こうなります。
2つの DateTime の差は TimeSpan になり、TotalSeconds、TotalMinutes、TotalHours で欲しい単位の長さを取り出せる。
人間向けの表示には、TimeSpan.ToString(@"hh\:mm\:ss") や @"d\.hh\:mm\:ss" のようなカスタム書式が使える。
処理時間計測には Stopwatch を使った小さなユーティリティを用意しておくと、計測コードがスッキリする。
マイナスの経過時間がありえるかどうかを仕様として決め、ありえないならチェックして例外にするなど、早めに気づけるようにする。
UTC とローカルの違いは「表示」と「日付の境界」に影響するが、経過時間そのものは差分で正しく出るので、時間の長さと表示の問題を分けて考える。
ここを押さえておけば、
「なんとなく Now を2回取って引き算している」状態から一歩進んで、
“ビジネスのリアルタイム感をきちんと数値化できる、実務で使える経過時間計算ユーティリティ”を
自分の C# コードの中に自然に組み込めるようになります。
