メインコンテンツまでスキップ

「DateTime.Now」タグの記事が1件件あります

全てのタグを見る

C# で DateTime.Now と DateTime.UtcNow を使い分ける基本

鶴田
ディアシステム(株)開発一部第2課

こんにちは。ディアシステム開発1部開発2課の鶴田です。

はじめに

DateTime.Now を使っていたら、Azure にデプロイしたら時刻が 9 時間ずれた」——こういう経験、ありませんか?

DateTime.NowDateTime.UtcNow の使い分けは、C# を書き始めてすぐにぶつかる疑問の一つです。プログラム内部の計算だけなら気にならないこともあります。ただ、「DB に保存する」「API のレスポンスとして返す」「画面に表示する」といった用途が絡んでくると、どちらを使うかで挙動が変わってきます。

この記事では、2 つのプロパティの違いを整理して、ローカル PC と Azure それぞれの環境での動き、そして用途ごとの使い分け方を紹介します。

対象読者:C# を書き始めたばかり〜書いて 1〜2 年程度のエンジニア
前提環境:.NET 6 以降(C# 10〜)


DateTime.Now と DateTime.UtcNow の違い

DateTime.Now はどの設定に依存しているのか

DateTime.Now は、実行環境の OS に設定されているタイムゾーンを使って現在時刻を返します。

  • ローカル PC(Windows):「設定」→「時刻と言語」→「タイムゾーン」の値が使われます。日本語環境なら通常 JST(UTC+9)なので、DateTime.Now は JST の時刻を返します。
  • Azure App Service / Azure Functions:デフォルトのタイムゾーンは UTC です。そのため、同じ DateTime.Now を呼んでもローカルとは 9 時間違う値が返ってきます。

ローカルでテストして OK だったのに、Azure にデプロイしたら時刻がずれる——というバグの正体がこれです。

DateTime.UtcNow は常に UTC

DateTime.UtcNow は協定世界時(UTC)を返します。OS のタイムゾーン設定に関わらず、ローカルでも Azure でも同じ基準の時刻を返してくれます。

2 つの違いは Kind プロパティにも表れます。

DateTime local = DateTime.Now;
Console.WriteLine(local.Kind); // Local

DateTime utc = DateTime.UtcNow;
Console.WriteLine(utc.Kind); // Utc

KindLocal の値が示す時刻は、実行環境のタイムゾーン設定によって変わります。KindUtc の値はどこで実行しても同じ基準です。

Azure でタイムゾーンを変更するには

どうしても DateTime.Now で JST を返したい場合は、Azure の環境変数でタイムゾーンを設定できます。

環境変数名対象プラン設定値(JST の場合)
WEBSITE_TIME_ZONEWindows プランTokyo Standard Time
TZLinux プランAsia/Tokyo

Azure ポータルの「構成」→「アプリケーション設定」から追加できます。ただし、この設定を忘れると UTC に戻るので、チームで開発する場合は「UTC で統一して必要なときだけ変換する」方針の方が安全です。

こうした環境差を踏まえると、DateTime.UtcNow を基準にする方が扱いやすいケースがほとんどです。次のセクションでは、用途別の判断基準を整理します。


どちらを使うべきか

基本方針は「内部処理・保存・通信は UTC で統一し、ユーザーに見せるときだけローカルに変換する」です。

用途推奨理由
DB への保存DateTime.UtcNowUTC で保存しておくと、読み出し時の変換が一意になります
API レスポンス・ログ出力DateTime.UtcNowサーバー間通信では UTC が標準的です
UI への表示UTC → ローカルに変換した値ユーザーが見る時刻は現地時刻の方が自然です
タイムゾーンをまたぐシステムDateTimeOffset.UtcNowオフセット情報を値に含めることで変換ミスを防げます

DateTimeOffsetDateTime にオフセット情報(+09:00 など)を加えた型です。タイムゾーンをまたいだ処理が多い場合は最初から DateTimeOffset を使うと後から悩む場面が減ります。


実践コード例

UTC で保存してローカルで表示する

// DB に保存するときは UTC
DateTime savedAt = DateTime.UtcNow;

// ユーザーに表示するときはローカルに変換
DateTime displayAt = savedAt.ToLocalTime();
Console.WriteLine($"保存日時(UTC): {savedAt:u}");
Console.WriteLine($"表示日時(JST): {displayAt}");

ToLocalTime() は実行環境のタイムゾーンに変換します。Azure のデフォルト環境(UTC)では変換されないので、JST 表示が必要な場合は次のパターンを使います。

TimeZoneInfo で任意のタイムゾーンに変換する

DateTime utcNow = DateTime.UtcNow;

TimeZoneInfo jst = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");
DateTime jstNow = TimeZoneInfo.ConvertTimeFromUtc(utcNow, jst);

Console.WriteLine($"UTC: {utcNow:u}");
Console.WriteLine($"JST: {jstNow}");

FindSystemTimeZoneById の引数は OS によって異なります。Windows では "Tokyo Standard Time"、Linux では "Asia/Tokyo" を使います。Azure App Service は Windows プランと Linux プランで異なるので注意してください。

DateTimeOffset を使う

DateTimeOffset utcNow = DateTimeOffset.UtcNow;

TimeZoneInfo jst = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");
DateTimeOffset jstNow = TimeZoneInfo.ConvertTime(utcNow, jst);

Console.WriteLine($"UTC: {utcNow}");
Console.WriteLine($"JST: {jstNow}");

DateTimeOffset はオフセット情報を値の中に持っているので、DB に保存した後でも「これは何時何分の何時間オフセット」という情報が失われません。


ハマりどころ・注意点

Kind が Unspecified になる問題

DateTimeKind には LocalUtc のほかに Unspecified(未指定)という値があります。これは「UTC なのかローカルなのか不明」という状態です。ORM や ADO.NET で DB から datetime 型の値を読み出すと、KindUnspecified になることがあります。Kind が不定の場合、変換メソッドの動作は呼び出す API によって異なり、意図しない結果になる場合があります。

// DB から取得した値(Kind は Unspecified)
DateTime fromDb = new DateTime(2026, 4, 26, 8, 0, 0);

// 危険:Kind が Unspecified のまま変換すると意図しない結果になる場合がある
DateTime wrong = fromDb.ToLocalTime();

// 安全:UTC と明示してから変換する
DateTime safe = DateTime.SpecifyKind(fromDb, DateTimeKind.Utc).ToLocalTime();

UTC で保存した値を読み出すときは DateTime.SpecifyKindUtc を明示する習慣をつけておくと安心です。

ToLocalTime() と ToUniversalTime() の変換方向ミス

ToLocalTime() は UTC → ローカル、ToUniversalTime() はローカル → UTC です。方向を間違えると 9 時間ずれた値が生成されます。「Local はローカルに変換する(=UTC から)」と覚えると混乱しにくいです。

Azure Functions のタイマートリガー

Azure Functions のタイマートリガーに書く cron 式も UTC 基準です。JST の午前 9 時に実行したい場合は 0 0 0 * * *(UTC 0 時 = JST 9 時)と指定します。


まとめ

  • DateTime.Now の返す時刻は OS のタイムゾーン設定に依存します。Azure のデフォルトは UTC なので、ローカルと挙動が異なります。
  • Azure でタイムゾーンを変えたい場合は WEBSITE_TIME_ZONE(Windows プラン)または TZ(Linux プラン)を設定します。
  • DB・API・ログには DateTime.UtcNow を使い、UI 表示のときだけローカルに変換する——この方針で統一しておくと、環境差によるバグが減ります。
  • タイムゾーンをまたぐ処理が多い場合は DateTimeOffset の利用を検討してみてください。

日時のバグは「ローカルでは動いたのに…」という形で出やすく、原因の特定に時間がかかりがちです。最初から UTC を基準にする習慣をつけておくと、将来の自分を助けることになりますよ。

未経験から始める
システムエンジニア

一生モノのITスキルを身につけよう

あなたの経験とスキルを
ディアシステムで発揮してください!