PowerShellでファイルの作成日時が1時間ずれる原因と対処法(CreationTimeとCreationTimeUtcを徹底解説)

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"

内部では、おおよそ次のようなステップで処理が行われます。

  1. 文字列 "2025-09-03 18:00:00" が [datetime] としてローカル時刻の DateTime に解釈される。
  2. その DateTime が「ローカル時刻 → UTC」に変換され、NTFS のタイムスタンプとして書き込まれる。
  3. プロパティ表示やエクスプローラーでは「UTC → ローカル時刻」に再変換して表示する。
  4. このとき、対象の日付がサマータイム期間内かどうかを見てオフセットが決まる。

書き込み時と表示時の「どのオフセットが使われるか」がずれていると、結果として 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 を反映した値表示・設定時に影響を受ける
CreationTimeUtcUTC としての作成日時常に UTC(タイムゾーンの影響なし)プロパティ自体は DST の影響を受けない
LastWriteTimeローカル時刻としての最終更新日時CreationTime と同様同じくずれる可能性あり
LastWriteTimeUtcUTC としての最終更新日時常に 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 ベースでの運用に慣れていくことをおすすめします。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次