batで処理時間・経過時間を測定するなら、基本は開始時刻と終了時刻の %time% を取得し、時・分・秒・センチ秒を数値化して差分を計算します。日またぎを考慮する場合は、差分が負になったときに1日分のセンチ秒を加算します。
この記事では、まず最小テンプレートと使い分けを早見表で整理し、そのあとに%time% の文字列変換、日またぎ、PowerShellのミリ秒計測、複数区間のラップタイム、ログ出力まで実務向けに解説します。
%time% をセンチ秒に変換、ミリ秒精度が必要ならPowerShellの Stopwatch、処理ログに残すなら開始・終了・経過秒を同じログへ書き出します。処理時間測定の書き方早見表
| 目的 | 方法 | 向いている場面 |
|---|---|---|
| ざっくり秒単位で測る | %time% の差分 |
通常のbat処理 |
| 1/100秒単位で測る | センチ秒へ変換 | 外部コマンドなしで計測 |
| 日またぎに対応する | 負の差分に1日分を加算 | 深夜実行・長時間処理 |
| ミリ秒精度で測る | PowerShell Stopwatch |
短時間処理・性能測定 |
| 区間ごとに測る | ラップタイムを記録 | ボトルネック調査 |
| ログに残す | 開始・終了・経過秒を出力 | タスクスケジューラ運用 |
@echo off setlocal enabledelayedexpansion call :to_cs "%time%" START_CS rem ===== 測定したい処理 ===== timeout /t 3 /nobreak >nul call :to_cs "%time%" END_CS set /a ELAPSED=END_CS-START_CS if !ELAPSED! lss 0 set /a ELAPSED+=8640000 set /a SEC=ELAPSED/100 set /a CS=ELAPSED%%100 echo 経過時間: !SEC!.!CS! 秒 exit /b :to_cs set "T=%~1" set "T=0%T: =%" set /a H=1%T:~0,2%-100 set /a M=1%T:~3,2%-100 set /a S=1%T:~6,2%-100 set /a C=1%T:~9,2%-100 set /a %~2=((H*60+M)*60+S)*100+C exit /b
目的別ショートカット
| やりたいこと | 読む場所 | 見るポイント |
|---|---|---|
| まず使えるコードが欲しい | 書き方早見表 | %time% 差分 |
%time% の形式を知りたい |
%time%の基本 | HH:MM:SS.cc |
| 開始から終了までを測りたい | 基本パターン | 開始時刻・終了時刻 |
| 日またぎで負の値になる | 日またぎ対処 | 1日分を加算 |
| ミリ秒精度で測りたい | PowerShell Stopwatch | ElapsedMilliseconds |
| 処理区間ごとに測りたい | ラップタイム | 複数区間の記録 |
| ログへ残したい | 実用テンプレート | 開始・終了・経過秒 |
%time%の基本構造と数値変換
%time%のフォーマット
%time%は現在時刻を文字列で返します。フォーマットはロケールによって異なりますが、日本語Windowsでは以下の形式です。
| フォーマット | 例 | 注意点 |
|---|---|---|
H:MM:SS.cc |
9:05:01.23 |
時が1桁のとき先頭がスペース(例: 9:05:01.23) |
HH:MM:SS.cc |
14:30:00.00 |
時が2桁のときは通常表示 |
各部分は文字列スライスで取り出せます。
@echo off rem %time% の例: " 9:05:01.23" または "14:30:00.00" rem 位置: 0123456789 01 set "HH=%time:~0,2%" rem 時(先頭スペースの可能性あり) set "MM=%time:~3,2%" rem 分 set "SS=%time:~6,2%" rem 秒 set "CC=%time:~9,2%" rem センチ秒(1/100秒) echo 時: [%HH%] 分: [%MM%] 秒: [%SS%] センチ秒: [%CC%]
時: [ 9] 分: [05] 秒: [01] センチ秒: [23] 時: [14] 分: [30] 秒: [00] センチ秒: [00]
センチ秒への変換と差分計算の仕組み
時刻の差分を計算するため、時刻全体をセンチ秒(1/100秒)単位の整数に変換します。センチ秒はset /aで整数演算できるので扱いやすい単位です。
| 単位 | 計算式 | 1日の最大値 |
|---|---|---|
| センチ秒(cc) | HH×360000 + MM×6000 + SS×100 + CC | 8,640,000 cs |
| ミリ秒(ms) | センチ秒 × 10(精度は10ms) | 86,400,000 ms |
%time%の最小単位は100分の1秒(10ms相当)です。1ms単位が必要な場合はPowerShellのStopwatchを使います(本記事後半で解説)。基本パターン:開始から終了までの経過時間を計測する
@echo off setlocal enabledelayedexpansion :: ===== 開始時刻を取得 ===== set "T0=%time%" set "H0=%T0:~0,2%" & set "H0=%H0: =0%" set "M0=%T0:~3,2%" set "S0=%T0:~6,2%" set "C0=%T0:~9,2%" set /a "START_CS=H0*360000 + M0*6000 + S0*100 + C0" :: ===== 計測対象の処理 ===== echo 処理中... ping -n 4 127.0.0.1 >nul :: ===== 終了時刻を取得 ===== set "T1=%time%" set "H1=%T1:~0,2%" & set "H1=%H1: =0%" set "M1=%T1:~3,2%" set "S1=%T1:~6,2%" set "C1=%T1:~9,2%" set /a "END_CS=H1*360000 + M1*6000 + S1*100 + C1" :: ===== 差分計算 ===== set /a "DIFF_CS=END_CS - START_CS" :: 日をまたいだ場合の補正(後述) if !DIFF_CS! lss 0 set /a "DIFF_CS=DIFF_CS + 8640000" :: ===== 結果を時・分・秒・センチ秒に分解 ===== set /a "EL_H=DIFF_CS / 360000" set /a "EL_M=DIFF_CS %% 360000 / 6000" set /a "EL_S=DIFF_CS %% 6000 / 100" set /a "EL_C=DIFF_CS %% 100" echo 所要時間: %EL_H%時間 %EL_M%分 %EL_S%秒 %EL_C%センチ秒
処理中... 所要時間: 0時間 0分 3秒 1センチ秒
set "H0=%H0: =0%"は時が1桁のときにスペースを0に置換する処理です。これがないとset /a " 9*360000"のように計算が失敗します。シンプルに使える計測関数パターン
開始・終了の計測コードを毎回書くのは冗長です。サブルーチンとしてまとめると再利用しやすくなります。
@echo off setlocal enabledelayedexpansion :: 計測開始 call :TIME_START :: ===== 計測対象の処理 ===== echo 処理A実行中... ping -n 3 127.0.0.1 >nul echo 処理A完了 :: 計測終了・表示 call :TIME_END "処理A" exit /b 0 ::-------------------------------------------- :TIME_START set "T_S=%time%" set "T_SH=%T_S:~0,2%" & set "T_SH=%T_SH: =0%" set /a "_TS=T_SH*360000 + 1%T_S:~3,2%*60 - 6000 + 1%T_S:~6,2% - 100 + 1%T_S:~9,2% - 100" exit /b :TIME_END set "T_E=%time%" set "T_EH=%T_E:~0,2%" & set "T_EH=%T_EH: =0%" set /a "_TE=T_EH*360000 + 1%T_E:~3,2%*60 - 6000 + 1%T_E:~6,2% - 100 + 1%T_E:~9,2% - 100" set /a "_TD=_TE - _TS" if !_TD! lss 0 set /a "_TD=_TD + 8640000" set /a "_TDS=_TD / 100, _TDC=_TD %% 100" echo [TIME] %~1: %_TDS%.%_TDC% 秒 exit /b
処理A実行中... 処理A完了 [TIME] 処理A: 2.01 秒
1%T_S:~3,2%*60 - 6000というパターンは「先頭に1をつけて3桁にしてから演算」するテクニックです。05という文字列は105になり、そこから100を引くと正しい5が得られます。これにより先頭ゼロによる8進数解釈を回避しています。日をまたいだ場合の対処
処理が23:59台に開始して翌日0:00以降に終了すると、差分がマイナスになります。この場合は1日分のセンチ秒(8,640,000)を加算して補正します。
:: 差分がマイナスなら1日分(8,640,000 cs = 24時間)を加算 set /a "DIFF_CS=END_CS - START_CS" if %DIFF_CS% lss 0 set /a "DIFF_CS=DIFF_CS + 8640000" rem 例: 開始 23:59:58.00 → 終了 00:00:03.50 rem END_CS = 350 cs rem START_CS= 8639800 cs rem 差分 = 350 - 8639800 = -8639450 (マイナス) rem 補正後 = -8639450 + 8640000 = 550 cs = 5.50 秒 ✓
%DATE%も組み合わせるか、後述のPowerShell方式を使ってください。ミリ秒精度が必要な場合:PowerShell Stopwatchを使う
%time%の精度は10ms単位(センチ秒)が限界です。1ms単位での計測が必要な場合は、PowerShellの[System.Diagnostics.Stopwatch]クラスを活用します。
@echo off setlocal enabledelayedexpansion :: PowerShell Stopwatch で開始 for /f "usebackq delims=" %%A in (`powershell -NoProfile -Command "[System.Diagnostics.Stopwatch]::GetTimestamp()"`) do set "TS_START=%%A" for /f "usebackq delims=" %%A in (`powershell -NoProfile -Command "[System.Diagnostics.Stopwatch]::Frequency"`) do set "TS_FREQ=%%A" :: ===== 計測対象の処理 ===== echo 処理中... ping -n 2 127.0.0.1 >nul :: PowerShell で終了時刻を取得し経過ミリ秒を計算 for /f "usebackq delims=" %%A in (`powershell -NoProfile -Command ^"[math]::Round(([System.Diagnostics.Stopwatch]::GetTimestamp() - %TS_START%) * 1000 / %TS_FREQ%)^"`) do set "ELAPSED_MS=%%A" echo 所要時間: %ELAPSED_MS% ms
より簡潔に書くなら、PowerShellブロック全体で計測させて結果だけ受け取る方法も使えます。
@echo off
rem PowerShell側で計測・処理・結果出力をすべて行う
for /f "usebackq delims=" %%A in (`powershell -NoProfile -Command ^"
$sw = [System.Diagnostics.Stopwatch]::StartNew();
Start-Sleep -Milliseconds 1500;
$sw.Stop();
Write-Output $sw.ElapsedMilliseconds
^"`) do set "ELAPSED_MS=%%A"
echo 所要時間: %ELAPSED_MS% ms
所要時間: 1502 ms
| 計測方法 | 精度 | 複雑さ | 推奨用途 |
|---|---|---|---|
%time%のみ |
10ms(センチ秒) | 低 | 数秒〜数分レベルの処理計測 |
| PowerShell Stopwatch | 1ms以下 | 中 | 1秒未満の高精度計測が必要な場合 |
複数区間のラップタイムを記録する
処理が複数のフェーズに分かれている場合、各区間の所要時間(ラップタイム)と開始からの累計時間を同時に記録すると、ボトルネックの特定に役立ちます。
@echo off setlocal enabledelayedexpansion :: 計測開始 call :TS _TOTAL_START set "_LAP_PREV=!_TOTAL_START!" :: ===== フェーズ1 ===== echo [Phase 1] データ取得中... ping -n 2 127.0.0.1 >nul call :TS _NOW call :SHOW_LAP "Phase 1" !_LAP_PREV! !_NOW! !_TOTAL_START! set "_LAP_PREV=!_NOW!" :: ===== フェーズ2 ===== echo [Phase 2] データ処理中... ping -n 4 127.0.0.1 >nul call :TS _NOW call :SHOW_LAP "Phase 2" !_LAP_PREV! !_NOW! !_TOTAL_START! set "_LAP_PREV=!_NOW!" :: ===== フェーズ3 ===== echo [Phase 3] 結果出力中... ping -n 2 127.0.0.1 >nul call :TS _NOW call :SHOW_LAP "Phase 3" !_LAP_PREV! !_NOW! !_TOTAL_START! exit /b 0 :TS varname set "_T=%time%" set "_TH=%_T:~0,2%" & set "_TH=%_TH: =0%" set /a "%~1=_TH*360000 + 1%_T:~3,2%*60 - 6000 + 1%_T:~6,2% - 100 + 1%_T:~9,2% - 100" exit /b :SHOW_LAP label prev_cs now_cs total_start_cs set /a "_lap=%~3 - %~2" if !_lap! lss 0 set /a "_lap=!_lap! + 8640000" set /a "_cum=%~3 - %~4" if !_cum! lss 0 set /a "_cum=!_cum! + 8640000" set /a "_ls=!_lap!/100, _lc=!_lap! %% 100" set /a "_cs=!_cum!/100, _cc=!_cum! %% 100" echo [LAP] %~1: !_ls!.!_lc! 秒 (累計: !_cs!.!_cc! 秒) exit /b
[Phase 1] データ取得中... [LAP] "Phase 1": 1.00 秒 (累計: 1.00 秒) [Phase 2] データ処理中... [LAP] "Phase 2": 3.01 秒 (累計: 4.01 秒) [Phase 3] 結果出力中... [LAP] "Phase 3": 1.00 秒 (累計: 5.01 秒)
ログファイルへの出力を含む実用テンプレート
定期実行バッチでは所要時間をログファイルに記録しておくと、処理時間の変化(増加傾向など)を後から確認できます。
@echo off setlocal enabledelayedexpansion set "LOG=C:\logs\job_timing.log" set "JOB_NAME=DailyDataExport" if not exist "C:\logs" mkdir "C:\logs" :: 開始 set "T0=%time%" set "_H=%T0:~0,2%" & set "_H=%_H: =0%" set /a "_TS=_H*360000 + 1%T0:~3,2%*60 - 6000 + 1%T0:~6,2% - 100 + 1%T0:~9,2% - 100" echo %DATE% %T0% [START] %JOB_NAME% >> "%LOG%" :: ===== メイン処理 ===== echo 処理実行中... ping -n 5 127.0.0.1 >nul set "PROC_STATUS=SUCCESS" if %ERRORLEVEL% neq 0 set "PROC_STATUS=FAILED" :: 終了 set "T1=%time%" set "_H=%T1:~0,2%" & set "_H=%_H: =0%" set /a "_TE=_H*360000 + 1%T1:~3,2%*60 - 6000 + 1%T1:~6,2% - 100 + 1%T1:~9,2% - 100" set /a "_TD=_TE - _TS" if !_TD! lss 0 set /a "_TD=!_TD! + 8640000" set /a "_S=!_TD!/100, _C=!_TD! %% 100" echo %DATE% %T1% [END] %JOB_NAME% - %PROC_STATUS% - %_S%.%_C% sec >> "%LOG%" echo 所要時間: %_S%.%_C% 秒 [%PROC_STATUS%]
2026/03/21 2:00:00.05 [START] DailyDataExport 2026/03/21 2:00:04.27 [END] DailyDataExport - SUCCESS - 4.22 sec 2026/03/22 2:00:00.10 [START] DailyDataExport 2026/03/22 2:00:05.88 [END] DailyDataExport - SUCCESS - 5.78 sec
日付ファイル名にする方法はバッチファイルで日付・時刻をファイル名に挿入する方法を参考にしてください。より詳細なログ出力の実装はバッチファイルでログ出力する方法完全ガイドで解説しています。
よくある落とし穴と対処法
落とし穴①:先頭スペースで計算がずれる
時が1桁(0〜9時)のとき%time%の先頭はスペース( )になります。スペースをそのままset /aに渡すと計算結果がおかしくなります。
set "HH=%time:~0,2%" rem HH が " 9" のとき set /a CS=HH*360000 rem エラーまたは誤計算
set "HH=%time:~0,2%" set "HH=%HH: =0%" rem " 9" → "09" set /a CS=HH*360000 rem 正常: 09*360000 = 3240000
落とし穴②:set /aで8進数として解釈されるケース
set /aは先頭に0がつく数値を8進数と解釈します。例えば08や09は8進数として無効になりエラーになります。
set "SS=08" set /a VAL=SS rem エラー: 08 は無効な8進数
set "T=%time%" rem "1%T:~3,2%" で "1MM" の形にして8進数問題を回避 set /a "MM=1%T:~3,2% - 100" rem "105" - 100 = 5、"112" - 100 = 12 set /a "SS=1%T:~6,2% - 100" set /a "CC=1%T:~9,2% - 100"
落とし穴③:ロケールによるフォーマットの違い
英語版Windowsや一部の設定では%time%のフォーマットがHH:MM:SS.ccではなくH:MM:SS.cc AM/PM(12時間表示)になることがあります。海外環境で実行するスクリプトでは注意が必要です。
@echo off
rem PowerShellで24時間形式を強制取得
for /f "usebackq delims=" %%A in (`powershell -NoProfile -Command "[datetime]::Now.ToString('HH:mm:ss.ff')"`) do set "NOW=%%A"
echo 現在時刻(24時間固定): %NOW%
落とし穴④:ループ内でdelayed expansionが必要
forループやifブロック内で時刻変数を更新・参照する場合、setlocal enabledelayedexpansionと!VAR!が必要です。%VAR%だとブロック先頭の値が展開されてしまいます。
@echo off
for /l %%i in (1,1,3) do (
set "T=%time%"
echo %T% rem 3回とも同じ時刻になる
)
@echo off
setlocal enabledelayedexpansion
for /l %%i in (1,1,3) do (
set "T=%time%"
echo !T! rem 都度最新の時刻が表示される
)
あわせて確認したい関連記事
- バッチファイル(bat)の目的別記事一覧
- バッチファイルでログを出力する方法
- schtasksコマンドでタスクスケジューラを制御する方法
- exit /bの使い方|ERRORLEVEL・終了コードを返す方法
- tasklistでプロセス監視|異常終了検知・自動再起動
よくある質問
%time% を取得し、時・分・秒・センチ秒へ分解して差分を計算します。秒単位で十分ならこの方法が最も手軽です。Stopwatch を使ってください。8640000、つまり24時間分のセンチ秒を加算してください。set "T=0%T: =%" のように空白を削除して0を補い、常に2桁として扱うと安定します。[System.Diagnostics.Stopwatch]::StartNew() を使います。batから powershell -NoProfile -Command で呼び出せば、ElapsedMilliseconds を取得できます。exit /b 1 で終了します。外部プロセスを強制終了する場合はPID管理や taskkill も組み合わせます。
