C# TimeSpan:期間を表す便利な構造体とその活用法
C# における TimeSpan
構造体は、時間の経過、つまり期間を表すために設計された、非常に強力で便利なツールです。単純な時間差の計算から、複雑な時間に基づくスケジューリング、経過時間のトラッキングまで、幅広い用途で活躍します。本記事では、TimeSpan
の基本的な概念から、その活用法、パフォーマンス上の考慮事項まで、網羅的に解説します。
1. TimeSpan
の基本概念
TimeSpan
は、ある時点から別の時点までの時間の長さを表します。これは特定の日時を表す DateTime
や DateTimeOffset
とは異なり、あくまで時間の「長さ」に焦点を当てています。TimeSpan
の精度はティック単位(1ティックは100ナノ秒)です。
1.1. TimeSpan
の作成方法
TimeSpan
オブジェクトを作成する方法はいくつかあります。
-
コンストラクタ:
最も基本的な方法は、コンストラクタを使用する方法です。
TimeSpan
クラスには、様々な引数を受け取るオーバーロードされたコンストラクタが多数用意されています。TimeSpan(long ticks)
: ティック数を直接指定してTimeSpan
を作成します。TimeSpan(int hours, int minutes, int seconds)
: 時間、分、秒を指定してTimeSpan
を作成します。TimeSpan(int days, int hours, int minutes, int seconds)
: 日、時間、分、秒を指定してTimeSpan
を作成します。TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds)
: 日、時間、分、秒、ミリ秒を指定してTimeSpan
を作成します。TimeSpan(TimeSpan value)
: 既存のTimeSpan
オブジェクトから新しいTimeSpan
を作成します (コピー)。
“`csharp
// ティック数から作成
TimeSpan ts1 = new TimeSpan(10000000); // 1秒// 時間、分、秒から作成
TimeSpan ts2 = new TimeSpan(1, 30, 0); // 1時間30分// 日、時間、分、秒から作成
TimeSpan ts3 = new TimeSpan(2, 12, 0, 0); // 2日と12時間// 日、時間、分、秒、ミリ秒から作成
TimeSpan ts4 = new TimeSpan(0, 0, 0, 5, 500); // 5秒と500ミリ秒// 既存の TimeSpan から作成
TimeSpan ts5 = new TimeSpan(ts2); // ts2 と同じ値の新しい TimeSpan
“` -
静的メソッド:
TimeSpan
クラスは、TimeSpan
オブジェクトをより簡潔に作成するための便利な静的メソッドを提供しています。TimeSpan.FromDays(double value)
: 日数からTimeSpan
を作成します。TimeSpan.FromHours(double value)
: 時間数からTimeSpan
を作成します。TimeSpan.FromMinutes(double value)
: 分数からTimeSpan
を作成します。TimeSpan.FromSeconds(double value)
: 秒数からTimeSpan
を作成します。TimeSpan.FromMilliseconds(double value)
: ミリ秒数からTimeSpan
を作成します。TimeSpan.FromTicks(long value)
: ティック数からTimeSpan
を作成します。
“`csharp
// 日数から作成
TimeSpan ts6 = TimeSpan.FromDays(1.5); // 1日と12時間// 時間数から作成
TimeSpan ts7 = TimeSpan.FromHours(24); // 1日// 分数から作成
TimeSpan ts8 = TimeSpan.FromMinutes(90); // 1時間30分// 秒数から作成
TimeSpan ts9 = TimeSpan.FromSeconds(60); // 1分// ミリ秒数から作成
TimeSpan ts10 = TimeSpan.FromMilliseconds(500); // 0.5秒// ティック数から作成
TimeSpan ts11 = TimeSpan.FromTicks(600000000); // 1分
“`
1.2. TimeSpan
のプロパティ
TimeSpan
オブジェクトは、期間に関する様々な情報を取得するための便利なプロパティを提供しています。
Days
: 日数 (整数)。Hours
: 時間数 (0 ~ 23 の整数)。Minutes
: 分数 (0 ~ 59 の整数)。Seconds
: 秒数 (0 ~ 59 の整数)。Milliseconds
: ミリ秒数 (0 ~ 999 の整数)。Ticks
: ティック数 (long 型)。TotalDays
: 合計日数 (double 型、小数値を含む)。TotalHours
: 合計時間数 (double 型、小数値を含む)。TotalMinutes
: 合計分数 (double 型、小数値を含む)。TotalSeconds
: 合計秒数 (double 型、小数値を含む)。TotalMilliseconds
: 合計ミリ秒数 (double 型、小数値を含む)。
“`csharp
TimeSpan ts = new TimeSpan(1, 12, 30, 45, 500);
Console.WriteLine($”Days: {ts.Days}”); // 出力: Days: 1
Console.WriteLine($”Hours: {ts.Hours}”); // 出力: Hours: 12
Console.WriteLine($”Minutes: {ts.Minutes}”); // 出力: Minutes: 30
Console.WriteLine($”Seconds: {ts.Seconds}”); // 出力: Seconds: 45
Console.WriteLine($”Milliseconds: {ts.Milliseconds}”); // 出力: Milliseconds: 500
Console.WriteLine($”Ticks: {ts.Ticks}”); // 出力: Ticks: 131445500000
Console.WriteLine($”TotalDays: {ts.TotalDays}”); // 出力: TotalDays: 1.5213601851851852
Console.WriteLine($”TotalHours: {ts.TotalHours}”); // 出力: TotalHours: 36.51264444444444
Console.WriteLine($”TotalMinutes: {ts.TotalMinutes}”); // 出力: TotalMinutes: 2190.7586666666665
Console.WriteLine($”TotalSeconds: {ts.TotalSeconds}”); // 出力: TotalSeconds: 131445.52
Console.WriteLine($”TotalMilliseconds: {ts.TotalMilliseconds}”); // 出力: TotalMilliseconds: 131445520
“`
1.3. TimeSpan
の演算
TimeSpan
は、加算、減算、比較などの様々な演算をサポートしています。これにより、時間の計算を簡単に行うことができます。
- 加算 (
+
):TimeSpan
同士を加算して、合計の期間を表す新しいTimeSpan
を作成します。 - 減算 (
-
):TimeSpan
同士を減算して、期間の差を表す新しいTimeSpan
を作成します。 - 乗算 (
*
):TimeSpan
に数値を乗算して、期間をスケーリングした新しいTimeSpan
を作成します。 - 除算 (
/
):TimeSpan
を数値で除算して、期間をスケーリングした新しいTimeSpan
を作成します。 - 単項マイナス (
-
):TimeSpan
の符号を反転します (正の期間を負の期間に、負の期間を正の期間に変換します)。 - 等価 (
==
):TimeSpan
同士が等しいかどうかを比較します。 - 不等価 (
!=
):TimeSpan
同士が等しくないかどうかを比較します。 - より小さい (
<
):TimeSpan
が別のTimeSpan
より小さいかどうかを比較します。 - より大きい (
>
):TimeSpan
が別のTimeSpan
より大きいかどうかを比較します。 - 以下 (
<=
):TimeSpan
が別のTimeSpan
以下かどうかを比較します。 - 以上 (
>=
):TimeSpan
が別のTimeSpan
以上かどうかを比較します。
“`csharp
TimeSpan ts1 = new TimeSpan(1, 0, 0); // 1時間
TimeSpan ts2 = new TimeSpan(0, 30, 0); // 30分
// 加算
TimeSpan ts3 = ts1 + ts2; // 1時間30分
// 減算
TimeSpan ts4 = ts1 – ts2; // 30分
// 乗算
TimeSpan ts5 = ts1 * 2; // 2時間
// 除算
TimeSpan ts6 = ts1 / 2; // 30分
// 単項マイナス
TimeSpan ts7 = -ts1; // -1時間
// 比較
bool isEqual = ts1 == new TimeSpan(1, 0, 0); // true
bool isLessThan = ts2 < ts1; // true
“`
1.4. TimeSpan
のメソッド
TimeSpan
クラスは、TimeSpan
オブジェクトを操作するための様々なメソッドを提供しています。
Add(TimeSpan ts)
: 現在のTimeSpan
に別のTimeSpan
を加算した新しいTimeSpan
を返します。Subtract(TimeSpan ts)
: 現在のTimeSpan
から別のTimeSpan
を減算した新しいTimeSpan
を返します。Multiply(double factor)
: 現在のTimeSpan
に数値を乗算した新しいTimeSpan
を返します。Divide(double divisor)
: 現在のTimeSpan
を数値で除算した新しいTimeSpan
を返します。Negate()
: 現在のTimeSpan
の符号を反転した新しいTimeSpan
を返します。CompareTo(TimeSpan value)
: 現在のTimeSpan
と別のTimeSpan
を比較します。Equals(object obj)
: 現在のTimeSpan
が別のオブジェクトと等しいかどうかを判定します。Equals(TimeSpan obj)
: 現在のTimeSpan
が別のTimeSpan
と等しいかどうかを判定します。GetHashCode()
: 現在のTimeSpan
のハッシュコードを返します。ToString()
: 現在のTimeSpan
を文字列として表現します。Duration()
: 常に正の値となるように、絶対値で新しいTimeSpan
インスタンスを返します。FromXmlString(string s)
: XML 文字列形式からTimeSpan
を作成します。ToXmlString()
:TimeSpan
を XML 文字列形式で表します。
“`csharp
TimeSpan ts = new TimeSpan(1, 30, 0);
// 加算
TimeSpan ts2 = ts.Add(new TimeSpan(0, 15, 0)); // 1時間45分
// 減算
TimeSpan ts3 = ts.Subtract(new TimeSpan(0, 15, 0)); // 1時間15分
// 乗算
TimeSpan ts4 = ts.Multiply(2); // 3時間
// 除算
TimeSpan ts5 = ts.Divide(2); // 45分
// 文字列化
string tsString = ts.ToString(); // “01:30:00”
// 絶対値
TimeSpan negativeTs = new TimeSpan(-1, 0, 0);
TimeSpan absoluteTs = negativeTs.Duration(); // 1時間
// XML 形式
string xmlString = ts.ToXmlString(); // “
TimeSpan tsFromXml = TimeSpan.FromXmlString(xmlString);
“`
2. TimeSpan
の活用法
TimeSpan
は、様々なシナリオで活用できる汎用性の高い構造体です。以下に、その具体的な活用例をいくつか紹介します。
2.1. 時間差の計算
DateTime
オブジェクト間の差を計算することで、TimeSpan
を取得できます。これは、イベントの継続時間、タスクの完了時間、または経過時間を測定するのに役立ちます。
“`csharp
DateTime startTime = DateTime.Now;
// 何らかの処理を実行
Thread.Sleep(5000); // 5秒間スリープ
DateTime endTime = DateTime.Now;
TimeSpan elapsedTime = endTime – startTime;
Console.WriteLine($”処理時間: {elapsedTime.TotalSeconds} 秒”);
“`
2.2. スケジューリング
TimeSpan
を使用して、定期的なタスクやイベントをスケジュールできます。例えば、特定の間隔で実行されるバックグラウンドタスクや、特定の期間後に期限切れになる通知などを実装できます。
“`csharp
// 5分間隔でタスクを実行する
Timer timer = new Timer(callback =>
{
// タスクの処理
Console.WriteLine(“タスクを実行しました。”);
}, null, TimeSpan.Zero, TimeSpan.FromMinutes(5));
// 期限切れになる通知を設定する
DateTime notificationTime = DateTime.Now + TimeSpan.FromHours(2);
Console.WriteLine($”通知は {notificationTime} に送信されます。”);
“`
2.3. タイマーとストップウォッチ
TimeSpan
は、タイマーやストップウォッチの実装に不可欠です。Stopwatch
クラスは、経過時間を TimeSpan
オブジェクトとして提供します。
“`csharp
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
// 何らかの処理を実行
Thread.Sleep(3000); // 3秒間スリープ
stopwatch.Stop();
TimeSpan elapsedTime = stopwatch.Elapsed;
Console.WriteLine($”経過時間: {elapsedTime.TotalSeconds} 秒”);
“`
2.4. 遅延とタイムアウト
TimeSpan
を使用して、処理の遅延やタイムアウトを設定できます。例えば、ネットワークリクエストが一定時間内に完了しない場合に、タイムアウト例外を発生させることができます。
“`csharp
// 5秒のタイムアウトを設定してネットワークリクエストを実行する
Task
{
// ネットワークリクエストの処理
Thread.Sleep(6000); // 6秒間スリープ (タイムアウト超過)
return “リクエスト成功”;
});
try
{
task.Wait(TimeSpan.FromSeconds(5));
if (task.IsCompleted)
{
Console.WriteLine(task.Result);
}
else
{
Console.WriteLine("タイムアウトしました。");
}
}
catch (AggregateException ex)
{
Console.WriteLine($”エラー: {ex.InnerException.Message}”);
}
“`
2.5. 期間のフォーマット
TimeSpan
オブジェクトを様々な形式で文字列として表現できます。標準の書式指定文字列やカスタム書式指定文字列を使用することで、日付と時間を特定のレイアウトで表示できます。
“`csharp
TimeSpan ts = new TimeSpan(1, 12, 30, 45);
// 標準の書式指定文字列
Console.WriteLine(ts.ToString(“c”)); // “1.12:30:45” (既定の書式)
Console.WriteLine(ts.ToString(“g”)); // “1.12:30:45” (汎用的な短い形式)
Console.WriteLine(ts.ToString(“G”)); // “1.12:30:45.0000000” (汎用的な長い形式)
Console.WriteLine(ts.ToString(“t”)); // “12:30” (短い時間形式)
Console.WriteLine(ts.ToString(“T”)); // “12:30:45” (長い時間形式)
// カスタム書式指定文字列
Console.WriteLine(ts.ToString(@”dd.hh\:mm\:ss”)); // “01.12:30:45″
Console.WriteLine(ts.ToString(@”hh\:mm\:ss.fff”)); // “12:30:45.000”
“`
2.6. 期間のパース
文字列から TimeSpan
オブジェクトをパースすることも可能です。TimeSpan.Parse()
メソッドまたは TimeSpan.TryParse()
メソッドを使用できます。
“`csharp
string timeSpanString = “01:30:00”;
// Parse メソッド (例外が発生する可能性あり)
try
{
TimeSpan ts1 = TimeSpan.Parse(timeSpanString);
Console.WriteLine($”Parse 成功: {ts1}”);
}
catch (FormatException ex)
{
Console.WriteLine($”Parse エラー: {ex.Message}”);
}
// TryParse メソッド (例外が発生しない)
if (TimeSpan.TryParse(timeSpanString, out TimeSpan ts2))
{
Console.WriteLine($”TryParse 成功: {ts2}”);
}
else
{
Console.WriteLine(“TryParse 失敗”);
}
string invalidTimeSpanString = “invalid format”;
if (TimeSpan.TryParse(invalidTimeSpanString, out TimeSpan ts3))
{
Console.WriteLine(“TryParse 成功”); // ここには到達しない
}
else
{
Console.WriteLine(“TryParse 失敗”); // 出力: TryParse 失敗
}
“`
3. パフォーマンス上の考慮事項
TimeSpan
は構造体 (値型) であるため、クラス (参照型) と比較して、一般的にパフォーマンス上のオーバーヘッドは少なくなります。ただし、TimeSpan
オブジェクトを頻繁に作成および破棄する場合や、大規模なコレクションで使用する場合は、パフォーマンスに影響を与える可能性があります。
3.1. 値型と参照型
値型 (構造体) は、メモリのスタック領域に直接格納されます。一方、参照型 (クラス) は、メモリのヒープ領域に格納され、スタックにはそのオブジェクトへの参照が格納されます。値型は、参照型と比較して、メモリ割り当てとガベージコレクションのオーバーヘッドが少なくなります。
3.2. TimeSpan
の再利用
TimeSpan
オブジェクトを頻繁に作成および破棄する代わりに、再利用することを検討してください。特に、ループ内や頻繁に呼び出されるメソッド内で TimeSpan
を使用する場合は、パフォーマンスが向上する可能性があります。
“`csharp
// 非効率な例 (ループ内で毎回 TimeSpan を作成)
for (int i = 0; i < 1000; i++)
{
TimeSpan delay = TimeSpan.FromMilliseconds(100);
Thread.Sleep(delay);
}
// 効率的な例 (TimeSpan を再利用)
TimeSpan delay = TimeSpan.FromMilliseconds(100);
for (int i = 0; i < 1000; i++)
{
Thread.Sleep(delay);
}
“`
3.3. 不変性
TimeSpan
は不変な型です。つまり、TimeSpan
オブジェクトを作成した後は、その値を変更することはできません。加算、減算などの演算を行うと、元の TimeSpan
オブジェクトは変更されず、新しい TimeSpan
オブジェクトが作成されます。
したがって、TimeSpan
オブジェクトを頻繁に変更する場合は、パフォーマンスに注意する必要があります。どうしても変更が必要な場合は、DateTime
オブジェクトを使用して、日付と時間を直接操作することを検討してください。
3.4. 構造体のコピー
構造体は値型であるため、代入やメソッドの引数として渡す際にコピーされます。TimeSpan
オブジェクトは比較的小さいですが、大規模な構造体のコピーはパフォーマンスに影響を与える可能性があります。
TimeSpan
オブジェクトを頻繁にコピーする場合は、パフォーマンスを向上させるために、in
キーワードを使用して、読み取り専用参照として渡すことを検討してください (C# 7.2 以降)。
csharp
// in キーワードの使用例
void ProcessTimeSpan(in TimeSpan ts)
{
// ts は読み取り専用参照として渡されるため、コピーは発生しない
Console.WriteLine(ts.TotalSeconds);
}
4. TimeSpan
と DateTime
/ DateTimeOffset
の使い分け
TimeSpan
、DateTime
、DateTimeOffset
はそれぞれ異なる目的で使用されます。それぞれの特性を理解することで、適切な型を選択できます。
-
TimeSpan
: 時間の経過、つまり期間を表すために使用します。特定の日時やタイムゾーンの情報は含まれません。 -
DateTime
: 特定の日時を表すために使用します。タイムゾーンの情報は含まれません (ローカルまたは UTC)。 -
DateTimeOffset
: 特定の日時と、UTC からのオフセットを表すために使用します。タイムゾーンの情報を保持できます。
以下は、それぞれの型の使い分けのガイドラインです。
- 時間の差を計算する場合:
TimeSpan
を使用します。 - 特定の日時を表す必要がある場合:
DateTime
またはDateTimeOffset
を使用します。 - タイムゾーンを考慮する必要がある場合:
DateTimeOffset
を使用します。 - 日付と時刻の計算を行う場合:
DateTime
またはDateTimeOffset
を使用します。 - 期間をフォーマットする必要がある場合:
TimeSpan
を使用します。
5. まとめ
TimeSpan
は、C# で期間を表すための非常に強力で柔軟な構造体です。時間差の計算、スケジューリング、タイマー、遅延、タイムアウトなど、幅広い用途で活用できます。TimeSpan
の基本的な概念、演算、メソッド、およびパフォーマンス上の考慮事項を理解することで、より効率的かつ効果的なコードを作成できます。DateTime
や DateTimeOffset
との適切な使い分けも重要です。本記事が、TimeSpan
の理解を深め、より効果的に活用するための一助となれば幸いです。