PowerShellでローカルタイムとUTCタイムを表示する方法を現在の環境で使うなら「current local timeはGet-Date、UTCは(Get-Date).ToUniversalTime()またはDateTimeOffset.UtcNowで取得し、交換用にはISO 8601 round-trip formatを使います。localとUTCを引き算してoffsetを求めず、Get-TimeZoneとDateTimeOffset.Offsetを読みます。」が基本方針です。対象はPowerShell版差に依存しないDateTime.ToUniversalTimeとDateTimeOffsetを中心にするケースです。時刻確認はread-onlyで行い、timezoneやtime serviceの設定変更は別手順にします。
local・UTC・offsetを同じ瞬間から作る
ローカル時刻とUTCを並べる場合は、同じ瞬間からDateTimeOffsetを作り、LocalDateTime、UtcDateTime、Offsetを出力します。端末のTimeZoneInfoと夏時間適用も記録し、文字列を再解析して時刻を作り直しません。UTCとの差がOffsetと一致することを確認すれば、ログ間の時差を安全に換算できます。
- Get-TimeZoneでIdとBaseUtcOffsetを確認する
- Get-DateのKindとDateTimeOffset.Now.Offsetを確認する
- NTP/time service状態は別途確認する
- log仕様がUTCかoffset付きlocalかを決める
ISO 8601 round-trip形式で表示する
表示は読み取りです。Set-Dateやw32tm resync/configはauthentication、certificate、log順序、clusterへ影響するため、時刻確認の延長で実行しません。変更前にauthoritative time source、domain policy、offset、drift、rollbackを確認します。誤った時刻変更時は管理手順でsourceと同期し、security logを改変しません。
DateTime KindとDateTimeOffsetを区別する
- localとUTCを別時点で取得して差を計算する
- BaseUtcOffsetを常にcurrent offsetとする
- DateTime Kindを見ない
- format文字列にoffsetを含めない
- 時刻確認のためSet-Dateする
Windows timezone IDで変換する
localとUTCを同時取得
$now=[DateTimeOffset]::Now
[pscustomobject]@{Local=$now;Utc=$now.UtcDateTime;Offset=$now.Offset;TimeZone=(Get-TimeZone).Id}
同じinstantからlocalとUTCを作ります。
ISO 8601表示
[DateTimeOffset]::Now.ToString('o')
[DateTimeOffset]::UtcNow.ToString('o')
o formatはoffsetとfractionを保持し、machine間交換に適します。
Get-DateからUTCへ
$local=Get-Date
$utc=$local.ToUniversalTime()
$local
$utc
Get-Date -AsUTCの版依存を避けたいscriptで使えます。
指定timezoneへ変換
$utc=[DateTime]::UtcNow
[TimeZoneInfo]::ConvertTimeFromUtc($utc,[TimeZoneInfo]::FindSystemTimeZoneById('Tokyo Standard Time'))
Windows timezone IDを実機で確認します。固定+09:00計算よりDST地域にも対応します。
time serviceを読み取る
w32tm.exe /query /status
権限やservice状態で取得できない場合があります。resyncやconfig変更は別作業です。
w32time同期statusを時刻表示と分ける
DateTime KindがUnspecifiedだと変換時の解釈が曖昧です。DateTimeOffsetはinstantとoffsetを持ちますがtimezone rulesそのものは持ちません。BaseUtcOffsetだけではDST適用中offsetを示さない地域があります。local表示とUTCを別々の瞬間に取得すると微小差があるため、同じDateTimeOffsetから派生します。
timezone変更をせずread-onlyで確認する
UTC clock source、w32tm status、Get-TimeZone、DateTimeOffsetを照合し、DST境界を扱うsystemは既知日時でunit testします。保存logをParseExactまたはDateTimeOffset.Parseで読み戻し、instantが同じか確認します。
UTC・offset・Unix timeの整合を検収する
現在時刻はDateTimeOffset.Nowでinstantとoffsetを一体で取得し、UtcDateTimeまたはToUniversalTimeでUTCへ変換します。表示はISO 8601のo formatを使い、Get-DateのKind不明な値やtimezone変換で二重offsetを加えません。
一つのDateTimeOffsetから四表現を出す
$now=[DateTimeOffset]::Now
[pscustomobject]@{Local=$now.ToString('o');Utc=$now.ToUniversalTime().ToString('o');Offset=$now.Offset;UnixSeconds=$now.ToUnixTimeSeconds()} | Format-List
timezone相違とw32time障害を独立確認する
- 時刻表現が整合:Local、UTC、Offsetが同じinstantを示し、UTC差がoffsetと一致する
- 期待timezoneと相違:timezone IDが見つからない場合はUnsupportedZoneとして現在時刻表示と分ける
- timezone・w32time error:time service query失敗、invalid/ambiguous local timeは同期状態/変換errorとして扱う
画面clockとUTCの文字列差を時刻ずれとみなしません。DST境界では同じlocal表記が二instantを表すことがあるためoffsetを保存します。w32tm statusは同期sourceの補助で、取得時刻そのものとは別です。
DST境界とUTC指定を試す
$zone=[TimeZoneInfo]::FindSystemTimeZoneById('Tokyo Standard Time')
$utc=[DateTime]::UtcNow
[pscustomobject]@{Utc=$utc.ToString('o');Tokyo=[TimeZoneInfo]::ConvertTimeFromUtc($utc,$zone).ToString('yyyy-MM-ddTHH:mm:ss')}
UTC、正/負offset、DST境界、unknown zone、time service unavailableをtestします。ISO local/UTC、offset、timezone ID、Unix time、w32tm source/stratumを保存します。
localとUTCを別々のGet-Dateで取得すると、わずかに異なる瞬間を比較してしまいます。一つのDateTimeOffsetから両方を作り、offsetとtimezone IDを同じ記録へ残します。

コメント