C#で経過時間を計測する方法|Stopwatchの使い方とDateTimeとの違いをわかりやすく解説

はじめに

C#で処理の経過時間を計測したい場面はよくあります。たとえば、あるメソッドの実行時間を確認したいとき、ループ処理がどのくらい時間を使っているか調べたいとき、非同期処理の待ち時間を含めて処理時間をログに残したいときなどです。

C#で経過時間を測る方法としては、主にStopwatchクラスを使う方法と、DateTimeを使って開始時刻と終了時刻の差分を求める方法があります。

結論から言うと、処理時間やパフォーマンスを計測するならStopwatchを使うのが基本です。一方で、「ある日時から現在まで何日経過したか」「開始日時と終了日時の差を求めたい」といった日時ベースの差分計算ではDateTimeTimeSpanが向いています。

この記事では、C#で経過時間を計測する方法について、Stopwatchの基本的な使い方、DateTimeとの違い、実践的なコード例、よくある失敗と対策までわかりやすく解説します。

1. C#で経過時間を計測する基本

1-1. 経過時間とは何か

経過時間とは、ある処理やイベントが始まってから終わるまでにかかった時間のことです。

たとえば、次のような時間が経過時間にあたります。

C#
処理開始

ファイル読み込み

データ変換

処理終了

この「処理開始」から「処理終了」までにかかった時間が経過時間です。

C#では、経過時間を使って次のようなことを確認できます。

C#
・メソッドの実行時間
・ループ処理にかかった時間
・API呼び出しにかかった時間
・ファイル読み込みや書き込みにかかった時間
・非同期処理全体の処理時間
・プログラムのボトルネック調査

経過時間の計測は、単に「何秒かかったか」を見るだけでなく、アプリケーションの改善やパフォーマンス分析にも役立ちます。

1-2. C#で経過時間を計測する主な方法

C#で経過時間を計測する代表的な方法は、次の2つです。

C#
・Stopwatchを使う方法
・DateTimeを使う方法

Stopwatchは、処理時間の計測に特化したクラスです。高精度なタイマーを利用して経過時間を測定できるため、パフォーマンス計測に向いています。

一方、DateTimeは日時を扱うための構造体です。現在日時を取得したり、特定の日付同士の差分を計算したりするときに使います。

たとえば、処理時間を測る場合は次のように考えるとわかりやすいです。

C#
処理時間を測りたい        Stopwatch
日時の差分を求めたい DateTime

どちらも経過時間を求めることはできますが、目的によって使い分けることが重要です。

1-3. 結論:基本はStopwatchを使うのがおすすめ

C#で処理の経過時間を計測するなら、基本的にはStopwatchを使うのがおすすめです。

理由は、Stopwatchが経過時間の計測に特化しているからです。DateTime.Nowのように現在時刻を取得して差分を計算する方法でも経過時間は求められますが、システム時刻の変更や精度の問題を考えると、処理時間の計測には向いていません。

Stopwatchを使う基本形は次のとおりです。

C#
using System.Diagnostics;

var stopwatch = Stopwatch.StartNew();

// 計測したい処理
Thread.Sleep(1000);

stopwatch.Stop();

Console.WriteLine($"経過時間: {stopwatch.ElapsedMilliseconds} ms");

このコードでは、Stopwatch.StartNew()で計測を開始し、Stop()で計測を停止しています。ElapsedMillisecondsを使うと、経過時間をミリ秒単位で取得できます。

処理時間を測る場合は、まずこの形を覚えておくとよいでしょう。

1-4. DateTimeで計測できるケースと注意点

DateTimeでも、開始時刻と終了時刻の差分を取ることで経過時間を計算できます。

C#
DateTime start = DateTime.Now;

// 計測したい処理
Thread.Sleep(1000);

DateTime end = DateTime.Now;

TimeSpan elapsed = end - start;

Console.WriteLine($"経過時間: {elapsed.TotalMilliseconds} ms");

このように、DateTime.Nowで開始時刻と終了時刻を取得し、その差をTimeSpanとして求めます。

ただし、DateTime.Nowはシステム時刻に依存します。処理中にPCの時刻が変更されたり、時刻同期が行われたりすると、実際の経過時間と異なる結果になる可能性があります。

そのため、DateTimeは次のような用途に向いています。

C#
・開始日時と終了日時の差分を求める
・ある日付から今日までの日数を計算する
・ログに記録された日時同士の差を求める
・業務上の期間を計算する

一方、プログラムの実行時間や処理性能を測る場合は、Stopwatchを使うのが適切です。

2. Stopwatchを使って経過時間を計測する方法

2-1. Stopwatchクラスとは

Stopwatchは、C#で経過時間を計測するためのクラスです。名前のとおり、ストップウォッチのように開始、停止、リセットを行いながら時間を測れます。

StopwatchクラスはSystem.Diagnostics名前空間に含まれています。

C#
using System.Diagnostics;

Stopwatchを使うと、次のような値を取得できます。

C#
・経過時間をTimeSpanで取得する
・経過時間をミリ秒で取得する
・経過時間をタイマーの刻み単位で取得する
・計測中かどうかを確認する

処理時間の計測や簡易的なパフォーマンス確認では、Stopwatchが最もよく使われます。

2-2. Stopwatchの基本的な使い方

Stopwatchの基本的な使い方は次のとおりです。

C#
using System;
using System.Diagnostics;
using System.Threading;

class Program
{
static void Main()
{
Stopwatch stopwatch = new Stopwatch();

stopwatch.Start();

// 計測したい処理
Thread.Sleep(1500);

stopwatch.Stop();

Console.WriteLine($"経過時間: {stopwatch.Elapsed}");
Console.WriteLine($"経過ミリ秒: {stopwatch.ElapsedMilliseconds} ms");
}
}

実行すると、次のような結果になります。

C#
経過時間: 00:00:01.5034567
経過ミリ秒: 1503 ms

ElapsedTimeSpan型で経過時間を取得します。秒、分、ミリ秒などに変換したい場合は、TimeSpanのプロパティを使います。

また、より簡潔に書きたい場合はStopwatch.StartNew()を使えます。

C#
Stopwatch stopwatch = Stopwatch.StartNew();

// 計測したい処理
Thread.Sleep(1500);

stopwatch.Stop();

Console.WriteLine($"経過時間: {stopwatch.ElapsedMilliseconds} ms");

StartNew()は、Stopwatchのインスタンスを作成してすぐに計測を開始するメソッドです。実務ではこの書き方がよく使われます。

2-3. Start・Stop・Reset・Restartの違い

Stopwatchには、計測を制御するためのメソッドがいくつかあります。

まず、Start()は計測を開始します。

C#
var stopwatch = new Stopwatch();
stopwatch.Start();

Stop()は計測を停止します。

C#
stopwatch.Stop();

Reset()は、経過時間をゼロに戻します。ただし、計測は開始されません。

C#
stopwatch.Reset();

Restart()は、経過時間をゼロに戻したうえで、すぐに計測を開始します。

C#
stopwatch.Restart();

それぞれの違いを整理すると次のようになります。

C#
Start()    : 計測を開始する
Stop() : 計測を停止する
Reset() : 経過時間をゼロに戻す。計測は開始しない
Restart() : 経過時間をゼロに戻して、すぐに計測を開始する

注意したいのは、Start()を再度呼び出すと、前回の経過時間に加算される点です。

C#
var stopwatch = new Stopwatch();

stopwatch.Start();
Thread.Sleep(500);
stopwatch.Stop();

stopwatch.Start();
Thread.Sleep(500);
stopwatch.Stop();

Console.WriteLine(stopwatch.ElapsedMilliseconds);

この場合、結果はおよそ1000ミリ秒になります。2回目のStart()でゼロからやり直すわけではありません。

ゼロから計測し直したい場合は、Restart()を使います。

C#
stopwatch.Restart();

2-4. Elapsed・ElapsedMilliseconds・ElapsedTicksの使い分け

Stopwatchで経過時間を取得する主なプロパティは、ElapsedElapsedMillisecondsElapsedTicksです。

Elapsedは、経過時間をTimeSpan型で取得します。

C#
TimeSpan elapsed = stopwatch.Elapsed;

秒、分、時間などに変換しやすいため、表示用に便利です。

C#
Console.WriteLine(elapsed.TotalSeconds);
Console.WriteLine(elapsed.TotalMinutes);

ElapsedMillisecondsは、経過時間をミリ秒単位の整数で取得します。

C#
long milliseconds = stopwatch.ElapsedMilliseconds;

ログ出力や簡単な処理時間の確認では、ElapsedMillisecondsがよく使われます。

ElapsedTicksは、タイマーの刻み単位で経過時間を取得します。

C#
long ticks = stopwatch.ElapsedTicks;

より細かい単位で計測したい場合に使います。ただし、ElapsedTicksの1tickが何秒に相当するかは環境に依存するため、必要に応じてStopwatch.Frequencyを使って秒に変換します。

C#
double seconds = (double)stopwatch.ElapsedTicks / Stopwatch.Frequency;

使い分けの目安は次のとおりです。

C#
Elapsed              : TimeSpanとして扱いたい場合
ElapsedMilliseconds : ミリ秒単位で表示・ログ出力したい場合
ElapsedTicks : より高精度な計測をしたい場合

通常の処理時間計測では、ElapsedまたはElapsedMillisecondsを使えば十分です。

2-5. 経過時間を秒・ミリ秒・分で表示するサンプルコード

Stopwatchで計測した経過時間は、TimeSpanのプロパティを使って秒、ミリ秒、分などに変換できます。

C#
using System;
using System.Diagnostics;
using System.Threading;

class Program
{
static void Main()
{
Stopwatch stopwatch = Stopwatch.StartNew();

// 計測したい処理
Thread.Sleep(2500);

stopwatch.Stop();

TimeSpan elapsed = stopwatch.Elapsed;

Console.WriteLine($"経過時間: {elapsed}");
Console.WriteLine($"秒: {elapsed.TotalSeconds} 秒");
Console.WriteLine($"ミリ秒: {elapsed.TotalMilliseconds} ms");
Console.WriteLine($"分: {elapsed.TotalMinutes} 分");
}
}

出力例は次のようになります。

C#
経過時間: 00:00:02.5012345
: 2.5012345
ミリ秒: 2501.2345 ms
: 0.0416872416666667

ElapsedMillisecondsを使うと整数のミリ秒になります。

C#
Console.WriteLine($"ミリ秒: {stopwatch.ElapsedMilliseconds} ms");

小数を含むミリ秒が必要な場合は、Elapsed.TotalMillisecondsを使います。

C#
Console.WriteLine($"ミリ秒: {stopwatch.Elapsed.TotalMilliseconds} ms");

用途に応じて、整数でよい場合はElapsedMilliseconds、小数も必要な場合はElapsed.TotalMillisecondsを使い分けるとよいでしょう。

3. Stopwatchを使った実践的なコード例

3-1. 処理時間を計測する基本サンプル

まずは、ある処理の実行時間を計測する基本的なサンプルです。

C#
using System;
using System.Diagnostics;
using System.Threading;

class Program
{
static void Main()
{
Stopwatch stopwatch = Stopwatch.StartNew();

// 計測したい処理
HeavyProcess();

stopwatch.Stop();

Console.WriteLine($"処理時間: {stopwatch.ElapsedMilliseconds} ms");
}

static void HeavyProcess()
{
Thread.Sleep(1200);
}
}

このコードでは、HeavyProcess()の実行時間を計測しています。

実務では、時間がかかりそうな処理の前後にStopwatchを置くことで、どの処理に時間がかかっているのかを確認できます。

C#
var stopwatch = Stopwatch.StartNew();

TargetProcess();

stopwatch.Stop();

Console.WriteLine($"TargetProcessの処理時間: {stopwatch.ElapsedMilliseconds} ms");

このように、計測対象を明確にすることが大切です。

3-2. メソッド単位で実行時間を計測する方法

メソッド単位で実行時間を計測したい場合は、共通の計測用メソッドを作ると便利です。

C#
using System;
using System.Diagnostics;
using System.Threading;

class Program
{
static void Main()
{
Measure("SampleMethod", SampleMethod);
}

static void Measure(string name, Action action)
{
Stopwatch stopwatch = Stopwatch.StartNew();

action();

stopwatch.Stop();

Console.WriteLine($"{name} の処理時間: {stopwatch.ElapsedMilliseconds} ms");
}

static void SampleMethod()
{
Thread.Sleep(800);
}
}

Measureメソッドに処理名と実行したい処理を渡すことで、簡単に処理時間を計測できます。

複数の処理を比較したい場合にも便利です。

C#
Measure("処理A", ProcessA);
Measure("処理B", ProcessB);
Measure("処理C", ProcessC);
C#
static void ProcessA()
{
Thread.Sleep(300);
}

static void ProcessB()
{
Thread.Sleep(600);
}

static void ProcessC()
{
Thread.Sleep(900);
}

このように共通化しておくと、同じ形式でログを出力できるため、処理時間の比較がしやすくなります。

3-3. ループ処理の経過時間を計測する方法

ループ処理の経過時間を計測する場合も、Stopwatchを使います。

C#
using System;
using System.Diagnostics;

class Program
{
static void Main()
{
Stopwatch stopwatch = Stopwatch.StartNew();

long total = 0;

for (int i = 0; i < 10_000_000; i++)
{
total += i;
}

stopwatch.Stop();

Console.WriteLine($"合計: {total}");
Console.WriteLine($"ループ処理の経過時間: {stopwatch.ElapsedMilliseconds} ms");
}
}

ループの回数が多い処理では、少しの違いが全体の処理時間に大きく影響することがあります。

ただし、短すぎる処理を1回だけ測定すると、結果がばらつきやすくなります。そのため、正確に比較したい場合は複数回実行して平均を取るのがおすすめです。

C#
using System;
using System.Diagnostics;

class Program
{
static void Main()
{
const int repeatCount = 10;
long totalMilliseconds = 0;

for (int i = 0; i < repeatCount; i++)
{
Stopwatch stopwatch = Stopwatch.StartNew();

RunLoop();

stopwatch.Stop();

totalMilliseconds += stopwatch.ElapsedMilliseconds;
}

double average = (double)totalMilliseconds / repeatCount;

Console.WriteLine($"平均処理時間: {average} ms");
}

static void RunLoop()
{
long total = 0;

for (int i = 0; i < 10_000_000; i++)
{
total += i;
}
}
}

パフォーマンス比較では、1回の結果だけで判断しないことが重要です。

3-4. async/awaitを使った非同期処理の経過時間を計測する方法

Stopwatchは、async/awaitを使った非同期処理の経過時間計測にも使えます。

C#
using System;
using System.Diagnostics;
using System.Threading.Tasks;

class Program
{
static async Task Main()
{
Stopwatch stopwatch = Stopwatch.StartNew();

await SampleAsync();

stopwatch.Stop();

Console.WriteLine($"非同期処理の経過時間: {stopwatch.ElapsedMilliseconds} ms");
}

static async Task SampleAsync()
{
await Task.Delay(1500);
}
}

このコードでは、SampleAsync()の完了までにかかった時間を計測しています。

非同期処理では、awaitによって処理の完了を待ちます。そのため、awaitの前にStopwatchを開始し、awaitの後で停止すれば、非同期処理全体の経過時間を測定できます。

複数の非同期処理をまとめて測る場合は、次のように書けます。

C#
using System;
using System.Diagnostics;
using System.Threading.Tasks;

class Program
{
static async Task Main()
{
Stopwatch stopwatch = Stopwatch.StartNew();

await Task.WhenAll(
Task.Delay(1000),
Task.Delay(1500),
Task.Delay(2000)
);

stopwatch.Stop();

Console.WriteLine($"全体の経過時間: {stopwatch.ElapsedMilliseconds} ms");
}
}

この場合、3つの処理を並行して待つため、全体の経過時間はおよそ2000ミリ秒になります。

3-5. ログ出力と組み合わせて処理時間を記録する方法

経過時間は、コンソールに表示するだけでなく、ログとして記録すると実務で役立ちます。

C#
using System;
using System.Diagnostics;
using System.Threading;

class Program
{
static void Main()
{
Stopwatch stopwatch = Stopwatch.StartNew();

try
{
ExecuteProcess();
}
finally
{
stopwatch.Stop();
Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] ExecuteProcess 処理時間: {stopwatch.ElapsedMilliseconds} ms");
}
}

static void ExecuteProcess()
{
Thread.Sleep(1000);
}
}

try-finallyを使うと、処理中に例外が発生しても経過時間を記録できます。

ログ出力をメソッド化すると、さらに使いやすくなります。

C#
using System;
using System.Diagnostics;

class Program
{
static void Main()
{
MeasureWithLog("データ取得処理", () =>
{
Thread.Sleep(700);
});
}

static void MeasureWithLog(string processName, Action action)
{
Stopwatch stopwatch = Stopwatch.StartNew();

try
{
action();
}
finally
{
stopwatch.Stop();
Console.WriteLine($"{processName}: {stopwatch.ElapsedMilliseconds} ms");
}
}
}

処理名と経過時間をログに残しておくと、後から遅い処理を見つけやすくなります。

4. DateTimeで経過時間を計測する方法

4-1. DateTime.Nowを使った経過時間の計算方法

DateTime.Nowを使うと、現在のローカル日時を取得できます。開始時刻と終了時刻をそれぞれ取得し、差分を計算することで経過時間を求められます。

C#
using System;
using System.Threading;

class Program
{
static void Main()
{
DateTime start = DateTime.Now;

Thread.Sleep(1000);

DateTime end = DateTime.Now;

TimeSpan elapsed = end - start;

Console.WriteLine($"経過時間: {elapsed.TotalMilliseconds} ms");
}
}

end - startの結果はTimeSpan型になります。TimeSpanを使うと、日数、時間、分、秒、ミリ秒などを取得できます。

C#
Console.WriteLine(elapsed.TotalSeconds);
Console.WriteLine(elapsed.TotalMinutes);
Console.WriteLine(elapsed.TotalMilliseconds);

ただし、DateTime.Nowは現在のシステム時刻に依存します。処理時間の正確な計測には向いていないため、パフォーマンス計測ではStopwatchを使うのが基本です。

4-2. DateTime.UtcNowを使う場合の考え方

DateTime.UtcNowは、UTCの現在日時を取得します。UTCとは協定世界時のことで、タイムゾーンの影響を受けない基準時刻です。

C#
DateTime start = DateTime.UtcNow;

// 処理
Thread.Sleep(1000);

DateTime end = DateTime.UtcNow;

TimeSpan elapsed = end - start;

Console.WriteLine($"経過時間: {elapsed.TotalMilliseconds} ms");

DateTime.Nowはローカル時刻を返しますが、DateTime.UtcNowはUTC時刻を返します。

日時の差分を計算するときは、NowUtcNowを混在させないように注意してください。

C#
DateTime start = DateTime.Now;
DateTime end = DateTime.UtcNow;

// NG: ローカル時刻とUTC時刻を混在させている
TimeSpan elapsed = end - start;

このように異なる基準の日時を引き算すると、意図しない結果になる可能性があります。

ログやシステム間連携ではUtcNowが使われることも多いですが、単純な処理時間の計測であればStopwatchを選ぶのが適切です。

4-3. TimeSpanで差分を取得する方法

TimeSpanは、時間の間隔を表す型です。DateTime同士を引き算すると、結果はTimeSpanになります。

C#
DateTime start = new DateTime(2026, 1, 1, 9, 0, 0);
DateTime end = new DateTime(2026, 1, 1, 10, 30, 0);

TimeSpan difference = end - start;

Console.WriteLine(difference.TotalMinutes);

この場合、出力は90になります。

TimeSpanには次のようなプロパティがあります。

C#
Days               : 日の部分
Hours : 時間の部分
Minutes : 分の部分
Seconds : 秒の部分
Milliseconds : ミリ秒の部分
TotalDays : 合計日数
TotalHours : 合計時間
TotalMinutes : 合計分
TotalSeconds : 合計秒
TotalMilliseconds : 合計ミリ秒

注意したいのは、MinutesTotalMinutesの違いです。

C#
TimeSpan span = TimeSpan.FromMinutes(90);

Console.WriteLine(span.Minutes); // 30
Console.WriteLine(span.TotalMinutes); // 90

Minutesは「分の部分」だけを返します。一方、TotalMinutesは全体を分に換算した値を返します。

経過時間を表示するときは、目的に応じてTotalSecondsTotalMillisecondsを使うとわかりやすいです。

4-4. DateTimeによる計測サンプルコード

DateTimeを使った経過時間計測のサンプルは次のとおりです。

C#
using System;
using System.Threading;

class Program
{
static void Main()
{
DateTime startTime = DateTime.Now;

// 計測したい処理
Execute();

DateTime endTime = DateTime.Now;

TimeSpan elapsed = endTime - startTime;

Console.WriteLine($"開始時刻: {startTime:HH:mm:ss.fff}");
Console.WriteLine($"終了時刻: {endTime:HH:mm:ss.fff}");
Console.WriteLine($"経過時間: {elapsed.TotalMilliseconds} ms");
}

static void Execute()
{
Thread.Sleep(1200);
}
}

この方法では、開始時刻と終了時刻をログに残せるというメリットがあります。

たとえば、次のようなログを出したい場合にはDateTimeが便利です。

C#
開始時刻: 10:00:00.123
終了時刻: 10:00:01.345
経過時間: 1222 ms

ただし、純粋に「処理に何ミリ秒かかったか」を知りたいだけであれば、Stopwatchのほうが適しています。

開始日時や終了日時も必要な場合は、DateTimeStopwatchを組み合わせる方法もあります。

C#
DateTime startedAt = DateTime.Now;
Stopwatch stopwatch = Stopwatch.StartNew();

Execute();

stopwatch.Stop();
DateTime endedAt = DateTime.Now;

Console.WriteLine($"開始日時: {startedAt:yyyy-MM-dd HH:mm:ss}");
Console.WriteLine($"終了日時: {endedAt:yyyy-MM-dd HH:mm:ss}");
Console.WriteLine($"処理時間: {stopwatch.ElapsedMilliseconds} ms");

このようにすると、ログ用の日時と正確な処理時間の両方を記録できます。

4-5. DateTimeで経過時間を測るときの注意点

DateTimeで経過時間を測るときは、いくつか注意点があります。

まず、DateTime.Nowはシステム時刻に依存します。処理中にシステム時刻が変更されると、経過時間が実際より長くなったり短くなったりする可能性があります。

次に、タイムゾーンや夏時間の影響にも注意が必要です。ローカル時刻を使う場合、環境によって結果が変わることがあります。

また、DateTime.NowDateTime.UtcNowを混在させるのも避けるべきです。

C#
DateTime start = DateTime.Now;
DateTime end = DateTime.UtcNow;

// 意図しない差分になる可能性がある
TimeSpan elapsed = end - start;

処理時間を計測する目的であれば、DateTimeではなくStopwatchを使いましょう。

DateTimeが向いているのは、次のようなケースです。

C#
・ユーザーの登録日時から何日経過したか
・予約開始時刻から終了時刻までの時間を求める
・ログに記録された日時の差分を計算する
・期限日までの残り時間を計算する

つまり、日時そのものに意味がある場合はDateTime、処理にかかった時間を測る場合はStopwatchが適しています。

5. StopwatchとDateTimeの違い

5-1. StopwatchとDateTimeの用途の違い

StopwatchDateTimeは、どちらも時間を扱えますが、用途が異なります。

Stopwatchは経過時間を計測するためのクラスです。処理の開始から終了までに何秒かかったか、何ミリ秒かかったかを測る用途に向いています。

一方、DateTimeは日時を表すための型です。現在日時、指定日時、日付同士の差分などを扱う用途に向いています。

たとえば、次のように使い分けます。

C#
処理時間を測る               Stopwatch
APIの応答時間を測る Stopwatch
ループ処理の実行時間を測る Stopwatch
現在日時を取得する DateTime
開始日時と終了日時を記録する DateTime
登録日からの経過日数を求める DateTime

どちらを使うか迷った場合は、「処理時間を測りたいのか」「日時の差分を求めたいのか」で判断するとよいでしょう。

5-2. 精度・正確性の違い

処理時間の計測においては、Stopwatchのほうが精度や正確性の面で向いています。

Stopwatchは、経過時間を測るためのタイマーを使います。環境によっては高解像度パフォーマンスカウンターを利用できるため、短い処理時間の計測にも適しています。

一方、DateTime.Nowは現在のシステム時刻を取得するものです。時刻を表すことが目的であり、処理時間の精密な計測を目的としたものではありません。

たとえば、次のようなコードは処理時間計測としてはおすすめできません。

C#
DateTime start = DateTime.Now;

// 計測したい処理

DateTime end = DateTime.Now;

Console.WriteLine((end - start).TotalMilliseconds);

簡単な確認には使えますが、パフォーマンス計測ではStopwatchを使うべきです。

5-3. システム時刻変更の影響を受けるかどうか

DateTime.Nowはシステム時刻を取得するため、PCやサーバーの時刻変更の影響を受けます。

たとえば、処理中にシステム時刻が1分戻った場合、計測結果がマイナスに近い値になったり、実際より短く見えたりする可能性があります。

C#
DateTime start = DateTime.Now;

// この間にシステム時刻が変更される可能性がある

DateTime end = DateTime.Now;

TimeSpan elapsed = end - start;

一方、Stopwatchは経過時間の計測用に設計されているため、通常の処理時間計測ではシステム時刻変更の影響を考えずに使えます。

この違いは、長時間動作するアプリケーションやサーバーアプリケーションでは特に重要です。

5-4. パフォーマンス計測にStopwatchが向いている理由

パフォーマンス計測にStopwatchが向いている理由は、経過時間を測るために設計されているからです。

Stopwatchには、計測開始、停止、再開、リセットといった操作が用意されています。

C#
var stopwatch = new Stopwatch();

stopwatch.Start();
stopwatch.Stop();
stopwatch.Reset();
stopwatch.Restart();

また、経過時間をさまざまな形式で取得できます。

C#
stopwatch.Elapsed
stopwatch.ElapsedMilliseconds
stopwatch.ElapsedTicks

これにより、簡単なログ出力から高精度な計測まで対応できます。

処理時間を測るコードも読みやすくなります。

C#
var stopwatch = Stopwatch.StartNew();

TargetProcess();

stopwatch.Stop();

Console.WriteLine($"{stopwatch.ElapsedMilliseconds} ms");

このように、何をしているコードなのかが明確です。

5-5. 日時の差分計算にDateTimeが向いているケース

DateTimeは、日時そのものに意味がある場合に向いています。

たとえば、次のようなケースです。

C#
・会員登録日から何日経過したか
・予約開始日時と終了日時の差を求める
・ログの記録時刻から経過時間を計算する
・有効期限までの残り時間を求める
・作成日時と更新日時の差を求める

具体例を見てみましょう。

C#
DateTime registeredAt = new DateTime(2026, 1, 1);
DateTime today = DateTime.Today;

TimeSpan elapsed = today - registeredAt;

Console.WriteLine($"登録から {elapsed.TotalDays} 日経過しています。");

このように、開始日時や終了日時がデータとして意味を持つ場合はDateTimeが自然です。

ただし、日時の計算ではタイムゾーンやUTC、ローカル時刻の扱いに注意しましょう。

5-6. StopwatchとDateTimeの使い分け早見表

StopwatchDateTimeの使い分けは、次のように整理できます。

目的おすすめ理由
メソッドの処理時間を測るStopwatch経過時間計測に向いている
ループ処理の速度を測るStopwatch短い処理時間を測りやすい
APIの応答時間を測るStopwatch開始から終了までの時間を測れる
非同期処理の実行時間を測るStopwatchawaitを含めた経過時間を測れる
現在日時を取得するDateTime日時を扱うための型だから
開始日時と終了日時を保存するDateTime日時情報として意味がある
登録日からの日数を計算するDateTime日付同士の差分に向いている
ログの時刻差分を計算するDateTime記録済み日時を扱える

迷った場合は、次の基準で判断するとよいです。

C#
処理にかかった時間を知りたい  Stopwatch
日時と日時の差を知りたい DateTime

6. 経過時間計測でよくある失敗と対策

6-1. DateTime.Nowで正確な処理時間を測ろうとする

よくある失敗の1つが、DateTime.Nowで処理時間を正確に測ろうとすることです。

C#
DateTime start = DateTime.Now;

// 処理

DateTime end = DateTime.Now;

Console.WriteLine((end - start).TotalMilliseconds);

この方法でも大まかな経過時間はわかりますが、処理性能の計測には向いていません。

対策として、処理時間を測る場合はStopwatchを使いましょう。

C#
var stopwatch = Stopwatch.StartNew();

// 処理

stopwatch.Stop();

Console.WriteLine(stopwatch.ElapsedMilliseconds);

DateTimeは日時の記録や日時差分の計算に使い、処理時間の計測にはStopwatchを使うのが基本です。

6-2. StopwatchをStopし忘れる

Stopwatchを使うときに、Stop()を呼び忘れることがあります。

C#
var stopwatch = Stopwatch.StartNew();

DoSomething();

Console.WriteLine(stopwatch.ElapsedMilliseconds);

このコードでも経過時間は取得できますが、Stopwatchは動き続けています。表示後にも時間が進み続けるため、後続処理で同じインスタンスを使う場合に意図しない結果になることがあります。

計測対象が終わったら、基本的にはStop()を呼びましょう。

C#
var stopwatch = Stopwatch.StartNew();

DoSomething();

stopwatch.Stop();

Console.WriteLine(stopwatch.ElapsedMilliseconds);

例外が発生しても停止したい場合は、try-finallyを使うと安全です。

C#
var stopwatch = Stopwatch.StartNew();

try
{
DoSomething();
}
finally
{
stopwatch.Stop();
Console.WriteLine($"処理時間: {stopwatch.ElapsedMilliseconds} ms");
}

6-3. RestartとResetの違いを混同する

Restart()Reset()の違いを混同するのもよくある失敗です。

Reset()は経過時間をゼロに戻しますが、計測は開始しません。

C#
stopwatch.Reset();

Restart()は経過時間をゼロに戻し、すぐに計測を開始します。

C#
stopwatch.Restart();

次のコードでは、Reset()後にStart()を呼ばないと計測されません。

C#
stopwatch.Reset();

// ここでStartしないと計測されない
DoSomething();

stopwatch.Stop();

正しくは次のようにします。

C#
stopwatch.Reset();
stopwatch.Start();

DoSomething();

stopwatch.Stop();

または、より簡潔にRestart()を使います。

C#
stopwatch.Restart();

DoSomething();

stopwatch.Stop();

繰り返し計測する場合は、Restart()を使うとミスが少なくなります。

6-4. 初回実行だけ遅くなる現象に注意する

C#の処理時間を計測するとき、初回実行だけ遅くなることがあります。

これは、JITコンパイル、クラスの初期化、キャッシュ、ファイル読み込み、接続確立など、初回だけ発生する処理が含まれる場合があるためです。

たとえば、次のように1回だけ測ると、初回特有の遅さを含んだ結果になる可能性があります。

C#
var stopwatch = Stopwatch.StartNew();

TargetProcess();

stopwatch.Stop();

Console.WriteLine(stopwatch.ElapsedMilliseconds);

対策として、計測前に一度処理を実行してウォームアップする方法があります。

C#
// ウォームアップ
TargetProcess();

// 本計測
var stopwatch = Stopwatch.StartNew();

TargetProcess();

stopwatch.Stop();

Console.WriteLine($"処理時間: {stopwatch.ElapsedMilliseconds} ms");

処理性能を比較するときは、初回実行の結果だけで判断しないようにしましょう。

6-5. 短すぎる処理時間を1回だけ測定して判断する

非常に短い処理を1回だけ測定すると、誤差の影響が大きくなります。

C#
var stopwatch = Stopwatch.StartNew();

int x = 1 + 1;

stopwatch.Stop();

Console.WriteLine(stopwatch.ElapsedTicks);

このような短い処理では、計測そのもののコストや実行環境の影響が結果に大きく出ます。

対策として、同じ処理を何度も繰り返して計測します。

C#
var stopwatch = Stopwatch.StartNew();

for (int i = 0; i < 1_000_000; i++)
{
int x = i + 1;
}

stopwatch.Stop();

Console.WriteLine($"処理時間: {stopwatch.ElapsedMilliseconds} ms");

また、複数回計測して平均値を見ることも重要です。

C#
const int repeat = 10;
double total = 0;

for (int i = 0; i < repeat; i++)
{
var stopwatch = Stopwatch.StartNew();

TargetProcess();

stopwatch.Stop();

total += stopwatch.Elapsed.TotalMilliseconds;
}

Console.WriteLine($"平均: {total / repeat} ms");

短い処理ほど、1回だけの結果を過信しないようにしましょう。

6-6. ReleaseビルドとDebugビルドの違いを考慮する

C#で処理時間を計測するときは、DebugビルドとReleaseビルドの違いにも注意が必要です。

Debugビルドはデバッグしやすいように最適化が抑えられている場合があります。そのため、Releaseビルドより遅くなることがあります。

パフォーマンスを確認する場合は、基本的にReleaseビルドで測定しましょう。

C#
・開発中の動作確認       Debugビルド
・処理性能の確認 Releaseビルド
・本番に近い速度の確認 Releaseビルド

また、Visual Studioのデバッガーを接続した状態だと、実行時間に影響が出ることもあります。

厳密に性能を見たい場合は、Releaseビルドで実行し、必要に応じてデバッガーなしで測定するとよいでしょう。

7. より正確に処理時間を計測するコツ

7-1. 複数回実行して平均値を取る

処理時間は、実行するたびに少しずつ変わることがあります。CPUの状態、メモリ使用量、GC、他のプロセスの影響などによって結果が変動するためです。

そのため、より正確に傾向を見たい場合は、複数回実行して平均値を取ります。

C#
using System;
using System.Diagnostics;

class Program
{
static void Main()
{
const int count = 10;
double totalMilliseconds = 0;

for (int i = 0; i < count; i++)
{
Stopwatch stopwatch = Stopwatch.StartNew();

TargetProcess();

stopwatch.Stop();

totalMilliseconds += stopwatch.Elapsed.TotalMilliseconds;
}

double average = totalMilliseconds / count;

Console.WriteLine($"平均処理時間: {average:F3} ms");
}

static void TargetProcess()
{
for (int i = 0; i < 1_000_000; i++)
{
int value = i * i;
}
}
}

{average:F3}のように書くと、小数点以下3桁で表示できます。

1回の結果ではなく、平均値や最小値、最大値を見ることで、より現実的な判断ができます。

7-2. ウォームアップを行ってから計測する

初回実行には、JITコンパイルや初期化処理などが含まれることがあります。そのため、本計測の前にウォームアップを行うと、初回特有の影響を減らせます。

C#
// ウォームアップ
TargetProcess();

// 本計測
Stopwatch stopwatch = Stopwatch.StartNew();

TargetProcess();

stopwatch.Stop();

Console.WriteLine($"処理時間: {stopwatch.Elapsed.TotalMilliseconds:F3} ms");

複数回測定する場合も、最初の1回を除外する方法があります。

C#
// ウォームアップ
TargetProcess();

const int count = 10;
double total = 0;

for (int i = 0; i < count; i++)
{
var stopwatch = Stopwatch.StartNew();

TargetProcess();

stopwatch.Stop();

total += stopwatch.Elapsed.TotalMilliseconds;
}

Console.WriteLine($"平均: {total / count:F3} ms");

処理性能を比較するときは、できるだけ同じ条件で測定することが大切です。

7-3. 計測対象以外の処理を含めない

経過時間を測るときは、計測対象以外の処理を含めないようにしましょう。

たとえば、次のコードでは、処理時間にコンソール出力の時間も含まれています。

C#
var stopwatch = Stopwatch.StartNew();

Console.WriteLine("処理開始");
TargetProcess();
Console.WriteLine("処理終了");

stopwatch.Stop();

コンソール出力やログ出力、ファイル書き込みなどは、それ自体にも時間がかかります。純粋にTargetProcess()だけを測りたい場合は、次のようにします。

C#
var stopwatch = Stopwatch.StartNew();

TargetProcess();

stopwatch.Stop();

Console.WriteLine($"処理時間: {stopwatch.ElapsedMilliseconds} ms");

計測対象を明確にすることで、結果の意味がわかりやすくなります。

複数の処理を分けて計測したい場合は、それぞれ別のStopwatchで測るとよいでしょう。

C#
var sw1 = Stopwatch.StartNew();
LoadData();
sw1.Stop();

var sw2 = Stopwatch.StartNew();
ConvertData();
sw2.Stop();

var sw3 = Stopwatch.StartNew();
SaveData();
sw3.Stop();

Console.WriteLine($"読み込み: {sw1.ElapsedMilliseconds} ms");
Console.WriteLine($"変換: {sw2.ElapsedMilliseconds} ms");
Console.WriteLine($"保存: {sw3.ElapsedMilliseconds} ms");

どの処理が遅いのかを切り分けやすくなります。

7-4. 高精度な計測にElapsedTicksを使う

より細かく経過時間を見たい場合は、ElapsedTicksを使えます。

C#
var stopwatch = Stopwatch.StartNew();

TargetProcess();

stopwatch.Stop();

Console.WriteLine($"Ticks: {stopwatch.ElapsedTicks}");

ElapsedTicksは、Stopwatchが使用するタイマーの刻み数を返します。秒に変換したい場合は、Stopwatch.Frequencyを使います。

C#
double seconds = (double)stopwatch.ElapsedTicks / Stopwatch.Frequency;

Console.WriteLine($"秒: {seconds}");

ミリ秒に変換する場合は、次のようにします。

C#
double milliseconds = (double)stopwatch.ElapsedTicks / Stopwatch.Frequency * 1000;

Console.WriteLine($"ミリ秒: {milliseconds}");

通常はElapsedMillisecondsElapsed.TotalMillisecondsで十分ですが、より細かい精度で比較したい場合にはElapsedTicksが役立ちます。

7-5. ベンチマークにはBenchmarkDotNetも検討する

Stopwatchは手軽に経過時間を測れる便利な方法ですが、本格的なベンチマークには専用ライブラリの利用も検討できます。

C#では、BenchmarkDotNetというベンチマーク用ライブラリがよく使われます。

BenchmarkDotNetを使うと、ウォームアップ、繰り返し実行、統計情報の出力などを自動で行えます。単純にStopwatchで1回測るより、信頼性の高い測定がしやすくなります。

簡単なイメージは次のとおりです。

C#
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

public class MyBenchmark
{
[Benchmark]
public void TestMethod()
{
for (int i = 0; i < 1_000_000; i++)
{
int value = i * i;
}
}
}

public class Program
{
public static void Main(string[] args)
{
BenchmarkRunner.Run<MyBenchmark>();
}
}

日常的な処理時間確認にはStopwatch、厳密な性能比較にはBenchmarkDotNetという使い分けがおすすめです。

8. C#の経過時間計測に関するよくある質問

8-1. Stopwatchはミリ秒以下も計測できる?

はい、Stopwatchはミリ秒以下の時間も計測できます。

ElapsedMillisecondsは整数のミリ秒を返しますが、Elapsed.TotalMillisecondsを使うと小数を含むミリ秒を取得できます。

C#
var stopwatch = Stopwatch.StartNew();

TargetProcess();

stopwatch.Stop();

Console.WriteLine(stopwatch.ElapsedMilliseconds);
Console.WriteLine(stopwatch.Elapsed.TotalMilliseconds);

より細かい値を見たい場合は、ElapsedTicksも使えます。

C#
Console.WriteLine(stopwatch.ElapsedTicks);

ただし、非常に短い処理では測定誤差が大きくなりやすいため、複数回実行して平均を取ることが大切です。

8-2. Stopwatchはスレッドセーフ?

Stopwatchの同じインスタンスを複数スレッドから同時に操作する使い方は避けたほうがよいです。

たとえば、複数スレッドから同じStopwatchに対してStart()Stop()を呼ぶと、意図しない計測結果になる可能性があります。

C#
// 複数スレッドで同じインスタンスを操作するのは避ける
Stopwatch sharedStopwatch = new Stopwatch();

スレッドごとに処理時間を測りたい場合は、スレッドごとに別のStopwatchインスタンスを作成すると安全です。

C#
var stopwatch = Stopwatch.StartNew();

// スレッド内の処理

stopwatch.Stop();

共有が必要な場合は、ロックなどで制御することもできますが、基本的には計測対象ごとにインスタンスを分けるほうがシンプルです。

8-3. DateTime.NowとDateTime.UtcNowはどちらを使うべき?

日時を表示したり、ユーザーのローカル時刻として扱ったりする場合はDateTime.Nowを使います。

C#
DateTime localTime = DateTime.Now;

システム間で日時を扱ったり、タイムゾーンの影響を避けたい場合はDateTime.UtcNowを使うことがあります。

C#
DateTime utcTime = DateTime.UtcNow;

ただし、処理時間の計測であれば、DateTime.NowでもDateTime.UtcNowでもなく、Stopwatchを使うのがおすすめです。

C#
var stopwatch = Stopwatch.StartNew();

// 処理

stopwatch.Stop();

日時の記録にはDateTime、処理時間の計測にはStopwatchと覚えておくとよいでしょう。

8-4. 処理時間を小数秒で表示するには?

処理時間を小数秒で表示するには、Elapsed.TotalSecondsを使います。

C#
var stopwatch = Stopwatch.StartNew();

TargetProcess();

stopwatch.Stop();

Console.WriteLine($"処理時間: {stopwatch.Elapsed.TotalSeconds:F3} 秒");

F3を指定すると、小数点以下3桁で表示されます。

C#
処理時間: 1.234 

ミリ秒を小数付きで表示したい場合は、Elapsed.TotalMillisecondsを使います。

C#
Console.WriteLine($"処理時間: {stopwatch.Elapsed.TotalMilliseconds:F3} ms");

表示用にはTotalSecondsTotalMilliseconds、内部的な高精度計測にはElapsedTicksを使うとよいでしょう。

8-5. Unityや.NET FrameworkでもStopwatchは使える?

はい、StopwatchはUnityや.NET Frameworkでも利用できます。

StopwatchSystem.Diagnostics名前空間に含まれているため、次のように書いて使います。

C#
using System.Diagnostics;

Unityで使う場合も、基本的な書き方は同じです。

C#
using System.Diagnostics;
using UnityEngine;

public class Sample : MonoBehaviour
{
void Start()
{
Stopwatch stopwatch = Stopwatch.StartNew();

// 計測したい処理
DoSomething();

stopwatch.Stop();

UnityEngine.Debug.Log($"処理時間: {stopwatch.ElapsedMilliseconds} ms");
}

void DoSomething()
{
// 何らかの処理
}
}

UnityではDebugという名前がSystem.Diagnostics.DebugUnityEngine.Debugで衝突することがあるため、ログ出力ではUnityEngine.Debug.Logのように明示するとわかりやすいです。

.NET FrameworkのコンソールアプリやWindowsアプリでも、System.Diagnostics.Stopwatchを使えます。

8-6. 経過時間をフォーマットして表示するには?

Stopwatch.ElapsedTimeSpan型なので、TimeSpanの書式を使って表示できます。

C#
var stopwatch = Stopwatch.StartNew();

TargetProcess();

stopwatch.Stop();

TimeSpan elapsed = stopwatch.Elapsed;

Console.WriteLine($@"{elapsed:hh\:mm\:ss\.fff}");

出力例は次のようになります。

C#
00:00:01.234

分、秒、ミリ秒だけを表示したい場合は次のように書けます。

C#
Console.WriteLine($@"{elapsed:mm\:ss\.fff}");

また、文章として表示したい場合は、TotalSecondsTotalMillisecondsを使うと簡単です。

C#
Console.WriteLine($"経過時間: {elapsed.TotalSeconds:F2} 秒");
Console.WriteLine($"経過時間: {elapsed.TotalMilliseconds:F0} ms");

用途に応じて、見やすい形式に整えるとログや画面表示で扱いやすくなります。

まとめ

C#で経過時間を計測する方法としては、主にStopwatchを使う方法とDateTimeを使う方法があります。

処理時間やパフォーマンスを測る場合は、基本的にStopwatchを使うのがおすすめです。Stopwatchは経過時間の計測に特化しており、Start()Stop()Reset()Restart()を使って簡単に処理時間を測定できます。

C#
var stopwatch = Stopwatch.StartNew();

// 計測したい処理

stopwatch.Stop();

Console.WriteLine($"経過時間: {stopwatch.ElapsedMilliseconds} ms");

一方、DateTimeは現在日時や日時同士の差分を扱うための型です。登録日からの経過日数、開始日時と終了日時の差、ログに記録された時刻の差分などを求める場合に向いています。

使い分けのポイントは次のとおりです。

C#
処理時間を測りたい        Stopwatch
日時の差分を計算したい DateTime

また、より正確に処理時間を測るには、複数回実行して平均を取る、ウォームアップを行う、計測対象以外の処理を含めない、Releaseビルドで測定する、といった工夫も重要です。

C#で経過時間を計測するときは、まずStopwatchを基本として使い、日時の差分計算が必要な場合にDateTimeTimeSpanを使うようにすると、目的に合ったわかりやすいコードを書けます。