C#で時間を扱う方法を徹底解説|DateTime・TimeSpan・現在時刻・計算・比較までわかる入門ガイド
はじめに
C#で時間を扱う処理は、アプリ開発でほぼ必ず登場します。たとえば、現在時刻を表示する、締切を判定する、作業時間を計算する、ログに日時を残す、APIにUTC時刻を送る、タイマーで一定間隔の処理を実行する、といった場面です。
ただし、C#の時間処理では「日時」「時刻」「経過時間」「タイムゾーン」を混同すると、思わぬバグにつながります。特に、DateTime.Nowをそのまま保存する、文字列のまま日時を比較する、TimeSpan.HoursとTimeSpan.TotalHoursを取り違える、といったミスは初心者だけでなく実務でもよく起こります。
この記事では、C#で時間を扱うための基本として、DateTime、TimeSpan、DateTimeOffset、DateOnly、TimeOnlyの使い分けから、現在時刻の取得、時間計算、比較、文字列変換、UTC・タイムゾーン、経過時間の測定、待機処理、実践サンプルまで順番に解説します。
1. C#で「時間」を扱う前に押さえる基本
1-1. C#で扱う「日時」と「時間間隔」の違い
C#で時間を扱うときは、まず「ある一点の日時」と「長さとしての時間」を分けて考えることが重要です。
「2026年6月7日 10時30分」のように、カレンダー上の特定の瞬間を表す場合はDateTimeやDateTimeOffsetを使います。一方、「3時間」「15分」「2日と4時間」のような時間の長さを表す場合はTimeSpanを使います。
C#DateTime startAt = new DateTime(2026, 6, 7, 10, 30, 0); // 日時
TimeSpan duration = TimeSpan.FromHours(3); // 時間間隔
DateTime endAt = startAt + duration;
Console.WriteLine(endAt); // 2026/06/07 13:30:00
DateTimeは日付と時刻を表す構造体で、TimeSpanは時間間隔を表す構造体です。Microsoftの公式ドキュメントでも、DateTimeは日付と時刻、TimeSpanは時間間隔を扱う型として説明されています。
1-2. DateTime・TimeSpan・DateTimeOffset・DateOnly・TimeOnlyの使い分け
C#で時間を扱う主な型は、次のように使い分けます。
| 型 | 主な用途 | 例 |
|---|---|---|
DateTime | 日付と時刻を扱う | 2026/06/07 10:30:00 |
TimeSpan | 時間の長さ・差分を扱う | 2時間30分 |
DateTimeOffset | UTCとの差分付きで日時を扱う | 2026/06/07 10:30:00 +09:00 |
DateOnly | 日付だけを扱う | 2026/06/07 |
TimeOnly | 時刻だけを扱う | 10:30:00 |
たとえば、ユーザーの誕生日には時刻が不要なのでDateOnlyが適しています。毎日9時に通知するような処理では、日付ではなく時刻だけが必要なのでTimeOnlyが向いています。TimeOnlyは「毎日の昼食時間」「目覚ましの時刻」のような、日付を持たない時刻を表す型です。
C#DateOnly birthday = new DateOnly(1995, 4, 10);
TimeOnly alarmTime = new TimeOnly(7, 30);
Console.WriteLine(birthday); // 1995/04/10
Console.WriteLine(alarmTime); // 7:30
海外ユーザーやAPI、ログ、データベース保存など、UTCとの関係を明確にしたい場合はDateTimeOffsetが便利です。DateTimeOffsetは日時に加えてUTCとの差分であるオフセットを持ち、UTCに対する日時をより明確に表せます。
1-3. 初心者が混乱しやすい「日付」「時刻」「経過時間」の考え方
初心者が混乱しやすいポイントは、「時刻」と「時間の長さ」を同じものとして扱ってしまうことです。
たとえば「9時」は1日の中の時刻です。一方で「9時間」は経過時間です。この2つは似ていますが、プログラム上では意味がまったく違います。
C#TimeOnly meetingTime = new TimeOnly(9, 0); // 9時
TimeSpan workDuration = TimeSpan.FromHours(9); // 9時間
「2026年6月7日 9時」はDateTime、「9時」はTimeOnly、「9時間」はTimeSpanと考えると整理しやすくなります。
1-4. この記事で作れるようになる時間処理の全体像
この記事を読み終えると、次のようなC#の時間処理を書けるようになります。
C#DateTime now = DateTime.Now;
DateTime deadline = new DateTime(2026, 6, 10, 18, 0, 0);
if (now > deadline)
{
Console.WriteLine("期限切れです");
}
else
{
TimeSpan remaining = deadline - now;
Console.WriteLine($"残り {remaining.TotalHours:F1} 時間です");
}
このように、現在時刻を取得し、指定日時と比較し、差分をTimeSpanとして求める処理は、C#の時間処理の基本パターンです。
2. DateTimeで日付と時刻を扱う基本
2-1. DateTimeとは何か
DateTimeは、C#で日付と時刻を扱う代表的な型です。年、月、日、時、分、秒、ミリ秒などを持ち、「いつ」を表します。
C#DateTime dateTime = new DateTime(2026, 6, 7, 10, 30, 0);
Console.WriteLine(dateTime); // 2026/06/07 10:30:00
DateTimeは、予約日時、作成日時、更新日時、締切日時、ログ出力日時など、日付と時刻をまとめて扱いたい場面で使います。
2-2. DateTimeを作成する基本構文
DateTimeはコンストラクターで作成できます。
C#DateTime date1 = new DateTime(2026, 6, 7);
DateTime date2 = new DateTime(2026, 6, 7, 10, 30, 0);
DateTime date3 = new DateTime(2026, 6, 7, 10, 30, 0, DateTimeKind.Local);
引数を3つ指定すると、年・月・日のみを持つDateTimeになります。この場合、時刻は00:00:00です。
C#DateTime date = new DateTime(2026, 6, 7);
Console.WriteLine(date); // 2026/06/07 0:00:00
年月日と時分秒を指定すると、特定の日時を作成できます。
C#DateTime meetingAt = new DateTime(2026, 6, 7, 14, 0, 0);
Console.WriteLine(meetingAt); // 2026/06/07 14:00:00
2-3. 年・月・日・時・分・秒を取得する方法
DateTimeから年、月、日、時、分、秒を取得するには、各プロパティを使います。
C#DateTime now = new DateTime(2026, 6, 7, 10, 30, 45);
Console.WriteLine(now.Year); // 2026
Console.WriteLine(now.Month); // 6
Console.WriteLine(now.Day); // 7
Console.WriteLine(now.Hour); // 10
Console.WriteLine(now.Minute); // 30
Console.WriteLine(now.Second); // 45
曜日を取得したい場合はDayOfWeekを使います。
C#DateTime date = new DateTime(2026, 6, 7);
Console.WriteLine(date.DayOfWeek); // Sunday
2-4. DateプロパティとTimeOfDayプロパティの使い方
DateTime.Dateは、日付部分だけを取り出します。ただし戻り値はDateTimeで、時刻は00:00:00になります。
C#DateTime now = new DateTime(2026, 6, 7, 10, 30, 45);
DateTime dateOnly = now.Date;
Console.WriteLine(dateOnly); // 2026/06/07 0:00:00
TimeOfDayは、時刻部分をTimeSpanとして取得します。
C#DateTime now = new DateTime(2026, 6, 7, 10, 30, 45);
TimeSpan time = now.TimeOfDay;
Console.WriteLine(time); // 10:30:45
日付だけを比較したい場合はDate、時刻部分の長さを扱いたい場合はTimeOfDayが便利です。
2-5. DateTimeKindでローカル時刻・UTC時刻を区別する
DateTimeにはKindという情報があります。これは、その日時がローカル時刻なのか、UTCなのか、未指定なのかを表します。
C#DateTime local = DateTime.Now;
DateTime utc = DateTime.UtcNow;
DateTime unspecified = new DateTime(2026, 6, 7, 10, 30, 0);
Console.WriteLine(local.Kind); // Local
Console.WriteLine(utc.Kind); // Utc
Console.WriteLine(unspecified.Kind); // Unspecified
DateTimeKindには、主に次の3つがあります。
| 値 | 意味 |
|---|---|
Local | ローカル時刻 |
Utc | UTC時刻 |
Unspecified | ローカルかUTCか未指定 |
DateTimeKindは、ToLocalTimeやToUniversalTimeで変換するときに重要です。タイムゾーンをまたぐ処理やAPI連携では、KindがあいまいなDateTimeをそのまま扱うと誤変換の原因になります。
3. C#で現在時刻を取得する方法
3-1. DateTime.Nowで現在のローカル日時を取得する
現在のローカル日時を取得するには、DateTime.Nowを使います。
C#DateTime now = DateTime.Now;
Console.WriteLine(now);
DateTime.Nowは、実行環境のローカルタイムゾーンに基づく現在日時を返します。画面表示、ユーザー向けメッセージ、ローカルPC上の簡単な処理では使いやすい方法です。
C#Console.WriteLine($"現在時刻: {DateTime.Now}");
ただし、サーバー側で保存する日時や、複数タイムゾーンのユーザーが関係するアプリでは、DateTime.Nowをそのまま保存すると混乱しやすくなります。
3-2. DateTime.Todayで今日の日付だけを取得する
今日の日付を取得したい場合は、DateTime.Todayを使います。
C#DateTime today = DateTime.Today;
Console.WriteLine(today); // 今日の日付 0:00:00
DateTime.Todayは、現在日付の00:00:00を表すDateTimeです。
C#DateTime today = DateTime.Today;
DateTime tomorrow = today.AddDays(1);
Console.WriteLine(today);
Console.WriteLine(tomorrow);
日付単位の判定では、DateTime.Now.DateよりもDateTime.Todayのほうが意図が明確です。
3-3. DateTime.UtcNowでUTC時刻を取得する
UTCの現在時刻を取得するには、DateTime.UtcNowを使います。
C#DateTime utcNow = DateTime.UtcNow;
Console.WriteLine(utcNow);
Console.WriteLine(utcNow.Kind); // Utc
UTCは協定世界時で、タイムゾーンに依存しない基準時刻として扱えます。API、ログ、データベース保存、分散システムでは、UTCで保存し、表示時にユーザーのローカル時刻へ変換する設計がよく使われます。
3-4. 現在時刻の取得でNowとUtcNowを使い分ける基準
DateTime.NowとDateTime.UtcNowは、用途で使い分けます。
| 目的 | 推奨 |
|---|---|
| 画面に現在時刻を表示する | DateTime.Now |
| ローカルPCだけで完結する処理 | DateTime.Now |
| DBに保存する作成日時・更新日時 | DateTime.UtcNow |
| APIに送る日時 | DateTime.UtcNow |
| 複数地域のユーザーが使うサービス | DateTime.UtcNowまたはDateTimeOffset |
実務では、「保存はUTC、表示はローカル」という方針にすると、タイムゾーンによるズレを減らしやすくなります。
3-5. 現在時刻を文字列として表示するサンプルコード
現在時刻を任意の形式で表示するには、ToStringにフォーマットを指定します。
C#DateTime now = DateTime.Now;
Console.WriteLine(now.ToString("yyyy/MM/dd HH:mm:ss"));
Console.WriteLine(now.ToString("yyyy年MM月dd日 HH時mm分ss秒"));
Console.WriteLine(now.ToString("HH:mm"));
ログ用なら、年月日と時分秒を固定桁で出力すると扱いやすくなります。
C#string logTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
Console.WriteLine($"[{logTime}] 処理を開始しました");
4. TimeSpanで時間間隔を扱う方法
4-1. TimeSpanとは何か
TimeSpanは、時間の長さや日時の差分を表す型です。たとえば「30分」「2時間」「3日と5時間」などを扱います。
C#TimeSpan duration = TimeSpan.FromMinutes(90);
Console.WriteLine(duration); // 01:30:00
TimeSpanは、待ち時間、処理時間、作業時間、残り時間、日時同士の差分などに使います。TimeSpanは時間間隔を表す構造体で、加算、減算、比較などの演算もサポートされています。
4-2. TimeSpanを作成する基本構文
TimeSpanはコンストラクターまたはFromDays、FromHours、FromMinutesなどのメソッドで作成できます。
C#TimeSpan time1 = new TimeSpan(1, 30, 0); // 1時間30分
TimeSpan time2 = TimeSpan.FromHours(1.5);
TimeSpan time3 = TimeSpan.FromMinutes(90);
Console.WriteLine(time1);
Console.WriteLine(time2);
Console.WriteLine(time3);
可読性を重視するなら、TimeSpan.FromHoursやTimeSpan.FromMinutesを使うと意図が伝わりやすくなります。
C#TimeSpan retryInterval = TimeSpan.FromSeconds(5);
TimeSpan timeout = TimeSpan.FromMinutes(3);
4-3. 日・時・分・秒・ミリ秒を取得する方法
TimeSpanから日、時間、分、秒、ミリ秒を取得できます。
C#TimeSpan span = new TimeSpan(2, 3, 4, 5, 600);
Console.WriteLine(span.Days); // 2
Console.WriteLine(span.Hours); // 3
Console.WriteLine(span.Minutes); // 4
Console.WriteLine(span.Seconds); // 5
Console.WriteLine(span.Milliseconds); // 600
ここで注意したいのは、HoursやMinutesは「全体の時間数」ではなく、日数などを除いた残り部分を返すことです。
4-4. DaysとTotalDays、HoursとTotalHoursの違い
TimeSpanで特に間違いやすいのが、DaysとTotalDays、HoursとTotalHoursの違いです。
C#TimeSpan span = TimeSpan.FromHours(27);
Console.WriteLine(span.Days); // 1
Console.WriteLine(span.Hours); // 3
Console.WriteLine(span.TotalDays); // 1.125
Console.WriteLine(span.TotalHours); // 27
Daysは「日部分」、Hoursは「時間部分」です。一方、TotalDaysやTotalHoursは、時間間隔全体を日数や時間数に換算した値です。
作業時間を「合計何時間か」で出したい場合は、HoursではなくTotalHoursを使います。
C#TimeSpan workTime = TimeSpan.FromHours(27);
Console.WriteLine($"合計作業時間: {workTime.TotalHours}時間");
4-5. 経過時間や待ち時間をTimeSpanで表すサンプルコード
処理の開始日時と終了日時の差分を求めると、結果はTimeSpanになります。
C#DateTime startedAt = new DateTime(2026, 6, 7, 9, 0, 0);
DateTime endedAt = new DateTime(2026, 6, 7, 17, 30, 0);
TimeSpan workTime = endedAt - startedAt;
Console.WriteLine(workTime); // 08:30:00
Console.WriteLine(workTime.TotalHours); // 8.5
待ち時間やタイムアウト時間もTimeSpanで表すと読みやすくなります。
C#TimeSpan waitTime = TimeSpan.FromSeconds(10);
TimeSpan timeout = TimeSpan.FromMinutes(1);
Console.WriteLine($"待機時間: {waitTime.TotalSeconds}秒");
Console.WriteLine($"タイムアウト: {timeout.TotalSeconds}秒");
5. C#で時間を計算する方法
5-1. DateTimeに日数や時間を足す方法
DateTimeに日数や時間を足すには、AddDays、AddHours、AddMinutes、AddSecondsなどを使います。
C#DateTime now = new DateTime(2026, 6, 7, 10, 0, 0);
DateTime after3Days = now.AddDays(3);
DateTime after2Hours = now.AddHours(2);
DateTime after30Minutes = now.AddMinutes(30);
Console.WriteLine(after3Days);
Console.WriteLine(after2Hours);
Console.WriteLine(after30Minutes);
DateTimeは不変の値型なので、AddDaysなどを呼び出しても元の値は変わりません。戻り値を受け取る必要があります。
C#DateTime date = new DateTime(2026, 6, 7);
date.AddDays(1); // 戻り値を使っていないため、dateは変わらない
Console.WriteLine(date); // 2026/06/07 0:00:00
正しくは次のように書きます。
C#date = date.AddDays(1);
Console.WriteLine(date); // 2026/06/08 0:00:00
5-2. DateTimeから日数や時間を引く方法
日数や時間を引くには、AddDaysやAddHoursに負の値を指定します。
C#DateTime now = new DateTime(2026, 6, 7, 10, 0, 0);
DateTime yesterday = now.AddDays(-1);
DateTime oneHourAgo = now.AddHours(-1);
Console.WriteLine(yesterday);
Console.WriteLine(oneHourAgo);
TimeSpanを使って引くこともできます。
C#DateTime now = DateTime.Now;
TimeSpan span = TimeSpan.FromMinutes(30);
DateTime before30Minutes = now - span;
Console.WriteLine(before30Minutes);
5-3. 2つのDateTimeの差分をTimeSpanで求める方法
2つのDateTimeを引き算すると、差分はTimeSpanになります。
C#DateTime start = new DateTime(2026, 6, 7, 9, 0, 0);
DateTime end = new DateTime(2026, 6, 7, 18, 0, 0);
TimeSpan diff = end - start;
Console.WriteLine(diff); // 09:00:00
Console.WriteLine(diff.TotalHours); // 9
締切までの残り時間を求める場合も同じです。
C#DateTime deadline = new DateTime(2026, 6, 10, 18, 0, 0);
TimeSpan remaining = deadline - DateTime.Now;
Console.WriteLine($"残り{remaining.TotalDays:F1}日");
5-4. TimeSpan同士を足し算・引き算する方法
TimeSpan同士は足し算・引き算できます。
C#TimeSpan morning = TimeSpan.FromHours(3);
TimeSpan afternoon = TimeSpan.FromHours(4.5);
TimeSpan total = morning + afternoon;
Console.WriteLine(total); // 07:30:00
Console.WriteLine(total.TotalHours); // 7.5
引き算も可能です。
C#TimeSpan planned = TimeSpan.FromHours(8);
TimeSpan actual = TimeSpan.FromHours(6.5);
TimeSpan remaining = planned - actual;
Console.WriteLine(remaining.TotalHours); // 1.5
5-5. AddDays・AddHours・AddMinutes・AddSecondsの使い方
DateTimeの加算では、単位に応じたメソッドを使うと読みやすくなります。
C#DateTime baseTime = new DateTime(2026, 6, 7, 10, 0, 0);
Console.WriteLine(baseTime.AddDays(1)); // 1日後
Console.WriteLine(baseTime.AddHours(2)); // 2時間後
Console.WriteLine(baseTime.AddMinutes(30)); // 30分後
Console.WriteLine(baseTime.AddSeconds(10)); // 10秒後
複数の単位を組み合わせることもできます。
C#DateTime result = baseTime
.AddDays(1)
.AddHours(2)
.AddMinutes(30);
Console.WriteLine(result);
5-6. 月末・うるう年・日付またぎで注意すべきポイント
日付計算では、月末やうるう年に注意が必要です。たとえば、1か月後を計算する場合はAddMonthsを使います。
C#DateTime date = new DateTime(2026, 1, 31);
DateTime nextMonth = date.AddMonths(1);
Console.WriteLine(nextMonth); // 2026/02/28 0:00:00
月の日数は月によって違うため、「30日後」と「1か月後」は同じ意味ではありません。
C#DateTime date = new DateTime(2026, 1, 31);
Console.WriteLine(date.AddDays(30));
Console.WriteLine(date.AddMonths(1));
また、日付をまたぐ計算では、単に時刻だけを引くと正しく計算できないことがあります。
C#DateTime start = new DateTime(2026, 6, 7, 22, 0, 0);
DateTime end = new DateTime(2026, 6, 8, 2, 0, 0);
TimeSpan duration = end - start;
Console.WriteLine(duration.TotalHours); // 4
深夜勤務や利用時間の計算では、必ず日付を含めて計算するのが安全です。
6. C#で時間を比較する方法
6-1. DateTime同士を比較する基本
DateTime同士は、前後関係を比較できます。
C#DateTime a = new DateTime(2026, 6, 7, 10, 0, 0);
DateTime b = new DateTime(2026, 6, 7, 12, 0, 0);
Console.WriteLine(a < b); // True
Console.WriteLine(a > b); // False
Console.WriteLine(a == b); // False
日時の比較は、予約日時、期限、開始・終了判定などでよく使います。
6-2. 比較演算子で日時の前後を判定する方法
期限切れを判定する例です。
C#DateTime deadline = new DateTime(2026, 6, 7, 18, 0, 0);
if (DateTime.Now > deadline)
{
Console.WriteLine("期限切れです");
}
else
{
Console.WriteLine("まだ期限内です");
}
開始日時と終了日時の整合性チェックにも使えます。
C#DateTime start = new DateTime(2026, 6, 7, 9, 0, 0);
DateTime end = new DateTime(2026, 6, 7, 18, 0, 0);
if (start >= end)
{
Console.WriteLine("終了日時は開始日時より後にしてください");
}
6-3. CompareToメソッドとDateTime.Compareの使い方
CompareToを使うと、比較結果を数値で取得できます。
C#DateTime a = new DateTime(2026, 6, 7);
DateTime b = new DateTime(2026, 6, 8);
int result = a.CompareTo(b);
Console.WriteLine(result); // 0より小さい
DateTime.Compareでも比較できます。
C#int result = DateTime.Compare(a, b);
if (result < 0)
{
Console.WriteLine("aはbより前です");
}
else if (result > 0)
{
Console.WriteLine("aはbより後です");
}
else
{
Console.WriteLine("同じ日時です");
}
通常の条件分岐では比較演算子のほうが読みやすく、並び替えや汎用的な比較処理ではCompareToやDateTime.Compareが使いやすいです。
6-4. 日付だけを比較して時刻を無視する方法
時刻を無視して日付だけを比較したい場合は、Dateプロパティを使います。
C#DateTime a = new DateTime(2026, 6, 7, 9, 0, 0);
DateTime b = new DateTime(2026, 6, 7, 18, 0, 0);
if (a.Date == b.Date)
{
Console.WriteLine("同じ日付です");
}
今日かどうかを判定する場合は、次のように書けます。
C#DateTime target = new DateTime(2026, 6, 7, 15, 30, 0);
if (target.Date == DateTime.Today)
{
Console.WriteLine("今日です");
}
.NET環境でDateOnlyが使える場合は、日付だけを表す型としてDateOnlyを使う方法もあります。
C#DateOnly date1 = new DateOnly(2026, 6, 7);
DateOnly date2 = DateOnly.FromDateTime(DateTime.Now);
if (date1 == date2)
{
Console.WriteLine("同じ日付です");
}
6-5. 時刻だけを比較する方法
時刻だけを比較したい場合は、TimeOnlyを使うと意図が明確です。
C#TimeOnly open = new TimeOnly(9, 0);
TimeOnly close = new TimeOnly(18, 0);
TimeOnly now = TimeOnly.FromDateTime(DateTime.Now);
if (now >= open && now < close)
{
Console.WriteLine("営業時間内です");
}
else
{
Console.WriteLine("営業時間外です");
}
DateTimeしか使えない環境では、TimeOfDayで時刻部分をTimeSpanとして比較できます。
C#TimeSpan open = new TimeSpan(9, 0, 0);
TimeSpan close = new TimeSpan(18, 0, 0);
TimeSpan now = DateTime.Now.TimeOfDay;
if (now >= open && now < close)
{
Console.WriteLine("営業時間内です");
}
ただし、深夜をまたぐ時間帯では単純な比較では足りません。
C#TimeOnly start = new TimeOnly(22, 0);
TimeOnly end = new TimeOnly(5, 0);
TimeOnly current = new TimeOnly(1, 0);
bool isInRange = current >= start || current < end;
Console.WriteLine(isInRange); // True
6-6. TimeSpan同士を比較する方法
TimeSpanも比較できます。
C#TimeSpan limit = TimeSpan.FromHours(8);
TimeSpan actual = TimeSpan.FromHours(9);
if (actual > limit)
{
Console.WriteLine("上限時間を超えています");
}
処理時間、利用時間、作業時間などを比較するときに便利です。
C#TimeSpan timeout = TimeSpan.FromSeconds(5);
TimeSpan elapsed = TimeSpan.FromSeconds(3);
Console.WriteLine(elapsed <= timeout); // True
7. 日時や時間を文字列に変換・解析する方法
7-1. DateTimeをToStringで表示形式に変換する
DateTimeを文字列として表示するには、ToStringを使います。
C#DateTime now = DateTime.Now;
Console.WriteLine(now.ToString());
Console.WriteLine(now.ToString("yyyy/MM/dd"));
Console.WriteLine(now.ToString("yyyy-MM-dd HH:mm:ss"));
フォーマットを指定すると、表示形式を自由に変更できます。
C#DateTime date = new DateTime(2026, 6, 7, 10, 30, 45);
string text = date.ToString("yyyy年MM月dd日 HH時mm分ss秒");
Console.WriteLine(text);
7-2. よく使う日付・時刻フォーマット指定子
よく使うフォーマット指定子は次のとおりです。
| 指定子 | 意味 | 例 |
|---|---|---|
yyyy | 4桁の年 | 2026 |
MM | 2桁の月 | 06 |
dd | 2桁の日 | 07 |
HH | 24時間表記の時 | 10 |
mm | 分 | 30 |
ss | 秒 | 45 |
fff | ミリ秒 | 123 |
C#DateTime now = DateTime.Now;
Console.WriteLine(now.ToString("yyyyMMdd"));
Console.WriteLine(now.ToString("yyyy-MM-dd"));
Console.WriteLine(now.ToString("HH:mm:ss"));
Console.WriteLine(now.ToString("yyyy-MM-ddTHH:mm:ss"));
ログやファイル名では、並び替えやすい形式にすると便利です。
C#string fileName = $"log_{DateTime.Now:yyyyMMdd_HHmmss}.txt";
Console.WriteLine(fileName);
7-3. Parseで文字列をDateTimeに変換する
文字列をDateTimeに変換するには、DateTime.Parseを使います。
C#string text = "2026/06/07 10:30:00";
DateTime dateTime = DateTime.Parse(text);
Console.WriteLine(dateTime);
ただし、Parseは変換できない文字列が渡されると例外が発生します。
C#string text = "invalid date";
// DateTime.Parse(text); // FormatException
ユーザー入力を扱う場合は、後述するTryParseを使うほうが安全です。
7-4. TryParseで安全に日時文字列を変換する
TryParseは、変換に成功したかどうかをboolで返します。失敗しても例外が発生しないため、ユーザー入力や外部データの変換に向いています。
C#string input = "2026/06/07 10:30:00";
if (DateTime.TryParse(input, out DateTime result))
{
Console.WriteLine($"変換成功: {result}");
}
else
{
Console.WriteLine("日時として変換できません");
}
不正な値を受け取る可能性がある場面では、ParseよりTryParseを優先すると安定したコードになります。
7-5. ParseExactで指定フォーマットの日時を変換する
文字列の形式が決まっている場合は、ParseExactを使います。
C#using System.Globalization;
string input = "2026-06-07 10:30:00";
DateTime dateTime = DateTime.ParseExact(
input,
"yyyy-MM-dd HH:mm:ss",
CultureInfo.InvariantCulture
);
Console.WriteLine(dateTime);
安全に変換したい場合は、TryParseExactを使います。
C#using System.Globalization;
string input = "2026-06-07";
if (DateTime.TryParseExact(
input,
"yyyy-MM-dd",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out DateTime result))
{
Console.WriteLine(result);
}
else
{
Console.WriteLine("指定形式ではありません");
}
APIやCSVなど、日時フォーマットが厳密に決まっているデータではParseExactやTryParseExactが適しています。
7-6. 変換エラーを防ぐための実装ポイント
日時文字列の変換エラーを防ぐには、次の点を意識します。
| ポイント | 理由 |
|---|---|
ユーザー入力にはTryParseを使う | 例外を避けられる |
形式が決まっている場合はTryParseExactを使う | 想定外の形式を弾ける |
| API送受信ではISO 8601形式を検討する | タイムゾーンを扱いやすい |
| 文字列のまま日時比較しない | 形式違いで誤判定しやすい |
| カルチャ差を考慮する | MM/dd/yyyyとdd/MM/yyyyなどの違いがある |
特に、日付の文字列は国や環境によって解釈が変わる可能性があります。内部処理ではできるだけDateTimeやDateTimeOffsetに変換して扱いましょう。
8. タイムゾーンとUTCを扱う方法
8-1. ローカル時刻とUTC時刻の違い
ローカル時刻は、実行環境やユーザーの地域に依存する時刻です。一方、UTCは世界共通の基準時刻です。
日本時間は通常UTC+09:00なので、日本時間の2026年6月7日 18:00は、UTCでは2026年6月7日 09:00です。
C#DateTime localNow = DateTime.Now;
DateTime utcNow = DateTime.UtcNow;
Console.WriteLine(localNow);
Console.WriteLine(utcNow);
グローバルなWebアプリやAPIでは、サーバーのローカル時刻ではなくUTCを基準にすることで、環境差によるバグを減らせます。
8-2. ToLocalTimeとToUniversalTimeの使い方
DateTimeをローカル時刻やUTCに変換するには、ToLocalTimeとToUniversalTimeを使います。
C#DateTime utc = DateTime.UtcNow;
DateTime local = utc.ToLocalTime();
Console.WriteLine(utc);
Console.WriteLine(local);
ローカル時刻をUTCへ変換する場合は、ToUniversalTimeを使います。
C#DateTime local = DateTime.Now;
DateTime utc = local.ToUniversalTime();
Console.WriteLine(utc);
ただし、DateTimeKindがUnspecifiedの値を変換すると、意図しない前提で変換されることがあります。変換前に、その日時がローカルなのかUTCなのかを明確にしておくことが重要です。
8-3. TimeZoneInfoでタイムゾーン変換する方法
任意のタイムゾーンへ変換するには、TimeZoneInfoを使います。TimeZoneInfoは、ローカルタイムゾーンだけでなく、任意のタイムゾーンを表し、別のタイムゾーンへの変換に利用できます。
C#DateTime utcNow = DateTime.UtcNow;
TimeZoneInfo tokyo = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");
DateTime tokyoTime = TimeZoneInfo.ConvertTimeFromUtc(utcNow, tokyo);
Console.WriteLine(tokyoTime);
Windowsでは"Tokyo Standard Time"のようなIDが使われます。LinuxやmacOSでは環境によってIANAタイムゾーンIDを使う場合があります。クロスプラットフォームで運用する場合は、実行環境のタイムゾーンIDに注意してください。
8-4. DateTimeOffsetを使うべきケース
DateTimeOffsetは、日時とUTCからのオフセットを一緒に持つ型です。
C#DateTimeOffset now = DateTimeOffset.Now;
Console.WriteLine(now); // 例: 2026/06/07 10:30:00 +09:00
Console.WriteLine(now.Offset); // 09:00:00
次のようなケースではDateTimeOffsetが向いています。
| ケース | 理由 |
|---|---|
| APIで日時を送受信する | オフセット込みで意味が明確になる |
| ユーザーの現地時刻を保存する | どのUTC差分の時刻か残せる |
| ログに発生時刻を残す | 後から時刻の意味を追いやすい |
| 複数タイムゾーンを扱う | ローカル時刻だけより誤解が少ない |
DateTimeOffsetはDateTimeよりもタイムゾーンを意識した日時表現に向いていますが、完全なタイムゾーン情報そのものを持つわけではありません。保持しているのはUTCとの差分です。
8-5. WebアプリやAPIで時刻を扱うときの注意点
WebアプリやAPIで時刻を扱う場合は、次の方針が基本です。
| 処理 | 推奨 |
|---|---|
| DB保存 | UTCまたはDateTimeOffset |
| API送信 | UTCまたはISO 8601形式 |
| 画面表示 | ユーザーのタイムゾーンへ変換 |
| 比較・集計 | 基準をUTCにそろえる |
| ログ | UTCまたはオフセット付き日時 |
避けたいのは、サーバーのDateTime.Nowをそのまま保存し、別の地域のユーザーにそのまま表示することです。開発者の環境では正しく見えても、本番環境や海外ユーザーでは時刻がずれる可能性があります。
9. 経過時間を測定する方法
9-1. DateTimeで経過時間を測る方法
簡単な経過時間であれば、開始時刻と終了時刻の差分を取ることで測定できます。
C#DateTime start = DateTime.Now;
// 何らかの処理
Thread.Sleep(1000);
DateTime end = DateTime.Now;
TimeSpan elapsed = end - start;
Console.WriteLine(elapsed.TotalSeconds);
ただし、処理時間の測定にはDateTimeよりStopwatchのほうが適しています。DateTime.Nowは現在時刻を取得するためのものであり、システム時刻の変更などの影響を受ける可能性があります。
9-2. Stopwatchで正確に処理時間を測る方法
処理時間を測定するには、System.Diagnostics.Stopwatchを使います。Stopwatchは経過時間を正確に測定するためのメソッドやプロパティを提供するクラスです。
C#using System.Diagnostics;
Stopwatch stopwatch = Stopwatch.StartNew();
// 測定したい処理
Thread.Sleep(1000);
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed);
Console.WriteLine(stopwatch.ElapsedMilliseconds);
ElapsedはTimeSpan、ElapsedMillisecondsは合計ミリ秒を返します。Stopwatch.Elapsedは、測定した経過時間をTimeSpanとして取得できます。
9-3. DateTimeとStopwatchの使い分け
DateTimeとStopwatchは、目的が違います。
| 目的 | 使う型 |
|---|---|
| 現在日時を知りたい | DateTime |
| 予約日時や期限を扱いたい | DateTime |
| 処理に何秒かかったか測りたい | Stopwatch |
| パフォーマンス測定をしたい | Stopwatch |
日時を扱うならDateTime、経過時間を測るならStopwatchと覚えるとよいでしょう。
9-4. 処理時間をミリ秒・秒で表示するサンプルコード
C#using System.Diagnostics;
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 1000000; i++)
{
// 測定対象の処理
}
sw.Stop();
Console.WriteLine($"処理時間: {sw.ElapsedMilliseconds} ms");
Console.WriteLine($"処理時間: {sw.Elapsed.TotalSeconds:F3} 秒");
小数点付きの秒数で表示したい場合は、Elapsed.TotalSecondsを使います。
C#Console.WriteLine($"{sw.Elapsed.TotalSeconds:F2}秒");
10. 待機・タイマー・定期実行で時間を使う方法
10-1. Task.Delayで一定時間待機する方法
非同期処理で一定時間待機するには、Task.Delayを使います。Task.Delayは、指定した時間だけタスクの処理を遅延させるために使われます。
C#await Task.Delay(1000); // 1秒待機
TimeSpanを渡すと、意図が読みやすくなります。
C#await Task.Delay(TimeSpan.FromSeconds(5));
リトライ処理の待機にもよく使います。
C#for (int i = 0; i < 3; i++)
{
Console.WriteLine("処理を試行します");
await Task.Delay(TimeSpan.FromSeconds(2));
}
10-2. Thread.Sleepとの違いと注意点
Thread.Sleepも待機に使えますが、現在のスレッドをブロックします。
C#Thread.Sleep(1000);
一方、Task.Delayは非同期的に待機できるため、非同期メソッドではTask.Delayを使うのが基本です。
C#public async Task WaitAsync()
{
await Task.Delay(TimeSpan.FromSeconds(1));
}
WebアプリやGUIアプリでThread.Sleepを使うと、スレッドを占有して応答性が悪くなることがあります。非同期処理ではawait Task.Delay(...)を使いましょう。
10-3. Timerを使って一定間隔で処理を実行する方法
一定間隔で処理を実行したい場合は、System.Threading.Timerを使えます。Timerは、指定した間隔でスレッドプール上のメソッドを実行する仕組みを提供します。
C#using System.Threading;
Timer timer = new Timer(_ =>
{
Console.WriteLine($"実行: {DateTime.Now:HH:mm:ss}");
}, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
この例では、最初はすぐ実行し、その後5秒ごとに処理を実行します。
アプリ終了までタイマーを使う場合は、タイマーのインスタンスがガベージコレクションされないように保持しておく必要があります。また、不要になったらDisposeで破棄します。
C#timer.Dispose();
10-4. 非同期処理で時間を扱うときの基本
非同期処理では、asyncとawaitを使い、待機にはTask.Delayを使います。
C#public async Task RunAsync()
{
Console.WriteLine("開始");
await Task.Delay(TimeSpan.FromSeconds(3));
Console.WriteLine("終了");
}
定期実行を簡単に書くなら、ループとTask.Delayを組み合わせる方法もあります。
C#public async Task RunLoopAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
Console.WriteLine($"実行: {DateTime.Now}");
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken);
}
}
キャンセルできるようにCancellationTokenを受け取ると、アプリ終了時や処理中断時に安全に止められます。
10-5. タイムアウト処理を実装する方法
非同期処理にタイムアウトを設定するには、CancellationTokenSourceを使う方法があります。
C#using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
try
{
await Task.Delay(TimeSpan.FromSeconds(10), cts.Token);
Console.WriteLine("完了");
}
catch (TaskCanceledException)
{
Console.WriteLine("タイムアウトしました");
}
また、処理タスクとTask.Delayを競争させる方法もあります。
C#Task work = DoWorkAsync();
Task timeout = Task.Delay(TimeSpan.FromSeconds(5));
Task completed = await Task.WhenAny(work, timeout);
if (completed == timeout)
{
Console.WriteLine("タイムアウトしました");
}
else
{
Console.WriteLine("処理が完了しました");
}
11. C#の時間処理でよくあるミスと対策
11-1. DateTime.Nowをそのまま保存して起きる問題
DateTime.Nowはローカル時刻です。サーバーのタイムゾーンに依存するため、別の環境へ移行したときや、複数地域のユーザーが使うアプリでは問題になることがあります。
C#DateTime createdAt = DateTime.Now; // 保存用途では注意
DBに保存する作成日時・更新日時は、UTCで保存する設計がよく使われます。
C#DateTime createdAt = DateTime.UtcNow;
または、オフセットも含めたい場合はDateTimeOffsetを使います。
C#DateTimeOffset createdAt = DateTimeOffset.UtcNow;
11-2. タイムゾーンを考慮しない比較の危険性
異なるタイムゾーンの日時をそのまま比較すると、意図しない結果になることがあります。
C#DateTime tokyoTime = new DateTime(2026, 6, 7, 18, 0, 0);
DateTime utcTime = new DateTime(2026, 6, 7, 9, 0, 0);
Console.WriteLine(tokyoTime == utcTime); // False
この2つは日本時間とUTCで同じ瞬間を表している可能性がありますが、DateTimeの値だけを見ると別の日時です。比較前にUTCへそろえる、またはDateTimeOffsetを使うことを検討しましょう。
C#DateTimeOffset tokyo = new DateTimeOffset(2026, 6, 7, 18, 0, 0, TimeSpan.FromHours(9));
DateTimeOffset utc = new DateTimeOffset(2026, 6, 7, 9, 0, 0, TimeSpan.Zero);
Console.WriteLine(tokyo.ToUniversalTime() == utc.ToUniversalTime()); // True
11-3. 文字列比較で日時を判定してはいけない理由
日時を文字列のまま比較すると、形式によっては正しく判定できません。
C#string a = "2026/6/7";
string b = "2026/12/1";
Console.WriteLine(string.Compare(a, b) < 0);
文字列比較は辞書順で比較されるため、日時としての前後関係と一致しない場合があります。日時はDateTimeに変換して比較しましょう。
C#DateTime a = DateTime.Parse("2026/6/7");
DateTime b = DateTime.Parse("2026/12/1");
Console.WriteLine(a < b); // True
11-4. Total系プロパティと通常プロパティの取り違え
TimeSpan.Hoursは合計時間ではありません。日数を除いた「時間部分」です。
C#TimeSpan span = TimeSpan.FromHours(27);
Console.WriteLine(span.Hours); // 3
Console.WriteLine(span.TotalHours); // 27
作業時間や利用時間を合計時間として表示するなら、TotalHoursを使います。
C#Console.WriteLine($"利用時間: {span.TotalHours}時間");
分単位ならTotalMinutes、秒単位ならTotalSecondsを使います。
11-5. null許容の日時を扱うときの注意点
日時が未設定になる可能性がある場合は、DateTime?を使います。
C#DateTime? completedAt = null;
if (completedAt.HasValue)
{
Console.WriteLine(completedAt.Value);
}
else
{
Console.WriteLine("未完了です");
}
DateTime?をそのままDateTimeとして扱おうとするとエラーになります。値があるか確認してから使いましょう。
C#DateTime? reservedAt = GetReservedAt();
if (reservedAt is not null)
{
Console.WriteLine(reservedAt.Value.ToString("yyyy/MM/dd HH:mm"));
}
デフォルト値が必要な場合は、GetValueOrDefaultや??を使えます。
C#DateTime displayDate = reservedAt ?? DateTime.Today;
11-6. テストしやすい時間処理を書くための考え方
DateTime.Nowをコードの中で直接呼び出すと、テスト時に現在時刻を固定しづらくなります。
C#public bool IsExpired(DateTime deadline)
{
return DateTime.Now > deadline;
}
テストしやすくするには、現在時刻を外から渡します。
C#public bool IsExpired(DateTime deadline, DateTime now)
{
return now > deadline;
}
呼び出し側で現在時刻を渡します。
C#bool expired = IsExpired(deadline, DateTime.UtcNow);
より本格的には、現在時刻を返すインターフェースやTimeProviderを使って、時間依存の処理を分離します。
12. C#の時間処理でよく使う実践サンプル
12-1. 現在時刻から期限切れを判定する
C#DateTime deadline = new DateTime(2026, 6, 10, 18, 0, 0);
DateTime now = DateTime.Now;
if (now > deadline)
{
Console.WriteLine("期限切れです");
}
else
{
Console.WriteLine("期限内です");
}
残り時間も表示する場合は、差分をTimeSpanで求めます。
C#TimeSpan remaining = deadline - now;
if (remaining > TimeSpan.Zero)
{
Console.WriteLine($"残り {remaining.TotalHours:F1} 時間です");
}
else
{
Console.WriteLine("期限切れです");
}
12-2. 開始時刻と終了時刻から作業時間を求める
C#DateTime start = new DateTime(2026, 6, 7, 9, 0, 0);
DateTime end = new DateTime(2026, 6, 7, 17, 30, 0);
TimeSpan workTime = end - start;
Console.WriteLine($"作業時間: {workTime.TotalHours:F1}時間");
休憩時間を引く場合は、TimeSpan同士で計算します。
C#TimeSpan breakTime = TimeSpan.FromHours(1);
TimeSpan actualWorkTime = workTime - breakTime;
Console.WriteLine($"実作業時間: {actualWorkTime.TotalHours:F1}時間");
12-3. 指定日時までの残り時間を表示する
C#DateTime target = new DateTime(2026, 12, 31, 23, 59, 59);
TimeSpan remaining = target - DateTime.Now;
if (remaining <= TimeSpan.Zero)
{
Console.WriteLine("指定日時を過ぎています");
}
else
{
Console.WriteLine($"残り {remaining.Days}日 {remaining.Hours}時間 {remaining.Minutes}分");
}
合計時間で表示する場合はTotalHoursを使います。
C#Console.WriteLine($"残り約 {remaining.TotalHours:F1} 時間");
12-4. 日付をまたぐ勤務時間や利用時間を計算する
開始が22時、終了が翌2時のように日付をまたぐ場合は、必ず日付付きのDateTimeで計算します。
C#DateTime start = new DateTime(2026, 6, 7, 22, 0, 0);
DateTime end = new DateTime(2026, 6, 8, 2, 0, 0);
TimeSpan duration = end - start;
Console.WriteLine($"利用時間: {duration.TotalHours}時間");
時刻だけで受け取った場合は、終了時刻が開始時刻より前なら翌日として扱う方法があります。
C#DateTime date = new DateTime(2026, 6, 7);
TimeSpan startTime = new TimeSpan(22, 0, 0);
TimeSpan endTime = new TimeSpan(2, 0, 0);
DateTime start = date + startTime;
DateTime end = date + endTime;
if (end <= start)
{
end = end.AddDays(1);
}
TimeSpan duration = end - start;
Console.WriteLine(duration.TotalHours); // 4
12-5. ログ出力用に現在時刻をフォーマットする
ログでは、年月日、時分秒、ミリ秒まで出すと追跡しやすくなります。
C#string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
Console.WriteLine($"[{timestamp}] 処理を開始しました");
UTCでログを出すなら、次のようにします。
C#string timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
Console.WriteLine($"[{timestamp}] API request started");
オフセット付きで出したい場合はDateTimeOffsetを使います。
C#string timestamp = DateTimeOffset.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffzzz");
Console.WriteLine($"[{timestamp}] 処理を開始しました");
12-6. API送信用にUTC時刻へ変換する
APIへ日時を送る場合は、UTCへ変換して送ると扱いやすくなります。
C#DateTime localTime = DateTime.Now;
DateTime utcTime = localTime.ToUniversalTime();
string apiValue = utcTime.ToString("yyyy-MM-ddTHH:mm:ssZ");
Console.WriteLine(apiValue);
DateTimeOffsetを使う場合は、ToUniversalTimeでUTCに変換できます。
C#DateTimeOffset localTime = DateTimeOffset.Now;
DateTimeOffset utcTime = localTime.ToUniversalTime();
Console.WriteLine(utcTime.ToString("yyyy-MM-ddTHH:mm:sszzz"));
APIの仕様でISO 8601形式が求められる場合は、仕様に合わせてフォーマットを統一しましょう。
13. C#で時間を扱う方法に関するよくある質問
13-1. DateTimeとTimeSpanの違いは何ですか
DateTimeは「ある日時」を表し、TimeSpanは「時間の長さ」を表します。
C#DateTime dateTime = new DateTime(2026, 6, 7, 10, 0, 0);
TimeSpan span = TimeSpan.FromHours(2);
「2026年6月7日10時」はDateTime、「2時間」はTimeSpanです。2つのDateTimeの差を求めると、結果はTimeSpanになります。
C#DateTime start = new DateTime(2026, 6, 7, 10, 0, 0);
DateTime end = new DateTime(2026, 6, 7, 12, 0, 0);
TimeSpan diff = end - start;
13-2. DateTime.NowとDateTime.UtcNowはどちらを使うべきですか
画面表示など、ローカル時刻が必要な場面ではDateTime.Nowを使います。DB保存、API連携、ログ、複数タイムゾーン対応ではDateTime.UtcNowを使うのが基本です。
C#DateTime displayTime = DateTime.Now;
DateTime savedTime = DateTime.UtcNow;
迷った場合は、内部保存はUTC、表示時にローカル変換という方針にすると安全です。
13-3. 日付だけ・時刻だけを扱うにはどうすればよいですか
日付だけならDateOnly、時刻だけならTimeOnlyが使えます。
C#DateOnly date = new DateOnly(2026, 6, 7);
TimeOnly time = new TimeOnly(10, 30);
DateTimeを使う場合、日付だけを比較するならDate、時刻だけを取得するならTimeOfDayを使います。
C#DateTime now = DateTime.Now;
DateTime datePart = now.Date;
TimeSpan timePart = now.TimeOfDay;
13-4. 2つの日時の差を時間単位で求めるにはどうすればよいですか
2つのDateTimeを引き算し、TimeSpan.TotalHoursを使います。
C#DateTime start = new DateTime(2026, 6, 7, 9, 0, 0);
DateTime end = new DateTime(2026, 6, 7, 17, 30, 0);
TimeSpan diff = end - start;
Console.WriteLine(diff.TotalHours); // 8.5
diff.Hoursでは合計時間ではなく「時間部分」しか取得できないため、合計時間が必要な場合はTotalHoursを使いましょう。
13-5. C#で時間の比較がうまくいかない原因は何ですか
よくある原因は次のとおりです。
| 原因 | 対策 |
|---|---|
| 文字列のまま比較している | DateTimeに変換して比較する |
| タイムゾーンがそろっていない | UTCへ変換して比較する |
| 日付も含めるべき場面で時刻だけ比較している | DateTimeで比較する |
DateTimeKindが不明確 | Utc、Local、Unspecifiedを意識する |
HoursとTotalHoursを混同している | 合計値にはTotal系を使う |
特に、APIやDBから取得した日時は、UTCなのかローカル時刻なのかを確認してから比較することが大切です。
13-6. DateTimeOffsetはいつ使うべきですか
DateTimeOffsetは、UTCとの差分を含めて日時を扱いたいときに使います。
C#DateTimeOffset now = DateTimeOffset.Now;
Console.WriteLine(now);
Console.WriteLine(now.Offset);
API、ログ、海外ユーザー対応、イベント発生時刻の記録など、「この日時がUTCからどれだけずれた地域の時刻なのか」を残したい場合に便利です。
一方で、単純に日付と時刻だけを扱うローカルアプリではDateTimeで十分なこともあります。タイムゾーンやUTCとの関係を明確にしたいならDateTimeOffsetを検討しましょう。
まとめ
C#で時間を扱うときは、まず「日時」「時間間隔」「日付だけ」「時刻だけ」「UTCとの差分付き日時」を分けて考えることが大切です。
DateTimeは日付と時刻、TimeSpanは時間の長さ、DateTimeOffsetはオフセット付きの日時、DateOnlyは日付だけ、TimeOnlyは時刻だけを扱います。現在時刻を取得するにはDateTime.NowやDateTime.UtcNowを使い、保存やAPI連携ではUTCを基準にすると安全です。
時間計算ではAddDays、AddHours、AddMinutesなどを使い、2つの日時の差分はTimeSpanで取得します。比較では、文字列のまま判定せず、DateTimeやDateTimeOffsetに変換してから比較しましょう。
また、処理時間の測定にはDateTimeではなくStopwatch、非同期の待機にはTask.Delay、定期実行にはTimerなど、目的に合った仕組みを使うことが重要です。
C#の時間処理は一見シンプルですが、タイムゾーン、UTC、日付またぎ、文字列変換、合計時間の扱いなどでバグが起きやすい分野です。基本の型と使い分けを理解しておけば、現在時刻の取得、時間計算、比較、ログ出力、API連携まで安定した実装ができるようになります。

