PowerShell でファイルの作成日時や更新日時を一括で書き換えたら、なぜか必ず 1 時間ずれてしまう──。特にサマータイム(DST)を採用している国やタイムゾーンでは、多くの管理者・開発者が一度は踏む落とし穴です。この記事では、ずれの正体から具体的な対処法、UTC を前提にした安全な書き換えパターンまで、実務でそのまま使えるレベルで整理して解説します。
PowerShell でファイル時刻が 1 時間ずれる現象とは
まず、実際に遭遇しやすい具体例から見ていきます。たとえば次のように PowerShell でファイルの作成日時を変更したとします。
(Get-Item "C:\Users\Owner\Documents\filename.odt").CreationTime = "2025-09-03 18:00:00"
スクリプト上は「2025/09/03 18:00:00」を設定したつもりなのに、エクスプローラーのプロパティや PowerShell で確認すると、次のように必ず 1 時間ずれた値になってしまうケースがあります。
(Get-Item "C:\Users\Owner\Documents\filename.odt").CreationTime
# 期待: 2025/09/03 18:00:00
# 実際: 2025/09/03 17:00:00 または 19:00:00 など、1 時間ズレ
このときにまず疑いたくなるのが「サマータイム(Summer Time / Daylight Saving Time, DST)の影響では?」という点です。そして結論から言うと、この 1 時間の差はほぼ確実に DST と UTC ⇔ ローカル時刻の変換ロジックが原因です。
現象をざっくり整理すると次のようになります。
| 項目 | 内容 |
|---|---|
| 発生条件 | OS のタイムゾーンがサマータイム(DST)を持つ地域に設定されている / またはそうしたマシン上でスクリプトが実行されている。 |
| 操作 | PowerShell から .CreationTime プロパティにローカル時刻の文字列や [datetime] をそのまま代入する。 |
| 症状 | 設定した時刻と比べて、保存された値が必ず ±1 時間ずれて見える。 |
| 主な原因 | ファイルシステムが内部的には UTC で保持しており、書き込み時と表示時で異なる DST オフセットが適用されるため。 |
では、なぜこうしたずれが生まれるのか、もう少し基礎から確認していきましょう。
Windows / NTFS のタイムスタンプの仕組みをおさらい
PowerShell の話に入る前に、まずは Windows / NTFS がファイルの日時をどのように扱っているかを理解しておくと、後の挙動がすべて腑に落ちます。
ポイントは次の 3 つです。
- ファイルシステムは UTC でタイムスタンプを保持する
- 人間が目にする値(エクスプローラーや PowerShell)は「ローカル時刻」に変換されて表示される
- ローカル時刻への変換時に、タイムゾーンとサマータイム(DST)の情報が使われる
この関係を表にすると次のようになります。
| レイヤー | どの時刻を使うか | 例 |
|---|---|---|
| NTFS(ディスク上) | 常に UTC(協定世界時) | 2025-09-03T09:00:00Z |
| Windows カーネル / API | ファイル API で UTC ⇔ ローカル時刻を相互変換 | タイムゾーンが UTC+9 なら、ローカルでは 18:00 として扱う |
| PowerShell / エクスプローラー | ローカル時刻の DateTime として表示 | 日本時間なら「2025/09/03 18:00」と表示 |
つまり、私たちが「ファイルの作成日時」として目にしている値は、「UTC で保存された値」+「そのマシンのタイムゾーンと DST 設定」から算出された結果にすぎません。
このとき、サマータイムが導入されているタイムゾーンでは、「いつの日時か」によって UTC からローカル時刻へのオフセットが変わります。
- 標準時間期間:UTC+1(例)
- 夏時間(DST)期間:UTC+2(例)
この「オフセットの差」が、CreationTime を扱うときに 1 時間のずれとして現れます。
CreationTime を使うと 1 時間ずれる理由
では、本題である .CreationTime プロパティの挙動をもう少し丁寧に追いかけてみます。
次のようなコードを実行したとします。
$path = "C:\Users\Owner\Documents\filename.odt"
(Get-Item $path).CreationTime = "2025-09-03 18:00:00"
内部では、おおよそ次のようなステップで処理が行われます。
- 文字列
"2025-09-03 18:00:00"が[datetime]としてローカル時刻の DateTime に解釈される。 - その DateTime が「ローカル時刻 → UTC」に変換され、NTFS のタイムスタンプとして書き込まれる。
- プロパティ表示やエクスプローラーでは「UTC → ローカル時刻」に再変換して表示する。
- このとき、対象の日付がサマータイム期間内かどうかを見てオフセットが決まる。
書き込み時と表示時の「どのオフセットが使われるか」がずれていると、結果として 1 時間の差が発生します。典型的なパターンを簡略化して表にすると次のようになります。
| タイミング | 想定される状況 | UTC との関係 |
|---|---|---|
| スクリプト実行時 | 現在は標準時間期間(UTC+1)だが、 ファイルに設定している日付は夏時間期間内 | ローカル 18:00 → UTC 17:00 として保存 |
| 後で表示するとき | 対象日付は夏時間期間(UTC+2)として解釈される | 保存されている UTC 17:00 → ローカル 19:00 として表示 |
| 結果 | 「18:00 を設定したのに 19:00 になっている」ように見える | 1 時間のずれ |
逆に、「現在が夏時間で、設定している日付が標準時間」といった組み合わせでは、設定値より 1 時間早い値として表示されることもあり得ます。
重要なのは、.CreationTime はあくまで「ローカル時刻」として解釈され、内部的には UTC へ変換されるという点です。その変換の際に使われる DST 設定が、書き込み時と表示時で食い違うと、どうしても 1 時間の差が出てしまいます。
CreationTime と CreationTimeUtc の違いを整理する
ここで、よく混乱の元になる .CreationTime と .CreationTimeUtc の違いを明確にしておきましょう。
| プロパティ名 | 意味 | 扱う時刻 | DST の影響 |
|---|---|---|---|
CreationTime | ローカル時刻としての作成日時 | OS のタイムゾーン+DST を反映した値 | 表示・設定時に影響を受ける |
CreationTimeUtc | UTC としての作成日時 | 常に UTC(タイムゾーンの影響なし) | プロパティ自体は DST の影響を受けない |
LastWriteTime | ローカル時刻としての最終更新日時 | CreationTime と同様 | 同じくずれる可能性あり |
LastWriteTimeUtc | UTC としての最終更新日時 | 常に UTC | プロパティ自体は DST の影響を受けない |
つまり、「DST で振り回されたくない」「1 時間ずれるのを避けたい」のであれば、基本的には UTC 系のプロパティ(○○Utc)を使うのが一番安全です。
解決策の本命:CreationTimeUtc を使ってずれをなくす
最もシンプルで再現性が高い解決策は、UTC を基準にして .CreationTimeUtc を直接書き換えるやり方です。
基本形は次のようになります。
$path = "C:\Users\Owner\Documents\filename.odt"
# 1. ローカル時刻として扱いたい日時を作成
$local = Get-Date "2025-09-03 18:00:00"
# 2. ローカル → UTC に変換(このとき DST を考慮)
$utc = [System.TimeZoneInfo]::ConvertTimeToUtc($local)
# 3. CreationTimeUtc に UTC をそのまま書き込む
(Get-Item $path).CreationTimeUtc = $utc
# 4. 確認
(Get-Item $path).CreationTime # ローカル時刻(18:00 になるはず)
(Get-Item $path).CreationTimeUtc # UTC
この方法のメリットは次の通りです。
- タイムスタンプの保存は常に UTCなので、DST による揺れがない。
- ローカル時刻で見たい場合は
CreationTimeを参照すればよく、表示時の変換ロジックに任せられる。 - 別のタイムゾーンのマシンでスクリプトを実行しても、UTC を介して一貫した値を扱える。
もし最初から UTC ベースで時刻を指定できるのであれば、次のように書くこともできます。
$path = "C:\Users\Owner\Documents\filename.odt"
# UTC の 2025-09-03 18:00:00 を直接指定
(Get-Item $path).CreationTimeUtc = [datetime]"2025-09-03T18:00:00Z"
この場合、「2025-09-03T18:00:00Z」は「UTC の 18:00」を意味します。ローカル時刻でどう見えるかは、そのマシンのタイムゾーン次第になります。
実務で使いやすいテンプレート例
実際に管理作業などでよく使うパターンをテンプレート化しておくと便利です。ここでは、「ローカル時間で A 日 B 時に見えるように CreationTime を揃えたい」というケースを想定します。
単一ファイルの作成日時を設定するテンプレート
$path = "C:\Users\Owner\Documents\filename.odt"
$targetLocalTime = Get-Date "2025-09-03 18:00:00"
# ローカル → UTC に変換してから CreationTimeUtc に書き込む
$targetUtc = [System.TimeZoneInfo]::ConvertTimeToUtc($targetLocalTime)
$item = Get-Item $path
$item.CreationTimeUtc = $targetUtc
$item.LastWriteTimeUtc = $targetUtc # 更新日時も揃えたい場合
フォルダ内のファイルを一括で揃えるテンプレート
$folder = "C:\Users\Owner\Documents\MyFolder"
$targetLocalTime = Get-Date "2025-09-03 18:00:00"
$targetUtc = [System.TimeZoneInfo]::ConvertTimeToUtc($targetLocalTime)
Get-ChildItem $folder -File | ForEach-Object {
$_.CreationTimeUtc = $targetUtc
$_.LastWriteTimeUtc = $targetUtc
}
このように、「まずローカルで扱いやすい日時を組み立てる → UTC に変換する → ○○Utc プロパティに書く」という流れを固定化しておくと、タイムゾーンが変わっても動作が安定します。
CreationTime を使い続けたい場合の妥協策
何らかの理由で .CreationTimeUtc を使えない(既存のコードとの互換性など)場合は、CreationTime に代入する値をあらかじめ「補正」するという手段もあります。
原理的には、「表示時に適用される DST オフセットの差分」を自分で先に加減しておくイメージです。ただし、将来的にタイムゾーン設定を変えたときやルール変更があったときに挙動が変わる可能性があり、あくまで妥協策・暫定策と考えるべきです。
シンプルな例として、「夏時間期間内の日時だけ 1 時間引いてから設定する」といった処理を自前で書くこともできます。
$path = "C:\Users\Owner\Documents\filename.odt"
$targetLocalTime = Get-Date "2025-09-03 18:00:00"
# 対象日時が夏時間期間かどうかで補正(例として 1 時間マイナス)
$tz = [System.TimeZoneInfo]::Local
if ($tz.IsDaylightSavingTime($targetLocalTime)) {
$adjusted = $targetLocalTime.AddHours(-1)
} else {
$adjusted = $targetLocalTime
}
# 補正後のローカル時刻を CreationTime に書き込む
(Get-Item $path).CreationTime = $adjusted
この方法は、
- あくまで「見た目を合わせるための補正」であり、
- 将来的に DST のルールが変わった場合などに再度ずれが発生する可能性がある
というリスクがあります。そのため、新規のスクリプトや長期運用するツールでは、やはり CreationTimeUtc を前提に設計することを強くおすすめします。
すでにずれてしまったタイムスタンプを修正する方法
「原因はわかったけれど、すでに 1 時間ずれて保存されてしまった大量のファイルがある」という状況もよくあります。この場合は、UTC のタイムスタンプを基準にして再計算するのが安全です。
単一ファイルのずれを ±1 時間補正する
$path = "C:\Users\Owner\Documents\filename.odt"
$item = Get-Item $path
# 現在の CreationTimeUtc を取得
$currentUtc = $item.CreationTimeUtc
# 1 時間戻す例(実際は状況に応じて +1 または -1)
$fixedUtc = $currentUtc.AddHours(-1)
$item.CreationTimeUtc = $fixedUtc
$item.LastWriteTimeUtc = $fixedUtc # 必要なら更新日時も
ここでは単純に「1 時間戻す」例を示しましたが、実際には「いつ」「どのタイムゾーンで」ずれが発生したかを踏まえて調整する必要があります。
フォルダ配下のファイルを一括補正する
$folder = "C:\Users\Owner\Documents\MyFolder"
Get-ChildItem $folder -File | ForEach-Object {
$utc = $_.CreationTimeUtc
$fixedUtc = $utc.AddHours(-1) # 必要に応じて +1 か -1 に変更
$_.CreationTimeUtc = $fixedUtc
$_.LastWriteTimeUtc = $fixedUtc
}
このように UTC 側を直接操作すれば、ローカルの DST 設定があとから変わっても、ファイル内部のタイムスタンプは一貫したままになります。
関連プロパティ(LastWriteTime など)にも同じ問題が出る
CreationTime だけでなく、更新日時やアクセス日時も同じ仕組みで動いています。そのため、「作成日時だけ UTC で管理し、更新日時はローカルのまま」というような運用をすると、後から見返したときに辻褄が合わなくなる可能性があります。
| プロパティ | 意味 | UTC 版 | 推奨運用 |
|---|---|---|---|
CreationTime | ローカルの作成日時 | CreationTimeUtc | 書き換えは UTC 版を使い、参照時に必要ならローカルに変換 |
LastWriteTime | ローカルの最終更新日時 | LastWriteTimeUtc | ログや監査用途なら UTC 版を基準にする |
LastAccessTime | ローカルの最終アクセス日時 | LastAccessTimeUtc | 必要な場合は UTC 版と合わせて運用 |
運用ポリシーとしては、次のように決めておくと混乱が少なくなります。
- スクリプト内の計算や比較は基本的に UTCで行う(
○○Utcを基準にする)。 - 人間が見る画面やレポートではローカル時刻に変換して表示する。
- すべてのファイル操作スクリプトで同じポリシーを採用する。
タイムゾーンの違うマシン間でスクリプトを共有する場合の注意点
PowerShell スクリプトを複数の拠点や異なる国にまたがって利用する場合、ローカル時刻前提のスクリプトは非常に危険です。同じコードでも、実行するマシンのタイムゾーンによって結果が変わるためです。
そのような環境では、次のような方針を徹底すると良いでしょう。
- 時刻データは基本的に UTC で扱う(
Get-Date -AsUtcや○○Utcプロパティを利用)。 - ログファイルには UTC を ISO 8601 形式(例:
2025-09-03T09:00:00Z)で出力する。 - ユーザー向け UI やレポートでは、最後にローカル時刻へ変換してから表示する。
PowerShell では、UTC からローカル時刻への変換も簡単に行えます。
$utc = (Get-Item "C:\Users\Owner\Documents\filename.odt").CreationTimeUtc
# ローカル時間に変換
$local = $utc.ToLocalTime()
$local # タイムゾーンと DST を考慮したローカル日時として表示
このように、スクリプト内部では UTC をインターフェースにしておけば、タイムゾーンや DST の有無が異なるマシン間でも、整合性の取れた運用が可能になります。
PowerShell でファイル時刻を扱う際のベストプラクティス
ここまでの内容を踏まえて、実務で意識しておきたいベストプラクティスをまとめます。
| シーン | 推奨する考え方 | 使用するプロパティ・機能 |
|---|---|---|
| 新規スクリプトの設計 | 内部ロジックは UTC で統一する | CreationTimeUtc, LastWriteTimeUtc, Get-Date -AsUtc |
| 既存コードの保守 | ローカル時刻前提の箇所を洗い出し、影響範囲を確認 | CreationTime を直接書き換えている部分に注意 |
| 人間が見るレポートや画面 | 最後にローカルへ変換し、タイムゾーンも併記する | $utc.ToLocalTime()、表示時に「JST」「CET」などを付ける |
| トラブルシュート | まず UTC の値を確認し、ローカル変換のロジックを疑う | CreationTimeUtc と CreationTime を両方比較 |
まとめ:ずれの正体は DST と UTC 変換。CreationTimeUtc で安定運用を
本記事で解説したポイントを最後に整理しておきます。
- ファイルのタイムスタンプは NTFS 上では常に UTC で保持されている。
- PowerShell の
.CreationTimeなどは「ローカル時刻」として表示され、内部で UTC ⇔ ローカル時刻の変換が行われている。 - サマータイム(DST)を採用しているタイムゾーンでは、書き込み時と表示時で異なるオフセットが適用されると、結果として 1 時間のずれが発生する。
- ずれを避ける一番確実な方法は、
CreationTimeUtc/LastWriteTimeUtcなどの UTC プロパティを前提に扱うこと。 - 既存のローカル時刻ベースのコードは、DST ルールの変更やタイムゾーンの変更に弱いので、可能な範囲で UTC ベースにリファクタリングする。
PowerShell でファイルの作成日時・更新日時を操作する処理は、バックアップ、ログ管理、テストデータ生成など、さまざまな場面で利用されます。そのたびに「1 時間ずれる…」と悩まされないように、UTC を基準に設計するという発想を一度身につけておくと、後々のトラブルを大きく減らすことができます。
もし今まさに CreationTime の 1 時間のずれに悩んでいるのであれば、まずは小さなスクリプトから CreationTimeUtc を試し、UTC ベースでの運用に慣れていくことをおすすめします。

コメント