Azure Virtual Desktop で DateTime.Now が UTC を返す

:white_question_mark: Question

Azure Virtual Desktop(AVD)上でワークフローを実行し、DateTime.Now で現在時刻を取得すると、日本標準時(JST)ではなく UTC ベースの値が返ります。
リモートデスクトップで接続しているセッション上では端末のタイムゾーンが日本標準時に設定されていることを確認しています。

:light_bulb: Answer

原因

AVD で Time Zone Redirection が有効になっていることが考えられます。これにより、ユーザーセッションのタイムゾーンは接続ユーザーごとにリダイレクトされますが、システムアカウントなどの非対話型セッションはホストの既定のシステムタイムゾーンを使用し続けます。

そのため、DateTime.Now はリダイレクトされたユーザーセッションのタイムゾーンではなく、システム側のタイムゾーンを参照し、結果として UTC ベースの時刻を返すことがあります。

解決策

システムと現在のユーザーセッションのタイムゾーンの確認方法

まずシステムとユーザーセッションのタイムゾーン設定に差分がないかどうかご確認ください。

コマンドプロンプトの下記コマンドを実行頂くことで、システムとユーザーセッションにそれぞれ設定されているタイムゾーンを確認できます。

システムのタイムゾーン確認:

reg query "HKLM\SYSTEM\CurrentControlSet\Control\TimeZoneInformation"

TimeZoneKeyName の値を確認します。

ユーザーセッションのタイムゾーン確認:

tzutil /g

上記の出力に差分がある場合、Time Zone Redirectionにより異なるタイムゾーンを参照していることが要因と考えられます。下記いずれかの回避策をご検討ください。

回避策A

ワークフローで UTC オフセットを含む一意の時刻を扱いたい場合は、DateTime.Now ではなく DateTimeOffset.Now の利用を検討します。

DateTime はタイムゾーン情報を保持しないため、取得結果がホストのタイムゾーン設定に依存します。一方で DateTimeOffset は UTC からの差分を保持できるため、日時を扱う既定の型として推奨されています。

これらの DateTimeOffset 値の使用は、 DateTime 値の場合よりもはるかに一般的です。 その結果、アプリケーション開発の既定の日付と時刻の種類として DateTimeOffset を検討してください。

参考: 「 DateTimeOffset 構造体」

出力結果を日本標準時として扱いたい場合は、TimeZoneInfo を使って明示的に変換します。

nowOffset = DateTimeOffset.Now
timeZone = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time")
displayTime = TimeZoneInfo.ConvertTime(nowOffset, timeZone)

変数の型:

  • nowOffset: DateTimeOffset

  • displayTime: DateTimeOffset

  • timeZone: TimeZoneInfo

回避策B

運用要件上必要な場合は、AVD ホストのシステムタイムゾーン自体を自動化で使用する標準タイムゾーンへ統一することも回避策として考えられます。
ただし、共有環境や複数リージョンからアクセスされる環境では慎重な判断が必要です。複数地域のユーザーが利用する場合は、Time Zone Redirection を維持しつつ、ワークフロー側で時刻を明示的に扱う方法の方が安全です。

参考: 「 AVD 環境でシステムのタイム ゾーンを設定する」