【bat】処理時間・経過時間を測定|%time%・ミリ秒・日またぎ

batで処理時間・経過時間を測定するなら、基本は開始時刻と終了時刻の %time% を取得し、時・分・秒・センチ秒を数値化して差分を計算します。日またぎを考慮する場合は、差分が負になったときに1日分のセンチ秒を加算します。

この記事では、まず最小テンプレートと使い分けを早見表で整理し、そのあとに%time% の文字列変換、日またぎ、PowerShellのミリ秒計測、複数区間のラップタイム、ログ出力まで実務向けに解説します。

先に結論:外部コマンドなしなら %time% をセンチ秒に変換、ミリ秒精度が必要ならPowerShellの Stopwatch、処理ログに残すなら開始・終了・経過秒を同じログへ書き出します。

スポンサーリンク

処理時間測定の書き方早見表

目的 方法 向いている場面
ざっくり秒単位で測る %time% の差分 通常のbat処理
1/100秒単位で測る センチ秒へ変換 外部コマンドなしで計測
日またぎに対応する 負の差分に1日分を加算 深夜実行・長時間処理
ミリ秒精度で測る PowerShell Stopwatch 短時間処理・性能測定
区間ごとに測る ラップタイムを記録 ボトルネック調査
ログに残す 開始・終了・経過秒を出力 タスクスケジューラ運用
%time%で処理時間を測る最小テンプレート
@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桁のときは通常表示

各部分は文字列スライスで取り出せます。

%time%の各部分の取り出し方
@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%の精度はセンチ秒(1/100秒): %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桁の分・秒も正しく計算する方法: 上記の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 秒 ✓
2日以上の処理には対応できない: この補正は最大1日分のオーバーフローにしか対応できません。24時間を超える処理の計測が必要な場合は%DATE%も組み合わせるか、後述のPowerShell方式を使ってください。

ミリ秒精度が必要な場合:PowerShell Stopwatchを使う

%time%の精度は10ms単位(センチ秒)が限界です。1ms単位での計測が必要な場合は、PowerShellの[System.Diagnostics.Stopwatch]クラスを活用します。

PowerShell 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ブロック全体で計測させて結果だけ受け取る方法も使えます。

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に渡すと計算結果がおかしくなります。

NG: 先頭スペースを除去せずに計算
set "HH=%time:~0,2%"
rem HH が " 9" のとき
set /a CS=HH*360000  rem エラーまたは誤計算
OK: スペースを0に置換してから計算
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進数と解釈します。例えば0809は8進数として無効になりエラーになります。

NG: 先頭ゼロ付き数値をそのまま使う
set "SS=08"
set /a VAL=SS   rem エラー: 08 は無効な8進数
OK: 1を先頭に付けてから100を引く(「1プレフィックス法」)
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時間表示)になることがあります。海外環境で実行するスクリプトでは注意が必要です。

ロケールを強制して時刻を取得(PowerShell利用)
@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%だとブロック先頭の値が展開されてしまいます。

NG: %VAR% ではループ内で更新されない
@echo off
for /l %%i in (1,1,3) do (
    set "T=%time%"
    echo %T%   rem 3回とも同じ時刻になる
)
OK: !VAR! で都度展開する
@echo off
setlocal enabledelayedexpansion
for /l %%i in (1,1,3) do (
    set "T=%time%"
    echo !T!   rem 都度最新の時刻が表示される
)

よくある質問

Qbatで処理時間を測定する一番簡単な方法は?
A開始時と終了時の %time% を取得し、時・分・秒・センチ秒へ分解して差分を計算します。秒単位で十分ならこの方法が最も手軽です。
Q%time%の .cc はミリ秒ですか?
Aミリ秒ではなくセンチ秒、つまり1/100秒です。より細かいミリ秒精度が必要な場合はPowerShellの Stopwatch を使ってください。
Q日をまたぐと経過時間が負になります。
A終了時刻のセンチ秒が開始時刻より小さい場合、日付をまたいでいます。差分に 8640000、つまり24時間分のセンチ秒を加算してください。
Q%time%の時刻が1桁のときに計算が崩れます。
A午前9時台などは先頭に空白が入ることがあります。set "T=0%T: =%" のように空白を削除して0を補い、常に2桁として扱うと安定します。
Qミリ秒精度で処理時間を測るには?
APowerShellの [System.Diagnostics.Stopwatch]::StartNew() を使います。batから powershell -NoProfile -Command で呼び出せば、ElapsedMilliseconds を取得できます。
Q複数ステップの処理時間を測れますか?
A測れます。各ステップの開始前と終了後に時刻を取得し、ラップタイムとしてログへ出力します。どの処理が遅いかを切り分けるときに便利です。
Qタスクスケジューラ実行時もログに処理時間を残せますか?
A残せます。開始時刻、終了時刻、経過秒、終了コードを同じログに追記すると、夜間処理や定期ジョブの遅延調査がしやすくなります。
Q長時間処理のタイムアウト判定にも使えますか?
A使えます。ループ内で経過秒を計算し、しきい値を超えたら exit /b 1 で終了します。外部プロセスを強制終了する場合はPID管理や taskkill も組み合わせます。