System.DateTime は、1年から9999年までの日付と時刻を、100ナノ秒(1「ティック」)の精度で表します。System.TimeSpan は期間、つまり2つの DateTime の値の差を表します。どちらも不変の値型(構造体)なので、すべての操作は新しい値を返します。
出力:
2026-09-24 00:00:00
2026-09-24 14:30:00
14:30:05.250
2026 9 24
14:30
Thursday
267
2026-09-24 00:00
14:30:00
このページのすべての例は、明示的な書式文字列で日付を表示しています。既定の ToString() は現在のカルチャに従う(米国では 9/24/2026 2:30:00 PM、ドイツでは 24.09.2026 14:30:00)ので、出力がマシンによって変わります。書式コードはDateTimeの書式にあります。
不正な日付は例外を投げます。new DateTime(2026, 2, 30) は ArgumentOutOfRangeException を発生させ、13月や24時も同じです。
Now、UtcNow、Today
3つの静的プロパティが時計を読みます。
出力例:
Now: 2026-09-24 18:20:41 (Local)
UtcNow: 2026-09-24 16:20:41 (Utc)
Today: 2026-09-24 00:00:00
この例ではローカルなタイムゾーンがUTCより2時間進んでいるので、最初の2行は2時間ずれています。UTCに設定されたマシンなら一致します。Kind プロパティは、値が Local、Utc、Unspecified(自分で作った日付の既定値)のどれかを記録します。保存する、ログに書く、比較する、他のシステムに送るものには、すべて DateTime.UtcNow を使います。夏時間の開始や終了で飛ぶことがなく、どのサーバーでも同じ瞬間を意味します。ローカル時刻への変換は、人に値を見せるときだけにします。
コードにかかる時間を測るには、2つの DateTime.Now の値を引き算するのではなく System.Diagnostics.Stopwatch を使います。分解能がはるかに細かく、時計の調整の影響も受けません。
時間の加算と減算
AddDays、AddHours、AddMinutes、AddSeconds、AddMonths、AddYears は新しい DateTime を返します。さかのぼるには負の数を渡します。DateTime は不変なので、結果を代入しなければなりません。
出力:
2026-01-31
2026-02-03 09:00
2026-01-30 21:00
2026-02-28
2027-01-31
10:30
29
True
その日が存在しない場合、AddMonths は月の最終日に切り詰めます。1月31日に1か月を足すと、3月3日ではなく2月28日(うるう年なら29日)になります。そのため、1か月を2回足すのと2か月を足すのとで、異なる日付になることがあります。
日付の減算:TimeSpan
ある DateTime から別の DateTime を引くと、TimeSpan が得られます。
出力:
3.20:30:00
Days: 3, Hours: 20, Minutes: 30
TotalDays: 3.85
TotalHours: 92.5
TotalMinutes: 5550
Nights: 4
ここはAPIの中で最も間違えやすい部分です。Days、Hours、Minutes、Seconds は期間の成分です(3日、20時間、30分)。TotalDays、TotalHours、TotalMinutes は、期間全体を1つの単位で表した double です。「宿泊客は何時間滞在したか」は Hours(20)ではなく TotalHours(92.5)です。
最後の行は関連する点を示しています。経過したのは3.85日ですが、宿泊客は4泊しました。.Date の部分を比較すると暦の日数を数えられ、請求や「あと何日」の表示で通常求められるのはこちらです。
TimeSpanの値の作成と書式設定
出力:
02:15:00
01:30:00
1.12:00:00
True
03:45:00
True
02:15
36h 0m
00:00:00
TimeSpan は、+、-、比較、Duration()(絶対値)、Negate() に対応しています。@"hh\:mm" のようなカスタム書式ではリテラルの文字の前にバックスラッシュが必要で、そこでの hh は時間の成分(0から23)しか表示しないので、1日を超える期間では、最後から2行目のように TotalHours からテキストを組み立てます。
日付の比較
DateTime は ==、!=、<、>、<=、>= に加えて、CompareTo と DateTime.Compare に対応しています。時刻を無視して日付だけを比較するには、.Date プロパティを比較します。
出力:
True
True
1
True
2026-09-01
「このタイムスタンプは9月30日までか」を判定するには、上のように翌日の始まりと < で比較します。check <= end と書くと、end は2026-09-30 00:00:00なので、最終日の午前0時以降のすべてが除外されてしまいます。
比較はティックだけを見て、Kind は見ません。同じように表示される Local の値と Utc の値は、異なる瞬間であっても等しいとみなされます。保存する時刻をUTCで保つべき理由がもう1つ増えます。
曜日と週の始まり
DayOfWeek は Sunday(0)から Saturday(6)までのenumです。これを使った計算で、平日や週の境界を求められます。
出力:
Thursday
4
Weekend: False
Week starts 2026-09-21 (Monday)
Next Friday: 2026-09-25
2026-09-01 to 2026-09-30
DayOfWeek.ToString() が表示する曜日の名前は常に英語です。ローカライズされた名前が必要なら、"dddd" とカルチャを指定して日付を書式設定します。
文字列から日付を解析する
入力の形式がわかっているなら、CultureInfo.InvariantCulture とともに ParseExact か TryParseExact を使います。書式文字列には、書式設定と同じコードを使います。
出力:
2026-09-24 00:00
2026-09-24 18:05
'2026-02-28' -> Saturday, February 28
'2026-02-30' -> invalid
'28.02.2026' -> invalid
'' -> invalid
2026-02-28
2026-09-24 10:00 Utc
形式なしの DateTime.Parse(text) は、現在のカルチャを使って推測しようとします。"03/04/2026" は米国のマシンでは3月4日、英国のマシンでは4月3日になり、自分のノートパソコンで解析できた日付がサーバーでは FormatException を投げることがあります。Parse はローカルのユーザーが入力したものに限り、ファイル、API、データベースにはインバリアントカルチャとともに ParseExact を使います。ParseExact はテキストが一致しないと FormatException を投げ、TryParseExact は代わりに false を返します。
年齢の計算
生年月日を引き算して365で割るのは、誕生日やうるう年の前後で間違います。年を比べ、今年の誕生日がまだ来ていなければ補正します。
出力:
36
35
18
70 days to go
DateTimeOffset
DateTime は、あいまいな Kind フラグ以上に、どのタイムゾーンにあるかを記録しません。DateTimeOffset は値とUTCからのオフセットを一緒に格納するので、常に1つの正確な瞬間を特定します。
出力:
2026-09-24 14:00 +02:00
2026-09-24 12:00
2026-09-24 12:30
00:30:00
21:00 +09:00
注文が確定した時刻やメッセージが送られた時刻のようなタイムスタンプには、DateTimeOffset(またはUTCの DateTime の値)を使います。データベースやJSONのシリアライザーもうまく扱えます。夏時間の規則を持つ名前付きのタイムゾーン間で変換するには TimeZoneInfo.ConvertTime を使います。古い.NETのバージョンではゾーンのIDがオペレーティングシステムによって異なり(Linuxでは「Europe/Paris」、Windowsでは「Romance Standard Time」)、.NET 6以降はどちらも受け付けます。
DateOnlyとTimeOnly(.NET 6以降)
多くの値は、時刻のない日付(誕生日、期日)か、日付のない時刻(営業時間)です。.NET 6では、それらのための2つの型が追加されました。
// .NET 6 and later
DateOnly birthday = new DateOnly(1990, 9, 24);
DateOnly due = DateOnly.FromDateTime(DateTime.Today).AddDays(14);
int daysLeft = due.DayNumber - DateOnly.FromDateTime(DateTime.Today).DayNumber;
TimeOnly opens = new TimeOnly(9, 0);
TimeOnly closes = new TimeOnly(17, 30);
bool isOpen = TimeOnly.FromDateTime(DateTime.Now).IsBetween(opens, closes);
これらの型によって、余計な時刻やタイムゾーンが日付を1日ずらしてしまう種類のバグがなくなります。古いコードや、.NET FrameworkやUnityを対象とするコードでは、時刻を午前0時のままにした DateTime を使います。
よくある間違い
AddDaysの結果を捨てる。DateTimeは不変です。返された値を代入します。TotalHoursの代わりにHoursを使う。 成分と期間全体の違いです。DateTime.Nowを保存する。 UTCで保存し、表示のために変換します。- ログ、ファイル、テストで書式なしの
ToString()を呼ぶ。 出力がマシンのカルチャに依存します。 - 機械のデータを
DateTime.Parseで解析する。ParseExactとインバリアントカルチャを使います。 - 書式文字列で
mmとMM(分と月)を取り違える。 DateTimeの書式を参照してください。
よくある質問
DateTime.NowとDateTime.UtcNowの違いは何ですか?
DateTime.Now はコンピューターのローカルなタイムゾーンでの現在時刻で、Kind は Local です。DateTime.UtcNow はUTCでの現在時刻で、Kind は Utc です。タイムゾーンの変換を省くので、こちらのほうが高速でもあります。タイムスタンプはUTCで保存して比較し、ローカル時刻への変換は表示のときだけにします。
C#で2つの日付の差を求めるには?
引き算します:TimeSpan gap = end - start;。その後、期間全体を double として得るには gap.TotalDays、gap.TotalHours、gap.TotalMinutes を、日数の部分には gap.Days を読みます。暦の月数や年数については、月によって長さが異なるので組み込みのプロパティはありません。年と月のフィールドを自分で比較します。
TimeSpan.HoursとTotalHoursの違いは何ですか?
Hours は、日数を除いた後の時間の成分だけで、0から23です。TotalHours は期間全体を時間で表したもので、double です。1日と3時間の期間なら、Hours は3、TotalHours は27です。TotalHours のつもりで Hours を使うのは、非常によくあるバグです。
C#で日付の文字列を解析するには?
形式がわかっているなら DateTime.ParseExact(text, "yyyy-MM-dd", CultureInfo.InvariantCulture) を使うか、不正な入力で FormatException の代わりに false を得るには DateTime.TryParseExact を使います。DateTime.Parse は現在のカルチャから形式を推測するので、03/04/2026 は米国では3月4日、英国では4月3日を意味します。
AddDaysでDateTimeが変わらないのはなぜですか?
DateTime は不変の値型だからです。AddDays、AddHours などのメソッドは新しい DateTime を返し、元の値は変えないので、結果を代入する必要があります:due = due.AddDays(7);。
DateTimeではなくDateTimeOffsetを使うべきなのはどんなときですか?
注文が確定した時刻やログが書かれた時刻のように、正確な瞬間を特定しなければならないタイムスタンプには、特にデータがサーバーやタイムゾーンをまたいで移動するなら DateTimeOffset を使います。値と一緒にUTCからのオフセットを格納します。UTCだけのタイムスタンプや、意味のあるタイムゾーンを持たない日付なら DateTime で十分です。