バッチファイルのログに「いつ実行したか」を残すだけでは不十分なケースがあります。「どのステップで何秒かかったか」「処理全体の経過時間はいくつか」を記録することで、ボトルネックの特定・定期実行の遅延検知・障害時の時系列把握が可能になります。
本記事では %TIME% の書式固定・ステップ別タイムスタンプ・経過時間の計算・wmic による精密取得まで、実践で使える実行時刻ログの手法を体系的に解説します。
- %DATE% %TIME% をログに追記する基本と注意点
- 時刻フォーマットを HH:MM:SS に固定してロケール差異を吸収する方法
- 開始・終了時刻と経過時間をまとめて記録する :log_time サブルーチン
- 経過時間を秒・センチ秒単位で計算するバッチ純正の手法
- 処理ステップごとにタイムスタンプ付きでログ出力するパターン
- wmic でロケール非依存の精密な時刻を取得する方法
時刻取得方法の比較
| 方法 | 精度 | ロケール依存 | 形式の固定 | 主な用途 |
|---|---|---|---|---|
| %TIME% | センチ秒(1/100秒) | あり(地域設定で変化) | 要整形 | 経過時間計算・ほぼすべての用途 |
| %DATE% | 日付のみ | あり | 要整形 | 日付付きタイムスタンプ |
| wmic os get LocalDateTime | 秒 | なし(ISO形式固定) | 不要 | ロケール非依存の厳密な日時 |
| PowerShell Get-Date | ミリ秒 | なし(書式指定可) | 不要 | 高精度・詳細な書式が必要な場合 |
日本語環境では通常
H:MM:SS.cc(時が1桁の場合あり)ですが、英語環境や一部のサーバー環境では異なることがあります。環境を問わず安定して使うには wmic か PowerShell を使う、または %TIME% を整形して固定書式にする処理を挟むのが安全です。基本:ログファイルに実行時刻を追記する
echo %DATE% %TIME% >> logfile.txt が最もシンプルな実装です。追記演算子 >> を使うことで、実行するたびに時刻が蓄積されます。
@echo off setlocal set LOGFILE=C:\logs\batch.log REM ログフォルダがなければ作成 if not exist "C:\logs" mkdir "C:\logs" REM 実行時刻を追記 echo ===== 実行開始: %DATE% %TIME% ===== >> "%LOGFILE%" REM ここに実際の処理を記述 echo 処理1: データ取得 >> "%LOGFILE%" echo 処理2: 変換 >> "%LOGFILE%" echo ===== 実行終了: %DATE% %TIME% ===== >> "%LOGFILE%" endlocal
>> は追記(ログが蓄積される)、> は上書き(毎回リセット)です。ログとして使う場合は必ず >> を使ってください。またログファイルへの出力は echo の直後にスペースなしで >> を続けることで、コンソールには表示せずファイルにのみ出力できます。時刻フォーマットを HH:MM:SS に固定する
%TIME% はロケールや環境によって桁数が変わるため、ログのソートや比較が難しくなることがあります。for /f でパースして HH:MM:SS 形式に固定することで、どの環境でも一貫したタイムスタンプになります。
@echo off setlocal set LOGFILE=C:\logs\batch.log REM %TIME% を HH:MM:SS に整形(先頭スペースを 0 に変換) set T=%TIME: =0% set HH=%T:~0,2% set MM=%T:~3,2% set SS=%T:~6,2% set TIMESTAMP=%HH%:%MM%:%SS% echo [%TIMESTAMP%] バッチ処理開始 >> "%LOGFILE%" echo [%TIMESTAMP%] 対象: C:\data\input >> "%LOGFILE%" REM 処理後の時刻を再取得 set T=%TIME: =0% set HH=%T:~0,2% set MM=%T:~3,2% set SS=%T:~6,2% set TIMESTAMP=%HH%:%MM%:%SS% echo [%TIMESTAMP%] 処理完了 >> "%LOGFILE%" endlocal
%TIME: =0% は変数の文字列置換で、スペースをゼロ (0) に変換します。%TIME% が 9:05:03.21(先頭スペースあり)のとき、09:05:03.21 に変換されるため、桁数が一定になり ~0,2 などの部分取得が正確に動作します。:log_time サブルーチンで再利用可能なタイムスタンプ出力を作る
毎回 %TIME% の整形コードを書くのは冗長です。:log_time サブルーチンにまとめることで、call :log_time "メッセージ" の1行でタイムスタンプ付きのログ出力ができます。
@echo off setlocal set LOGFILE=C:\logs\batch.log if not exist "C:\logs" mkdir "C:\logs" REM サブルーチンを呼び出すだけでタイムスタンプ付きログ出力 call :log_time "処理開始" REM ここに処理を記述 call :log_time "ステップ1: ファイル取得完了" timeout /t 2 >nul call :log_time "ステップ2: データ変換完了" timeout /t 1 >nul call :log_time "処理終了" goto :eof :log_time set T=%TIME: =0% set _TS=%T:~0,2%:%T:~3,2%:%T:~6,2% echo [%_TS%] %~1 >> "%LOGFILE%" echo [%_TS%] %~1 goto :eof
出力例(batch.log):
[09:15:00] 処理開始 [09:15:00] ステップ1: ファイル取得完了 [09:15:02] ステップ2: データ変換完了 [09:15:03] 処理終了
経過時間を計算してログに記録する
処理全体や各ステップの所要時間を秒単位で記録することで、パフォーマンス劣化や異常な遅延を検出できます。バッチ純正の計算のみで経過時間を算出する方法を示します。
@echo off
setlocal enabledelayedexpansion
set LOGFILE=C:\logs\perf.log
if not exist "C:\logs" mkdir "C:\logs"
REM === 処理開始 ===
set START_TIME=%TIME%
call :log_time "処理開始"
REM ---- 実際の処理(例)----
timeout /t 3 >nul
call :log_time "ステップ1完了"
timeout /t 2 >nul
call :log_time "ステップ2完了"
REM === 経過時間を計算して記録 ===
set END_TIME=%TIME%
call :calc_elapsed "%START_TIME%" "%END_TIME%"
call :log_time "処理終了 (経過時間: !ELAPSED_S!.!ELAPSED_CS! 秒)"
goto :eof
:log_time
set T=%TIME: =0%
set _TS=%T:~0,2%:%T:~3,2%:%T:~6,2%
echo [%_TS%] %~1 >> "%LOGFILE%"
echo [%_TS%] %~1
goto :eof
:calc_elapsed
REM 引数: %1=開始時刻(%TIME%) %2=終了時刻(%TIME%)
REM 結果: ELAPSED_S(秒), ELAPSED_CS(センチ秒の端数)をセット
setlocal
set T1=%~1
set T2=%~2
REM 先頭スペースを 0 に変換してから分解
set T1=%T1: =0%
set T2=%T2: =0%
for /f "tokens=1-4 delims=:." %%a in ("%T1%") do (
set /a "s_h=%%a, s_m=%%b, s_s=%%c, s_cs=%%d"
)
for /f "tokens=1-4 delims=:." %%a in ("%T2%") do (
set /a "e_h=%%a, e_m=%%b, e_s=%%c, e_cs=%%d"
)
set /a start_cs = s_h*360000 + s_m*6000 + s_s*100 + s_cs
set /a end_cs = e_h*360000 + e_m*6000 + e_s*100 + e_cs
set /a total_cs = end_cs - start_cs
if %total_cs% lss 0 set /a total_cs += 8640000
set /a _elapsed_s = total_cs / 100
set /a _elapsed_cs = total_cs %% 100
endlocal & set ELAPSED_S=%_elapsed_s% & set ELAPSED_CS=%_elapsed_cs%
goto :eof
%TIME% は
HH:MM:SS.cc 形式で、cc がセンチ秒です。時・分・秒・センチ秒をそれぞれセンチ秒に換算して合計し差し引くことで、外部コマンド不要で経過時間を算出できます。日付またぎ(処理が真夜中にまたがる)には 8640000(1日のセンチ秒数)を加算して対応します。endlocal & set ELAPSED_S=%_elapsed_s% は endlocal 実行後に外側のスコープへ変数を渡すテクニックです。endlocal した後の set は外側のスコープで実行されます。遅延展開を使わず複数の変数を返せるため、サブルーチンの返り値として広く使われます。処理ステップごとに経過時間をタイムスタンプ付きで記録する
大規模なバッチ処理では「どのステップが遅いか」を特定することが重要です。各ステップの開始時刻と経過時間を記録することで、ボトルネックを可視化できます。
@echo off
setlocal enabledelayedexpansion
set LOGFILE=C:\logs\steps.log
if not exist "C:\logs" mkdir "C:\logs"
set TOTAL_START=%TIME%
call :log_step "=== バッチ開始 ==="
REM --- ステップ1 ---
set STEP_START=%TIME%
timeout /t 2 >nul
set STEP_END=%TIME%
call :calc_elapsed "%STEP_START%" "%STEP_END%"
call :log_step "ステップ1: データ取得 完了 (%ELAPSED_S%.%ELAPSED_CS%秒)"
REM --- ステップ2 ---
set STEP_START=%TIME%
timeout /t 4 >nul
set STEP_END=%TIME%
call :calc_elapsed "%STEP_START%" "%STEP_END%"
call :log_step "ステップ2: 変換処理 完了 (%ELAPSED_S%.%ELAPSED_CS%秒)"
REM --- ステップ3 ---
set STEP_START=%TIME%
timeout /t 1 >nul
set STEP_END=%TIME%
call :calc_elapsed "%STEP_START%" "%STEP_END%"
call :log_step "ステップ3: ファイル出力 完了 (%ELAPSED_S%.%ELAPSED_CS%秒)"
REM --- 合計経過時間 ---
call :calc_elapsed "%TOTAL_START%" "%TIME%"
call :log_step "=== バッチ終了 合計: %ELAPSED_S%.%ELAPSED_CS%秒 ==="
goto :eof
:log_step
set T=%TIME: =0%
set _TS=%T:~0,2%:%T:~3,2%:%T:~6,2%
echo [%_TS%] %~1 >> "%LOGFILE%"
echo [%_TS%] %~1
goto :eof
:calc_elapsed
setlocal
set T1=%~1
set T2=%~2
set T1=%T1: =0%
set T2=%T2: =0%
for /f "tokens=1-4 delims=:." %%a in ("%T1%") do set /a "s_h=%%a, s_m=%%b, s_s=%%c, s_cs=%%d"
for /f "tokens=1-4 delims=:." %%a in ("%T2%") do set /a "e_h=%%a, e_m=%%b, e_s=%%c, e_cs=%%d"
set /a start_cs = s_h*360000 + s_m*6000 + s_s*100 + s_cs
set /a end_cs = e_h*360000 + e_m*6000 + e_s*100 + e_cs
set /a total_cs = end_cs - start_cs
if %total_cs% lss 0 set /a total_cs += 8640000
set /a _s = total_cs / 100
set /a _cs = total_cs %% 100
endlocal & set ELAPSED_S=%_s% & set ELAPSED_CS=%_cs%
goto :eof
wmic でロケール非依存の精密な時刻を取得する
wmic の LocalDateTime は YYYYMMDDHHmmss.ffffff+000 形式で返るため、ロケール設定に左右されません。サーバー・複数環境での実行や厳密なタイムスタンプが必要な場合に適しています。
@echo off
setlocal
set LOGFILE=C:\logs\strict.log
if not exist "C:\logs" mkdir "C:\logs"
REM wmic で ISO 形式の日時を取得 (YYYYMMDDHHmmss)
for /f "tokens=2 delims==" %%A in ('wmic os get LocalDateTime /value') do set DT=%%A
REM 年月日・時分秒を取り出す
set YYYY=%DT:~0,4%
set MM=%DT:~4,2%
set DD=%DT:~6,2%
set HH=%DT:~8,2%
set MI=%DT:~10,2%
set SS=%DT:~12,2%
set TIMESTAMP=%YYYY%-%MM%-%DD% %HH%:%MI%:%SS%
echo [%TIMESTAMP%] バッチ実行 >> "%LOGFILE%"
echo タイムスタンプ: %TIMESTAMP%
endlocal
経過時間の計算には %TIME% が向いています(センチ秒単位で計算可能)。一方、ログファイルの先頭タイムスタンプなどロケール非依存の固定書式が必要な場合は wmic を使います。日付付きファイル名の生成は日付と時間をファイル名に挿入する方法も参照してください。
実践: ERRORLEVEL を時刻とともにログに記録する
処理の成否(ERRORLEVEL)と実行時刻をセットで記録することで、「いつ・どこで・どんなエラーが出たか」を後から追跡できます。
@echo off
setlocal
set LOGFILE=C:\logs\result.log
if not exist "C:\logs" mkdir "C:\logs"
call :log_time "=== 実行開始 ==="
REM 外部コマンドを実行
xcopy /e /y "C:\src" "C:\dst" >nul 2>&1
set XCOPY_RC=%ERRORLEVEL%
if %XCOPY_RC% equ 0 (
call :log_time "[OK] xcopy 成功"
) else (
call :log_time "[NG] xcopy 失敗 ERRORLEVEL=%XCOPY_RC%"
)
call :log_time "=== 実行終了 ==="
goto :eof
:log_time
set T=%TIME: =0%
set _TS=%T:~0,2%:%T:~3,2%:%T:~6,2%
echo [%_TS%] %~1 >> "%LOGFILE%"
echo [%_TS%] %~1
goto :eof
ログ全体の設計(リダイレクト・PC情報・ローテーション)はバッチファイルでログを出力する方法完全ガイド、日付別ファイルへの分割保存はログを日付別ファイルに自動保存する完全ガイド、ログの自動削除・世代管理はログローテーション完全ガイドを参照してください。
まとめ
- 基本:
echo %DATE% %TIME% >> log.txtでタイムスタンプを追記 - 書式固定:
set T=%TIME: =0%でスペースをゼロに変換し HH:MM:SS 形式を安定取得 - サブルーチン化:
:log_timeでcall :log_time "msg"の1行でログ出力 - 経過時間計算: 開始・終了時刻をセンチ秒に変換して差分を算出(外部コマンド不要)
- ステップ計測: ステップごとに開始・終了を記録してボトルネックを特定
- ロケール非依存: wmic LocalDateTime で ISO 形式の固定タイムスタンプ
- 成否セット記録: ERRORLEVEL と時刻を同時に出力して障害追跡を容易に
関連記事: バッチファイルでログを出力する完全ガイド / ログを日付別ファイルに自動保存する完全ガイド / 日付と時間をファイル名に挿入する方法
よくある質問(FAQ)
set T=%TIME: =0% を使うとスペースが 0 に変換されて 09:05:03.21 になります。その後 %T:~0,2% で先頭2文字を取得すれば常に2桁の時間が得られます。% で参照する場合、ループ開始時の値が展開されます。ループ内でリアルタイムに %TIME% を取得するにはsetlocal enabledelayedexpansion を宣言して !TIME!(感嘆符)で参照してください。または call :log_time のようにサブルーチン内で %TIME% を参照する方法でも解決できます。if %total_cs% lss 0 set /a total_cs += 8640000(1日 = 86400秒 = 8,640,000センチ秒)を加算することで正しい経過時間になります。chcp 65001 を追記して UTF-8 コードページに切り替える方法もありますが、一部コマンドの出力に影響する場合があります。文字化けの詳細な原因と対策はバッチファイルで日本語が文字化けする完全解説を参照してください。

