【bat】バッチファイルでファイルの更新日時を変更する方法完全ガイド|PowerShell・copy /b・一括変更・相対日時・3種タイムスタンプ・実践パターンまで

ファイルのタイムスタンプ(更新日時)を変更したい場面は意外と多くあります。テスト用データの準備・バックアップ後の整合性確認・古いファイルをforfilesで検出するための条件調整・ログファイルの日付整理などが典型的な用途です。

この記事では バッチファイルでファイルの更新日時を変更する全手法 を体系的に解説します。PowerShellによる指定日時変更・copy /b で現在日時にリセット・作成日時/アクセス日時の変更・一括処理まで網羅します。

この記事でわかること

  • PowerShell でファイルの更新日時を任意の日時に変更する方法(基本)
  • copy /b で更新日時を現在日時にリセットする方法
  • 作成日時(CreationTime)・アクセス日時(LastAccessTime)も変更する方法
  • 複数ファイルを一括で更新日時変更する方法
  • サブフォルダ再帰で全ファイルを一括変更する方法
  • 現在日時の N 日前・N 日後に相対変更する方法
  • 落とし穴5選・実践例3本・FAQ6問
スポンサーリンク
  1. 1. Windowsファイルのタイムスタンプ3種類
  2. 2. PowerShell で更新日時を指定日時に変更(基本)
    1. 2-1. 更新日時(LastWriteTime)を指定日時に変更
    2. 2-2. 3種類のタイムスタンプをまとめて変更
    3. 2-3. 変更前後のタイムスタンプを確認して変更
  3. 3. copy /b で更新日時を現在日時にリセット
    1. 3-1. 単一ファイルを現在日時に更新
    2. 3-2. 複数ファイルを for /r で再帰的に現在日時にリセット
  4. 4. 複数ファイルを一括で任意の日時に変更
    1. 4-1. フォルダ直下のファイルを一括変更
    2. 4-2. 拡張子を指定して一括変更
    3. 4-3. サブフォルダ再帰で全ファイルを一括変更
  5. 5. 現在日時を基準にした相対的な日時変更
    1. 5-1. 現在日時の N 日前に変更
    2. 5-2. 現在日時の N 日後・N 時間後に変更
    3. 5-3. 相対変更を一括適用(フォルダ内の全ファイルを 30 日前に)
  6. 6. ファイルリストに基づいて個別に日時変更
  7. 7. 落とし穴5選と対策
    1. 落とし穴1:PowerShell の引用符(シングル/ダブル)が混在してエラーになる
    2. 落とし穴2:copy /b は LastWriteTime しか更新しない
    3. 落とし穴3:読み取り専用ファイルはタイムスタンプを変更できない
    4. 落とし穴4:setlocal enabledelayedexpansion 内で PowerShell に !VAR! を渡すと ! が消える
    5. 落とし穴5:ファイルが他のプロセスで使用中はタイムスタンプを変更できない
  8. 8. 実践例3本
    1. 実践例1:forfiles テスト用にファイルを N 日前に設定(テストデータ準備)
    2. 実践例2:バックアップ先のファイルのタイムスタンプをソースと同期
    3. 実践例3:ファイル一覧CSVに更新日時を記録してから日時を変更、変更後の状態をCSVに追記
  9. まとめ:使い分け早見表
  10. FAQ

1. Windowsファイルのタイムスタンプ3種類

Windows のファイルには3種類のタイムスタンプがあります。

プロパティ名 日本語名 説明
LastWriteTime 更新日時 ファイルの内容を最後に変更した日時。エクスプローラーで「更新日時」として表示される
CreationTime 作成日時 ファイルが作成された日時。コピー/移動先では変わる場合がある
LastAccessTime アクセス日時 最後に読み取りまたはアクセスした日時。Windows の設定によっては更新されない
forfiles の日付フィルタは LastWriteTime を参照する
forfiles /d -30 の日付判定は LastWriteTime(更新日時)が基準です。テスト時に意図した日付フィルタを効かせたい場合は、LastWriteTime を変更します。forfiles の詳細は 指定期間以前のファイルを自動削除する方法完全ガイド を参照してください。

2. PowerShell で更新日時を指定日時に変更(基本)

バッチファイルから PowerShell を呼び出してファイルのタイムスタンプを変更する方法が最も汎用的です。

2-1. 更新日時(LastWriteTime)を指定日時に変更

@echo off

:: 対象ファイルと新しい日時を設定
set "FILE=C:\work\report.txt"
set "NEWDT=2024-01-15 09:30:00"

:: PowerShell で LastWriteTime を変更
powershell -NoProfile -Command "(Get-Item '%FILE%').LastWriteTime = [datetime]::Parse('%NEWDT%')"

if %ERRORLEVEL% equ 0 (
    echo [OK] 更新日時を変更しました: %FILE%
) else (
    echo [ERROR] 変更に失敗しました
)

2-2. 3種類のタイムスタンプをまとめて変更

@echo off

set "FILE=C:\work\report.txt"
set "NEWDT=2024-01-15 09:30:00"

:: 作成日時・更新日時・アクセス日時を一括変更
powershell -NoProfile -Command "
    $f = Get-Item '%FILE%';
    $dt = [datetime]::Parse('%NEWDT%');
    $f.CreationTime   = $dt;
    $f.LastWriteTime  = $dt;
    $f.LastAccessTime = $dt
"

echo 完了: %FILE% のタイムスタンプを %NEWDT% に変更しました

2-3. 変更前後のタイムスタンプを確認して変更

@echo off

set "FILE=C:\work\report.txt"
set "NEWDT=2024-06-01 00:00:00"

:: 変更前を表示
echo [変更前]
powershell -NoProfile -Command "Get-Item '%FILE%' | Select-Object Name, LastWriteTime, CreationTime"

:: 変更実行
powershell -NoProfile -Command "(Get-Item '%FILE%').LastWriteTime = [datetime]::Parse('%NEWDT%')"

:: 変更後を表示
echo [変更後]
powershell -NoProfile -Command "Get-Item '%FILE%' | Select-Object Name, LastWriteTime, CreationTime"

3. copy /b で更新日時を現在日時にリセット

copy /b ファイル +NUL ファイル は「ファイルにNULを追記してサイズゼロ変更」という操作をすることで、ファイルの内容を変えずに LastWriteTime を現在日時に更新する昔ながらの手法です。

3-1. 単一ファイルを現在日時に更新

:: copy /b でファイルの更新日時を現在日時にリセット(内容は変わらない)
copy /b "C:\work\report.txt" +NUL "C:\work\report.txt" >nul
echo 更新日時をリセットしました

:: 複数ファイルを一括でリセット(for ループで1件ずつ処理)
for %%F in ("C:\work\*.txt") do copy /b "%%~fF" +NUL "%%~fF" >nul

:: カレントフォルダのすべてのファイル
for %%F in (*) do copy /b "%%~fF" +NUL "%%~fF" >nul
copy /b の動作の仕組み
copy /b file +NUL file はバイナリモードで「file と NUL(空)を結合して file に上書き」という操作を行います。NUL は長さ0のため内容は変わりませんが、書き込みが発生するため LastWriteTime が現在日時に更新されます。任意の日時への変更は PowerShell を使ってください。

3-2. 複数ファイルを for /r で再帰的に現在日時にリセット

@echo off
setlocal enabledelayedexpansion

set "TARGET=C:\work"
set "COUNT=0"

for /r "%TARGET%" %%F in (*) do (
    copy /b "%%~fF" +NUL "%%~fF" >nul 2>&1
    if !ERRORLEVEL! equ 0 (
        set /a COUNT+=1
    ) else (
        echo [ERROR] %%~nxF
    )
)
echo 完了: !COUNT! ファイルの更新日時をリセットしました
endlocal

4. 複数ファイルを一括で任意の日時に変更

特定フォルダ内のファイルをすべて指定日時に変更するパターンです。

4-1. フォルダ直下のファイルを一括変更

@echo off

set "TARGET=C:\work"
set "NEWDT=2024-01-01 00:00:00"

:: PowerShell で一括変更(Get-ChildItem を使うと高速)
powershell -NoProfile -Command "
    Get-ChildItem -Path '%TARGET%' -File |
    ForEach-Object { $_.LastWriteTime = [datetime]::Parse('%NEWDT%') }
"

echo 完了: %TARGET% 内の全ファイルを %NEWDT% に変更しました

4-2. 拡張子を指定して一括変更

@echo off

set "TARGET=C:\work"
set "EXT=*.log"
set "NEWDT=2023-12-31 23:59:59"

:: .log ファイルのみ更新日時を変更
powershell -NoProfile -Command "
    Get-ChildItem -Path '%TARGET%' -Filter '%EXT%' -File |
    ForEach-Object { $_.LastWriteTime = [datetime]::Parse('%NEWDT%') }
"

echo 完了: %TARGET% の %EXT% を %NEWDT% に変更しました

4-3. サブフォルダ再帰で全ファイルを一括変更

@echo off

set "TARGET=C:\work"
set "NEWDT=2024-01-01 00:00:00"

:: -Recurse でサブフォルダも含めて全ファイルを一括変更
powershell -NoProfile -Command "
    Get-ChildItem -Path '%TARGET%' -Recurse -File |
    ForEach-Object { $_.LastWriteTime = [datetime]::Parse('%NEWDT%') }
"

echo 完了: %TARGET% 以下の全ファイルを %NEWDT% に変更しました

5. 現在日時を基準にした相対的な日時変更

「N日前」「N時間後」といった相対的な日時に変更するパターンです。テスト環境の整備や日付フィルタのテストに役立ちます。

5-1. 現在日時の N 日前に変更

@echo off

set "FILE=C:\work\test.log"
set "DAYS=30"

:: 現在日時から %DAYS% 日前の日時に変更
powershell -NoProfile -Command "
    (Get-Item '%FILE%').LastWriteTime = (Get-Date).AddDays(-%DAYS%)
"

echo 完了: %FILE% の更新日時を %DAYS% 日前に変更しました

5-2. 現在日時の N 日後・N 時間後に変更

@echo off

set "FILE=C:\work\test.log"

:: 7日後に変更(AddDays に正の値)
powershell -NoProfile -Command "(Get-Item '%FILE%').LastWriteTime = (Get-Date).AddDays(7)"

:: 2時間前に変更(AddHours に負の値)
powershell -NoProfile -Command "(Get-Item '%FILE%').LastWriteTime = (Get-Date).AddHours(-2)"

:: 現在日時の深夜0時(00:00:00)に変更
powershell -NoProfile -Command "
    $today = (Get-Date).Date;
    (Get-Item '%FILE%').LastWriteTime = $today
"

5-3. 相対変更を一括適用(フォルダ内の全ファイルを 30 日前に)

@echo off

set "TARGET=C:\work"
set "DAYS=30"

powershell -NoProfile -Command "
    $dt = (Get-Date).AddDays(-%DAYS%);
    Get-ChildItem -Path '%TARGET%' -Recurse -File |
    ForEach-Object { $_.LastWriteTime = $dt }
"

echo 完了: %TARGET% 以下の全ファイルを %DAYS% 日前に変更しました

日付の取得・操作については 日付と時間をファイル名に挿入する方法完全ガイド も参照してください。

6. ファイルリストに基づいて個別に日時変更

CSVや設定ファイルに書かれた「ファイルパス, 変更日時」のリストを読み込んで一括処理するパターンです。

@echo off
setlocal enabledelayedexpansion

:: timestamp_list.csv の形式:
:: C:\work\file1.txt,2024-01-15 09:00:00
:: C:\work\file2.log,2024-02-20 18:30:00

set "LIST=C:\work\timestamp_list.csv"
set "COUNT=0"

for /f "usebackq tokens=1,2 delims=," %%A in ("%LIST%") do (
    set "FPATH=%%A"
    set "NEWDT=%%B"

    if exist "!FPATH!" (
        powershell -NoProfile -Command "(Get-Item '!FPATH!').LastWriteTime = [datetime]::Parse('!NEWDT!')"
        if !ERRORLEVEL! equ 0 (
            echo [OK] !FPATH! -> !NEWDT!
            set /a COUNT+=1
        ) else (
            echo [ERROR] !FPATH!
        )
    ) else (
        echo [SKIP] ファイルなし: !FPATH!
    )
)

echo ===== 完了: !COUNT! 件変更 =====
endlocal

7. 落とし穴5選と対策

落とし穴1:PowerShell の引用符(シングル/ダブル)が混在してエラーになる

:: NG: ファイルパスにスペースがあると外側のダブルクォートと干渉する
powershell -Command "(Get-Item "C:\My Work\file.txt").LastWriteTime = ..."

:: OK: PowerShell コマンド内はシングルクォート、
::     バッチ変数は外側でダブルクォートを使って分離する
set "FILE=C:\My Work\file.txt"
powershell -NoProfile -Command "(Get-Item '%FILE%').LastWriteTime = [datetime]::Parse('2024-01-01 00:00:00')"

落とし穴2:copy /b は LastWriteTime しか更新しない

:: copy /b で更新されるのは LastWriteTime のみ
:: CreationTime や LastAccessTime は変わらない
copy /b "C:\work\file.txt" +NUL "C:\work\file.txt" >nul

:: CreationTime も変えたい場合は PowerShell を使う
powershell -NoProfile -Command "
    $f = Get-Item 'C:\work\file.txt';
    $dt = Get-Date;
    $f.CreationTime = $dt;
    $f.LastWriteTime = $dt
"

落とし穴3:読み取り専用ファイルはタイムスタンプを変更できない

:: NG: 読み取り専用属性があると変更に失敗する
powershell -NoProfile -Command "(Get-Item 'C:\work\readonly.txt').LastWriteTime = Get-Date"
:: → エラー: プロパティを設定できません

:: OK: 属性を一時的に解除してから変更
@echo off
set "FILE=C:\work\readonly.txt"
attrib -r "%FILE%"
powershell -NoProfile -Command "(Get-Item '%FILE%').LastWriteTime = [datetime]::Parse('2024-01-01 00:00:00')"
attrib +r "%FILE%"
echo 完了(読み取り専用属性を元に戻しました)

落とし穴4:setlocal enabledelayedexpansion 内で PowerShell に !VAR! を渡すと ! が消える

:: NG: 遅延展開が有効な場合、! が PowerShell コマンドに渡せない
@echo off
setlocal enabledelayedexpansion
set "FILE=C:\work\file.txt"
set "NEWDT=2024-01-01 00:00:00"
powershell -NoProfile -Command "(Get-Item '!FILE!').LastWriteTime = [datetime]::Parse('!NEWDT!')"
:: → 遅延展開変数 !FILE! の ! が PowerShell に渡る前に消えてエラーになる場合がある

:: OK: 一時変数を使って setlocal endlocal をサンドイッチにするか、
::     PowerShell スクリプトファイル(.ps1)に分離して引数で渡す
set "CMD_FILE=!FILE!"
set "CMD_DT=!NEWDT!"
endlocal & powershell -NoProfile -Command "(Get-Item '%CMD_FILE%').LastWriteTime = [datetime]::Parse('%CMD_DT%')"

落とし穴5:ファイルが他のプロセスで使用中はタイムスタンプを変更できない

:: NG: ファイルが他プロセスで排他ロックされている場合はエラーになる

:: OK: エラーを ERRORLEVEL でチェックしてリトライするか、ロック解放を待つ
@echo off
setlocal enabledelayedexpansion
set "FILE=C:\work\active.log"
set "RETRY=3"

:retry_loop
powershell -NoProfile -Command "(Get-Item '%FILE%').LastWriteTime = [datetime]::Parse('2024-01-01 00:00:00')" 2>nul
if !ERRORLEVEL! equ 0 (
    echo [OK] タイムスタンプ変更完了
    goto :done
)
set /a RETRY-=1
if !RETRY! gtr 0 (
    echo [WAIT] ロック中、5秒後にリトライ...
    timeout /t 5 /nobreak >nul
    goto :retry_loop
)
echo [ERROR] 変更に失敗しました
:done
endlocal

ERRORLEVELによるエラーハンドリングは ERRORLEVELを使ったエラーハンドリング完全ガイド を参照してください。

8. 実践例3本

実践例1:forfiles テスト用にファイルを N 日前に設定(テストデータ準備)

古いファイルの自動削除スクリプトをテストするため、指定フォルダのファイルを任意の日数前の日時に一括設定するパターンです。

@echo off
setlocal enabledelayedexpansion

set "TARGET=C:\test\logs"
set /p "DAYS=何日前に設定しますか? (例: 30): "

if "!DAYS!"=="" set "DAYS=30"

:: 入力値が数字かチェック
echo !DAYS!| findstr /r "^[0-9][0-9]*$" >nul
if !ERRORLEVEL! neq 0 (
    echo [ERROR] 数値を入力してください
    exit /b 1
)

echo [INFO] %TARGET% 内の全ファイルを !DAYS! 日前に設定します...

powershell -NoProfile -Command "
    $dt = (Get-Date).AddDays(-!DAYS!);
    Get-ChildItem -Path '%TARGET%' -Recurse -File |
    ForEach-Object { $_.LastWriteTime = $dt };
    Write-Host ('完了: ' + (Get-ChildItem -Path '%TARGET%' -Recurse -File).Count + ' ファイル')
"

echo.
echo [TEST] forfiles /p "%TARGET%" /d -!DAYS! で確認できます
endlocal

ユーザー入力受け取りの詳細は バッチファイルでユーザー入力を受け取る方法完全ガイド を参照してください。

実践例2:バックアップ先のファイルのタイムスタンプをソースと同期

バックアップコピー後にタイムスタンプがズレた場合に、ソース側のタイムスタンプをバックアップ側に合わせるパターンです。

@echo off
setlocal enabledelayedexpansion

set "SRC=C:\work"
set "DST=D:\backup\work"
set "COUNT=0"

for /r "%SRC%" %%F in (*) do (
    set "SFILE=%%~fF"
    :: SRC のパスを DST のパスに置換
    set "DFILE=!SFILE:%SRC%=%DST%!"

    if exist "!DFILE!" (
        :: ソースの LastWriteTime を取得してバックアップ側に設定
        powershell -NoProfile -Command "
            $src = Get-Item '!SFILE!';
            $dst = Get-Item '!DFILE!';
            $dst.LastWriteTime = $src.LastWriteTime
        " 2>nul
        if !ERRORLEVEL! equ 0 set /a COUNT+=1
    )
)

echo ===== タイムスタンプ同期完了: !COUNT! ファイル =====
endlocal

実践例3:ファイル一覧CSVに更新日時を記録してから日時を変更、変更後の状態をCSVに追記

変更前のタイムスタンプを記録しておき、後で元に戻せるようにするパターンです。

@echo off
setlocal enabledelayedexpansion

set "TARGET=C:\work"
set "NEWDT=2024-04-01 00:00:00"
set "LOG=C:\work\timestamp_backup.csv"

:: 変更前のタイムスタンプをCSVに保存
echo ファイル名,変更前_LastWriteTime,変更後_LastWriteTime > "%LOG%"

powershell -NoProfile -Command "
    $newdt = [datetime]::Parse('%NEWDT%');
    Get-ChildItem -Path '%TARGET%' -File | ForEach-Object {
        $before = $_.LastWriteTime;
        $_.LastWriteTime = $newdt;
        $after = $_.LastWriteTime;
        ($_.Name + ',' + $before.ToString('yyyy/MM/dd HH:mm:ss') + ',' + $after.ToString('yyyy/MM/dd HH:mm:ss'))
    }
" >> "%LOG%"

echo [OK] 変更完了。変更前記録: %LOG%
echo 元に戻す場合は CSV の「変更前_LastWriteTime」列を使って再設定できます。
endlocal

ファイル一覧取得の詳細は バッチファイルでファイル一覧を取得する方法完全ガイド を参照してください。

まとめ:使い分け早見表

やりたいこと 方法 ポイント
更新日時を任意の日時に変更 PowerShell $f.LastWriteTime = [datetime]::Parse(...) 最も汎用的
更新日時を現在日時にリセット copy /b file +NUL file コマンドのみ・シンプル
3種のタイムスタンプを一括変更 PowerShell で CreationTime/LastWriteTime/LastAccessTime を代入 一度に3つ変更可能
フォルダ内を一括変更 PowerShell Get-ChildItem | ForEach-Object Recurse でサブフォルダも対応
N日前/N日後に相対変更 PowerShell (Get-Date).AddDays(-N) テスト環境準備に最適
読み取り専用ファイルの変更 attrib -r 後に変更、attrib +r で戻す 属性解除が必要
CSVリストに基づいて個別変更 for /f で CSV 読み込み + PowerShell 一括・個別対応の柔軟パターン

setlocal enabledelayedexpansion 完全ガイド も合わせて参照してください。

FAQ

Q. copy /b で更新日時をリセットしようとしたらファイルサイズが 0 になりました。
A. copy /b "file.txt" +NUL "file.txt" のように元のファイル名を出力先にも指定しているか確認してください。出力先を省略すると NUL(空)のみが書き込まれてサイズが 0 になります。また読み取り専用ファイルや使用中ファイルでは copy /b が失敗するため PowerShell を使ってください。
Q. PowerShell のコマンドでエラーが出て変更できません。
A. 主な原因は①ファイルが読み取り専用(attrib -r で解除)、②他プロセスが排他ロック中(ファイルを閉じる)、③日時のフォーマットが不正(yyyy-MM-dd HH:mm:ss 形式で指定)、④PowerShell の実行ポリシー制限(-NoProfile を付けて実行)、の4つです。
Q. 作成日時(CreationTime)だけを変更したい。
A. powershell -NoProfile -Command "(Get-Item 'C:\work\file.txt').CreationTime = [datetime]::Parse('2024-01-01 00:00:00')" で変更できます。3種類のタイムスタンプはそれぞれ独立して変更できます。
Q. タイムスタンプ変更後に元に戻す方法はありますか?
A. 変更前にCSVへ記録しておくのが確実です(実践例3参照)。または robocopy の /COPY:T オプションでタイムスタンプを保持したままコピーすることで、オリジナルのタイムスタンプを持つコピーを事前に作成しておく方法もあります。
Q. forfiles で古いファイルが検出されません。LastWriteTime を変更したはずなのに。
A. forfiles の /d オプションは LastWriteTime(更新日時)を参照します。powershell -Command "Get-Item 'ファイルパス' | Select LastWriteTime" で実際に変更されているか確認してください。また日付が正しく変更されているか、/d -30 の 30 日前より古い日時になっているか確認します。
Q. バッチファイル内で PowerShell を呼ぶと遅いです。
A. PowerShell は起動オーバーヘッドがあるため、ファイルごとに別々に呼び出すと遅くなります。Get-ChildItem | ForEach-Object { $_.LastWriteTime = ... } のように1回の PowerShell 呼び出しで複数ファイルをまとめて処理するか、処理対象リストを PowerShell スクリプトファイル(.ps1)に渡す方式にするとパフォーマンスが向上します。