I have a batch file which scans the computer taking various readings and storing each in to a variable. During this time I would like a little more than a simple message telling the user to 'Please wait - Gathering system information'.
I've tried the quick, easy, (and poor), way of doing this, as follows, as I want this to be a true incremental progress bar.
cls
echo.
echo.
echo Please wait - Gathering system information
echo ----------------------------------
echo Progress: ░░░░░░░░░░░░░░░░░░░░ 0%%
echo ----------------------------------
>nul timeout /t 1 &cls
cls
REM Do something here.
echo.
echo.
echo Please wait - Gathering system information...
echo ----------------------------------
echo Progress: █░░░░░░░░░░░░░░░░░░░ 5%%
echo ----------------------------------
>nul timeout /t 1 &cls
cls
REM Do something else here.
echo.
echo.
echo Please wait - Gathering system information.
echo ----------------------------------
echo Progress: ██░░░░░░░░░░░░░░░░░░ 10%%
echo ----------------------------------
>nul timeout /t 1 &cls
cls
REM And something else..
echo.
echo.
echo Please wait - Gathering system information..
echo ----------------------------------
echo Progress: ███░░░░░░░░░░░░░░░░░ 15%%
echo ----------------------------------
>nul timeout /t 1 &cls
cls
REM Etc...
However, the effect is not exactly great, as the cls command causes the command window to flicker, and there has to be a 'false' pause after each update, which slows the whole process… Also not great.
I have the following that looks great, but I have no idea how to 'increment' the bar after every section of code, as per my initial example, and having it play from 0-100% before running anything else is a bit pointless.
#echo off &cls
mode con: cols=70 lines=5 &color f0
call :setESC
chcp 65001 >nul
set progress=
set/a progressnum=0
:gettingdata
set progress=%progress%%ESC%[96m█%ESC%[30m
cls
echo.
echo. Please wait - Gathering system information...
echo. %progress% (%progressnum%/20)
ping localhost -n 2 >nul
set/a progressnum=%progressnum% +1
if %progressnum%==20 goto finished
goto gettingdata
:finished
echo.
echo. Finished
echo. %ESC%[92m████████████████████%ESC%[30m (20/20)
echo. Press any key to exit &>nul timeout /t -1 &exit /B
:setESC
REM Define escape char
for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do (
set ESC=%%b
exit /B 0
)
So, my question is, how can I get the effect of the second script, but actually increment the bar after each section of my batch script has executed?
Ok I figured it out (with the help of this post).
Below is how I set up and used the script with only small additions to make it work with Unicode symbols.
First, set up your environment however you please. For the purpose of this post we will use the following:
#echo off &cls
mode con: cols=70 lines=4 &color f0
setlocal
Now in between each segment of your code you'll need to define the percentage of the progress bar by calling :drawProgressBar like so:
REM Your code here (segment 1)
REM - Progress Bar 0% - 33% ------------------------------------------------------
echo. Please wait - Gathering system information...
for /l %%f in (0 1 33) do (
call :drawProgressBar %%f "Code segment 1 finished"
)
REM ------------------------------------------------------------------------------
Repeat as required increasing the percentage each time like so:
REM Your code here (segment 2)
REM - Progress Bar 34% - 66% -----------------------------------------------------
for /l %%f in (34 1 66) do (
call :drawProgressBar %%f "Code segment 2 finished"
)
REM ------------------------------------------------------------------------------
REM Your code here (segment 3)
REM - Progress Bar 67% - 100% ----------------------------------------------------
for /l %%f in (67 1 100) do (
call :drawProgressBar %%f "Code segment 2 finished"
)
REM ------------------------------------------------------------------------------
REM The rest of your script goes here
endlocal
echo. &echo. All done. Press any key to Exit. &>nul timeout /t -1 &exit /B
Addendum: If you would like each segment to show 0-100% (as opposed to counting up from 0-100% overall) then you can use the following each time and simply update the description:
REM - Progress Bar 0% - 100% -----------------------------------------------------
for /l %%f in (0 1 100) do (
call :drawProgressBar %%f "Code segment finished"
)
REM ------------------------------------------------------------------------------
Ok to continue and this is personal preference here, but I then like to resize the CMD Window with another cls &mode con: cols=70 lines=60 &color f0 (or whatever size and/or colour you need) then continue to the output. As I say, this is just personal choice but I think it looks better with the progress meter in a smaller windows while the information is gathered before moving on to the 'main output' of the script.
Finally, add the following to the end of your batch file:
:drawProgressBar value [text]
if "%~1"=="" goto :eof
if not defined pb.barArea call :initProgressBar
setlocal enableextensions enabledelayedexpansion
set /a "pb.value=%~1 %% 101", "pb.filled=pb.value*pb.barArea/100", "pb.dotted=pb.barArea-pb.filled", "pb.pct=1000+pb.value"
set "pb.pct=%pb.pct:~-3%"
if "%~2"=="" ( set "pb.text=" ) else (
set "pb.text=%~2%pb.back%"
set "pb.text=!pb.text:~0,%pb.textArea%!"
)
<nul set /p "pb.prompt= !pb.fill:~0,%pb.filled%!!pb.dots:~0,%pb.dotted%! [%pb.pct%%%] %pb.text%!pb.cr!"
endlocal
goto :eof
:initProgressBar [fillChar] [dotChar]
chcp 65001 >nul
if defined pb.cr call :finalizeProgressBar
for /f %%a in ('copy "%~f0" nul /z') do set "pb.cr=%%a"
if "%~1"=="" ( set "pb.fillChar=▓" ) else ( set "pb.fillChar=%~1" )
if "%~2"=="" ( set "pb.dotChar=▒" ) else ( set "pb.dotChar=%~2" )
set "pb.console.columns="
for /f "tokens=2 skip=4" %%f in ('mode con') do if not defined pb.console.columns set "pb.console.columns=%%f"
set /a "pb.barArea=pb.console.columns/2-2", "pb.textArea=pb.barArea-9"
set "pb.fill="
setlocal enableextensions enabledelayedexpansion
for /l %%p in (1 1 %pb.barArea%) do set "pb.fill=!pb.fill!%pb.fillChar%"
set "pb.fill=!pb.fill:~0,%pb.barArea%!"
set "pb.dots=!pb.fill:%pb.fillChar%=%pb.dotChar%!"
set "pb.back=!pb.fill:~0,%pb.textArea%!
set "pb.back=!pb.back:%pb.fillChar%= !"
endlocal & set "pb.fill=%pb.fill%" & set "pb.dots=%pb.dots%" & set "pb.back=%pb.back%"
chcp 1252 >nul
goto :eof
:finalizeProgressBar [erase]
if defined pb.cr (
if not "%~1"=="" (
setlocal enabledelayedexpansion
set "pb.back="
for /l %%p in (1 1 %pb.console.columns%) do set "pb.back=!pb.back! "
<nul set /p "pb.prompt=!pb.cr!!pb.back:~1!!pb.cr!"
endlocal
)
)
for /f "tokens=1 delims==" %%v in ('set pb.') do set "%%v="
goto :eof
This is probably an easy task for the seasoned guys & gals here but I struggled with how to do this so I thought that I would share what worked for me and how I used the script.
Full credit goes to MC ND who is the original author of the script used. Please see the original post for more customisation options.
My internet is not always working properly and I'd like to check the quality based on the cmd windows tool. I believe it's a task simple enough for it to handle.
I've begun by making a shortcut so I can have easy access to the command:
C:\Windows\System32\PING.EXE 8.8.8.8 -t
Now I was trying to transform the cmd ping command into a visually responsive one based on the output. I'd like to make the color change according to the time response.
After looking and not finding anything related, I believe it's either impossible or no one has ever tried.
Thank you very much :)
PD: (In case there was anything unclear just ask and I'll gladly answer)
Based on Magoo's post, I wrote this little batch program.
It asks for the target, the number of requests to make, the max time allowed and the time between requests and then prints in red if the request is over the time max, otherwise it sums the number of requests. It includes timestamp to be more accurate.
Copy and paste in a text file and name it with extension ".bat" (But don't name it "ping.bat" otherwise the program will enter in an infinite loop).
REM CMD PING TOOL
REM By Daweb
REM https://stackoverflow.com/users/3779294/daweb
#ECHO OFF
REM Needed for Line colored
SETLOCAL EnableDelayedExpansion
FOR /F "tokens=1,2 delims=#" %%a IN ('"PROMPT #$H#$E# & echo on & for %%b in (1) do rem"') do (
SET "DEL=%%a"
)
for /f %%a in ('copy /Z "%~f0" nul') do set "CR=%%a"
ECHO *****************
ECHO * CMD PING TOOL *
ECHO *****************
REM Start
:start
ECHO.
ECHO Set yours values
REM SET Target
SET /p hostInput=" - Target (ip or hostname): "
If "%hostInput%"=="" ECHO.&GOTO start
REM SET loops
SET /p loopsInput=" - Requests number: "
SET /a loops=loopsInput
REM SET time limit
SET /p maxmsInput=" - Maximum Time Limit (ms): "
SET /a maxms=maxmsInput
REM Value used for sleep between loops
SET /p sleepInput=" - Delay between requests (s): "
SET /a sleepDelay=sleepInput+1
REM Variables
SET displayText=""
SET /a countRequestsOk=0
SET /a countRequestsKo=0
SET /a totalRequests=0
SET /a maxTime=0
ECHO.
ECHO START at %TIME% [target: %hostInput%, requests: %loops%, time limit: %maxms% ms, delay: %sleepInput% s]
ECHO.
REM Loop
:loop
REM Set time
FOR /f "tokens=1-3 delims=/:" %%a IN ("%TIME%") DO (SET mytime=%%ah%%bm%%cs)
REM Get ping value
FOR /f "tokens=3delims==" %%a IN ('PING -n 1 %hostInput%') DO FOR /f "delims=m" %%b IN ("%%a") DO (
SET /a timems=%%b
SET /a totalRequests+=1
REM Check result
IF !timems! GTR %maxms% ( GOTO failed ) ELSE ( GOTO success )
)
REM Request success
:success
SET /a countRequestsOk+=1
IF !timems! GTR !maxTime! ( SET /a maxTime=timems )
<nul set /P "=!countRequestsOk! requests [Max !maxTime! ms]!CR!"
GOTO next
REM Request failed
:failed
IF !countRequestsOk! GTR 0 ECHO.
SET /a countRequestsOk=0
SET /a countRequestsKo+=1
SET displayText=" %mytime% - !timems!ms"
CALL :ColorText 0c !displayText!
GOTO next
REM Next loop
:next
REM Sleep a little bit
IF %sleepDelay% GTR 1 ( ping -n %sleepDelay% localhost > nul )
REM Check continue
SET /a loops-=1
IF %loops% gtr 0 GOTO loop
REM Display result
IF !countRequestsOk! GTR 0 ECHO.
ECHO.
ECHO STOP at %TIME%
ECHO.
if !countRequestsKo! GTR 0 (
SET displayText="FAILED - !countRequestsKo! requests over %maxms% ms on !totalRequests! requests in total"
CALL :ColorText 0c !displayText!
) ELSE (
SET displayText="SUCCESS - No request over %maxms% ms on !totalRequests! requests in total"
CALL :ColorText 02 !displayText!
)
REM Ask if restart
ECHO.&ECHO *********************
SET /p restartInput="Do it again ? (Y/N): "
If "%restartInput%"=="" ECHO *********************&GOTO start
If /I "%restartInput%"=="y" ECHO *********************&GOTO start
If /I "%restartInput%"=="n" ECHO *********************&GOTO end
REM End
:end
PAUSE
GOTO :EOF
REM Line color
:ColorText
ECHO off
ECHO %DEL% > "%~2"
FINDSTR /v /a:%1 /R "^$" "%~2" NUL
DEL "%~2" > NUL 2>&1
#ECHO OFF
SETLOCAL
SET loops=10
:loop
FOR /f "tokens=3delims==" %%a IN ('PING 8.8.8.8 -n 1') DO FOR /f "delims=m" %%b IN ("%%a") DO ECHO %%b&COLOR %%b&GOTO cchgd
:cchgd
PAUSE
SET /a loops-=1
IF %loops% gtr 0 GOTO loop
COLOR
GOTO :EOF
A simple demonstration - repeats the ping 10 times, changing colours depending on the response. Manipulate to do as you wish...
I am not sure that I know what the desired output should be, but this will output GREEN text for response times 0-39 ms, YELLOW for 40-79 ms, and RED for 80+ ms.
Run this from a cmd.exe prompt using the following command or put it into a .bat file script. Change the directory to the location where the Get-PingColor.ps1 file is landed.
powershell -NoLogo -NoProfile -File "%USERPROFILE%\bin\Get-PingColor.ps1"
=== Get-PingColor.ps1
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[string[]]$ComputerNames
,[Parameter(Mandatory=$false)]
[int]$Count = 4
,[Parameter(Mandatory=$false)]
[int]$SpeedMinimumSlow = 80
,[Parameter(Mandatory=$false)]
[int]$SpeedMinimumMedium = 40
)
foreach ($ComputerName in $ComputerNames) {
$Pings = Test-Connection -ComputerName $ComputerName -Count $Count
$Average = ($Pings | Measure-Object -Property responsetime -Average).Average
$ForegroundColor = 'Green'
if ($Average -ge $SpeedMinimumSlow) { $ForegroundColor = 'Red'}
else { if ($Average -ge $SpeedMinimumMedium) { $ForegroundColor = 'Yellow' }}
Write-Host -ForegroundColor $ForegroundColor -BackgroundColor 'Black' "$ComputerName $Average ms"
}
=== Execution examples
I am loathe to put images into a post, but I do not see a way to produce color on SO.
I am creating a batch file to execute some exe file. In that exe i need to specify first date(mm/dd/yyyy) and last day(mm/dd/yyyy) of last month.
#echo off
start "Testing" "c:\Program Files\app-cmd\bin\admincmd\imagelist -d 08/01/2015 -e 01/09/2015 > c:\test.txt"
Imagelist is an exe file where I need to pass value in -d and -e parameter.
I will add this script in schedule task so, it will run as schedule.
I can't use powershell as machine is win server 2003.
Please advise.
Assuming the date format on your system is MM/DD/YYYY as shown by date /t command:
#echo off
setlocal enableDelayedExpansion
for /f "delims=/ tokens=1,3" %%a in ("%date%") do set month=%%a& set year=%%b
if %month:~0,1%==0 set month=%month:~1%
set /a month-=1 && if !month!==0 set month=12&set /a year-=1
if not !month!==2 (
set /a last_day="31 - (month - 1) %% 7 %% 2"
) else (
set /a y4="year %% 4" & if !y4!==0 (
set /a y100="year %% 100" & if not !y100!==0 set is_leap_year=1
set /a y400="year %% 400" & if !y400!==0 set is_leap_year=1
)
if "!is_leap_year!"=="1" (set last_day=29) else set last_day=28
)
set month=0!month!
start "Testing" "c:\Program Files\app-cmd\bin\admincmd\imagelist" ^
-d !month:~-2!/01/!year! -e !month:~-2!/!last_day!/!year! > c:\test.txt
The code detects leap years as (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)) for February and uses 31 - (month - 1) % 7 % 2 for other months.
You can try with
#echo off
setlocal enableextensions
call :getLastMonthLimits start end
echo %start% - %end%
goto :eof
:getLastMonthLimits startDateReturnVar endDateReturnVar
setlocal enableextensions disabledelayedexpansion
for %%t in ("%temp%\%~nx0.%random%%random%%random%.rpt") do (
for /f "tokens=4,7" %%a in ('
^>nul makecab /f nul /d RptFileName^="%%~ft" /V0
^& type "%%~ft" ^| find "MakeCAB Report:"
') do (
set /a "jan=1,feb=2,mar=3,apr=4,may=5,jun=6,jul=7,aug=8,sep=9,oct=10,nov=11,dec=12"
set /a "m=(%%a+10) %% 12 + 1", "y=%%b-m/12", "d=30+((m+m/8) %% 2)", "m+=100"
if /i %%a==mar set /a "d=d-2 +(3-y %% 4)/3 -(99-y %% 100)/99 +(399-y %% 400)/399"
set /a "d+=100"
)
) & del "%%~ft"
endlocal & set "%~1=%m:~-2%/01/%y%" & set "%~2=%m:~-2%/%d:~-2%/%y%"
goto :eof
This uses a simplified version of the getDate function by carlos to get the date in a locale independent format (I prefer to use robocopy for it, but it is not a native tool in 2003). The rest are just arithmetic operations to calculate the month limits.
edited For the robocopy version (instead of the makecab to retrieve the date in locale independent format), just in case someone could need it
#echo off
call :getLastMonthLimits start end
echo %start% - %end%
goto :eof
:getLastMonthLimits startDateReturnVar endDateReturnVar
setlocal enableextensions disabledelayedexpansion
for /f "tokens=1-2 delims=/ " %%a in ('robocopy "|" . /njh ^| find "|"') do (
set /a "m=((1%%b-100)+10) %% 12 + 1", "y=%%a-m/12", "d=30+((m+m/8) %% 2)", "m+=100"
if %%b==03 set /a "d=d-2 +(3-y %% 4)/3 -(99-y %% 100)/99 +(399-y %% 400)/399"
set /a "d+=100"
)
endlocal & set "%~1=%m:~-2%/01/%y%" & set "%~2=%m:~-2%/%d:~-2%/%y%"
goto :eof
I am looking for a way to get a value return from a start-command launched batch script. Let me explain:
I need to take advantage of multiprocessing by launching multiple sub-batch scripts simultaneously from a main batch script, then retrieve every sub batch file return value when they're done.
I've been using return variables with the call-command as very well explained by dbenham.
That solution does not allow multithreading, since sub-batch scripts are run one after the other.
Using the start-command allows multiple running batch scripts, but values are not returned to my main script because apparently the start-command creates a whole new variable context.
Does anybody have a solution/workaround to return values from the sub-scripts to the main script ?
Below is a model of what I need:
mainScript.bat
#echo off
setlocal
set "retval1=0"
set "retval2=0"
REM run two scripts in parallel:
start "" subscript1.bat arg1 retval1
start "" subscript2.bat arg1 retval2
REM wait for returned value
:waiting
call :sleep 1
set /a "DONE=1"
if %retval1% equ 0 set "DONE=0"
if %retval2% equ 0 set "DONE=0"
if %DONE% equ 0 goto :waiting
echo returned values are %retval1% %retval2%
exit /b
subscript1.bat
#echo off
setlocal
set "arg1=%~1"
set "retval1=%~1"
REM do some stuff...
REM return value
(endlocal
set "%retval1%=%foo%"
)
exit /b
Can't see any alternative to writing your return values to files, so
main
#ECHO OFF
SETLOCAL
for %%a in (1 2) do (
del "%temp%\retval%%a" 2>nul
)
start /min "" q225220791.bat arg1 retval1
choice /t 1 /d y >nul
start /min "" q225220791.bat arg1 retval2
:waiting
choice /t 1 /d y >nul
ECHO wait...%retval1%...%retval2%
if not exist "%temp%\retval1" goto waiting
if not exist "%temp%\retval2" goto waiting
for %%a in (1 2) do (
for /f "usebackqdelims=" %%i in ("%temp%\retval%%a") do set "retval%%a=%%i"
)
for %%a in (1 2) do (
del "%temp%\retval%%a" 2>nul
)
echo returned values are %retval1% %retval2%
GOTO :EOF
q225220791.bat
#ECHO OFF
SETLOCAL
:: wait a random time 2..10 sec.
SET /a timeout=%RANDOM% %% 8 + 2
choice /t %timeout% /d y >nul
:: return a random result 12..20
SET /a foo=%RANDOM% %% 8 + 12
>"%temp%\%2" echo %foo%
ENDLOCAL
exit
Relying on the value of the second parameter to the sub-process to set the tempfile name. I've changed the names of the batches to suit my system.
Not sure if this is even practical, but just testing to avoid the temporary file. So looking for a place in child process that can be readed from parent process i decided to use the window title.
task.cmd
#echo off
setlocal
rem Retrieve task information and set window title
set "taskID=%~1"
title [task];%taskID%;working;
rem Retrieve the rest of parameters. For this sample, a random value
set /a "timeToWait=%~2 %% 30"
rem Simulate command processing
timeout /t %timeToWait%
rem Calculate a return value for this task
for /f "tokens=1-10 delims=,.:/ " %%a in ("%date%%time%_%~2") do set "returnValue=%%a%%b%%c%%d%%e%%f%%g%%h%%i%%j"
rem Signal the end of the task
title [task];%taskID%;ended; my return value is %returnValue% ;
rem Wait for master to end this tasks
cls
echo Waiting for master....
waitfor %taskID%
rem Cleanup
endlocal
master.cmd
#echo off
setlocal enableextensions enabledelayedexpansion
rem Configure tasks
set "taskPrefix=myTSK"
set "numTasks=5"
rem Start tasks
for /l %%a in (1 1 %numTasks%) do (
set "return[%taskPrefix%%%a]=unknown"
start "[task];%taskPrefix%%%a;working;" cmd /c "task.cmd %taskPrefix%%%a !random!"
)
rem Wait for tasks to start
timeout /t 2 > nul
rem Wait for tasks to end. Get the list of cmd.exe windows with window title
rem to see the state of the task
rem Tasks in working state indicate master needs to keep working
rem Tasks in ended state have the return value in the window title and are
rem waiting for the master to retrieve the value and end them
:wait
set "keepWaiting="
for /f "tokens=9 delims=," %%a in ('tasklist /fi "imagename eq cmd.exe" /fo csv /v ^| findstr /l /c:"[task];%taskPrefix%"'
) do for /f "tokens=2-4 delims=;" %%b in ("%%a") do (
if "%%c"=="working" (
set "keepWaiting=1"
) else if "%%c"=="ended" (
set "return[%%b]=%%d"
start "" /min waitfor.exe /si %%b
)
)
rem If any task has been found in working state, keep waiting
if defined keepWaiting (
echo %time% : waiting ...
timeout /t 5 > nul
goto wait
)
rem All tasks have ended. Show return values
for /l %%a in (1 1 %numTasks%) do (
echo task #%%a ended with exit value :[!return[%taskPrefix%%%a]!]
)
endlocal
In this sample, the task waits for master using waitfor command. In XP this is not available, and can be replaced with just a pause, and from master.cmd the waiting loop must be modified to include the processID token in the tasklist processing, so the waiting task can be closed with taskkill.
How can I get the difference between two times in a Batch file? Because I want to print it in an HTML file.
I thought this would be possible, but it isn't.
Set "tijd=%time%"
echo %tijd%
echo %time%-%tijd%
Results:
11:07:48,85
11:16:58,99-11:07:48,85
But what I want is:
00:09:10,14
Or 9 minutes and 10 seconds or 550 seconds
#echo off
rem Get start time:
for /F "tokens=1-4 delims=:.," %%a in ("%time%") do (
set /A "start=(((%%a*60)+1%%b %% 100)*60+1%%c %% 100)*100+1%%d %% 100"
)
rem Any process here...
rem Get end time:
for /F "tokens=1-4 delims=:.," %%a in ("%time%") do (
set /A "end=(((%%a*60)+1%%b %% 100)*60+1%%c %% 100)*100+1%%d %% 100"
)
rem Get elapsed time:
set /A elapsed=end-start
rem Show elapsed time:
set /A hh=elapsed/(60*60*100), rest=elapsed%%(60*60*100), mm=rest/(60*100), rest%%=60*100, ss=rest/100, cc=rest%%100
if %mm% lss 10 set mm=0%mm%
if %ss% lss 10 set ss=0%ss%
if %cc% lss 10 set cc=0%cc%
echo %hh%:%mm%:%ss%,%cc%
EDIT 2017-05-09: Shorter method added
I developed a shorter method to get the same result, so I couldn't resist to post it here. The two for commands used to separate time parts and the three if commands used to insert leading zeros in the result are replaced by two long arithmetic expressions, that could even be combined into a single longer line.
The method consists in directly convert a variable with a time in "HH:MM:SS.CC" format into the formula needed to convert the time to centiseconds, accordingly to the mapping scheme given below:
HH : MM : SS . CC
(((10 HH %%100)*60+1 MM %%100)*60+1 SS %%100)*100+1 CC %%100
That is, insert (((10 at beginning, replace the colons by %%100)*60+1, replace the point by %%100)*100+1 and insert %%100 at end; finally, evaluate the resulting string as an arithmetic expression. In the time variable there are two different substrings that needs to be replaced, so the conversion must be completed in two lines. To get an elapsed time, use (endTime)-(startTime) expression and replace both time strings in the same line.
EDIT 2017/06/14: Locale independent adjustment added
EDIT 2020/06/05: Pass-over-midnight adjustment added
#echo off
setlocal EnableDelayedExpansion
set "startTime=%time: =0%"
set /P "=Any process here..."
set "endTime=%time: =0%"
rem Get elapsed time:
set "end=!endTime:%time:~8,1%=%%100)*100+1!" & set "start=!startTime:%time:~8,1%=%%100)*100+1!"
set /A "elap=((((10!end:%time:~2,1%=%%100)*60+1!%%100)-((((10!start:%time:~2,1%=%%100)*60+1!%%100), elap-=(elap>>31)*24*60*60*100"
rem Convert elapsed time to HH:MM:SS:CC format:
set /A "cc=elap%%100+100,elap/=100,ss=elap%%60+100,elap/=60,mm=elap%%60+100,hh=elap/60+100"
echo Start: %startTime%
echo End: %endTime%
echo Elapsed: %hh:~1%%time:~2,1%%mm:~1%%time:~2,1%%ss:~1%%time:~8,1%%cc:~1%
You may review a detailed explanation of this method at this answer.
As answered here:
How can I use a Windows batch file to measure the performance of console application?
Below batch "program" should do what you want. Please note that it outputs the data in centiseconds instead of milliseconds. The precision of the used commands is only centiseconds.
Here is an example output:
STARTTIME: 13:42:52,25
ENDTIME: 13:42:56,51
STARTTIME: 4937225 centiseconds
ENDTIME: 4937651 centiseconds
DURATION: 426 in centiseconds
00:00:04,26
Here is the batch script:
#echo off
setlocal
rem The format of %TIME% is HH:MM:SS,CS for example 23:59:59,99
set STARTTIME=%TIME%
rem here begins the command you want to measure
dir /s > nul
rem here ends the command you want to measure
set ENDTIME=%TIME%
rem output as time
echo STARTTIME: %STARTTIME%
echo ENDTIME: %ENDTIME%
rem convert STARTTIME and ENDTIME to centiseconds
set /A STARTTIME=(1%STARTTIME:~0,2%-100)*360000 + (1%STARTTIME:~3,2%-100)*6000 + (1%STARTTIME:~6,2%-100)*100 + (1%STARTTIME:~9,2%-100)
set /A ENDTIME=(1%ENDTIME:~0,2%-100)*360000 + (1%ENDTIME:~3,2%-100)*6000 + (1%ENDTIME:~6,2%-100)*100 + (1%ENDTIME:~9,2%-100)
rem calculating the duratyion is easy
set /A DURATION=%ENDTIME%-%STARTTIME%
rem we might have measured the time inbetween days
if %ENDTIME% LSS %STARTTIME% set set /A DURATION=%STARTTIME%-%ENDTIME%
rem now break the centiseconds down to hors, minutes, seconds and the remaining centiseconds
set /A DURATIONH=%DURATION% / 360000
set /A DURATIONM=(%DURATION% - %DURATIONH%*360000) / 6000
set /A DURATIONS=(%DURATION% - %DURATIONH%*360000 - %DURATIONM%*6000) / 100
set /A DURATIONHS=(%DURATION% - %DURATIONH%*360000 - %DURATIONM%*6000 - %DURATIONS%*100)
rem some formatting
if %DURATIONH% LSS 10 set DURATIONH=0%DURATIONH%
if %DURATIONM% LSS 10 set DURATIONM=0%DURATIONM%
if %DURATIONS% LSS 10 set DURATIONS=0%DURATIONS%
if %DURATIONHS% LSS 10 set DURATIONHS=0%DURATIONHS%
rem outputing
echo STARTTIME: %STARTTIME% centiseconds
echo ENDTIME: %ENDTIME% centiseconds
echo DURATION: %DURATION% in centiseconds
echo %DURATIONH%:%DURATIONM%:%DURATIONS%,%DURATIONHS%
endlocal
goto :EOF
A re-hash of Aacini's code because most likely you are going to set the start time as a variable and want to save that data for output:
#echo off
rem ****************** MAIN CODE SECTION
set STARTTIME=%TIME%
rem Your code goes here (remove the ping line)
ping -n 4 -w 1 127.0.0.1 >NUL
set ENDTIME=%TIME%
rem ****************** END MAIN CODE SECTION
rem Change formatting for the start and end times
for /F "tokens=1-4 delims=:.," %%a in ("%STARTTIME%") do (
set /A "start=(((%%a*60)+1%%b %% 100)*60+1%%c %% 100)*100+1%%d %% 100"
)
for /F "tokens=1-4 delims=:.," %%a in ("%ENDTIME%") do (
IF %ENDTIME% GTR %STARTTIME% set /A "end=(((%%a*60)+1%%b %% 100)*60+1%%c %% 100)*100+1%%d %% 100"
IF %ENDTIME% LSS %STARTTIME% set /A "end=((((%%a+24)*60)+1%%b %% 100)*60+1%%c %% 100)*100+1%%d %% 100"
)
rem Calculate the elapsed time by subtracting values
set /A elapsed=end-start
rem Format the results for output
set /A hh=elapsed/(60*60*100), rest=elapsed%%(60*60*100), mm=rest/(60*100), rest%%=60*100, ss=rest/100, cc=rest%%100
if %hh% lss 10 set hh=0%hh%
if %mm% lss 10 set mm=0%mm%
if %ss% lss 10 set ss=0%ss%
if %cc% lss 10 set cc=0%cc%
set DURATION=%hh%:%mm%:%ss%,%cc%
echo Start : %STARTTIME%
echo Finish : %ENDTIME%
echo ---------------
echo Duration : %DURATION%
Output:
Start : 11:02:45.92
Finish : 11:02:48.98
---------------
Duration : 00:00:03,06
If you do not mind using powershell within batch script:
#echo off
set start_date=%date% %time%
:: Simulate some type of processing using ping
ping 127.0.0.1
set end_date=%date% %time%
powershell -command "&{$start_date1 = [datetime]::parse('%start_date%'); $end_date1 = [datetime]::parse('%date% %time%'); echo (-join('Duration in seconds: ', ($end_date1 - $start_date1).TotalSeconds)); }"
Aacini's latest code showcases an awesome variable substitution method.
It's a shame it's not Regional format proof - it fails on so many levels.
Here's a short fix that keeps the substitution+math method intact:
#echo off
setlocal EnableDelayedExpansion
set "startTime=%time: =0%" & rem AveYo: fix single digit hour
set /P "=Any process here..."
set "endTime=%time: =0%" & rem AveYo: fix single digit hour
rem Aveyo: Regional format fix with just one aditional line
for /f "tokens=1-3 delims=0123456789" %%i in ("%endTime%") do set "COLON=%%i" & set "DOT=%%k"
rem Get elapsed time:
set "end=!endTime:%DOT%=%%100)*100+1!" & set "start=!startTime:%DOT%=%%100)*100+1!"
set /A "elap=((((10!end:%COLON%=%%100)*60+1!%%100)-((((10!start:%COLON%=%%100)*60+1!%%100)"
rem Aveyo: Fix 24 hours
set /A "elap=!elap:-=8640000-!"
rem Convert elapsed time to HH:MM:SS:CC format:
set /A "cc=elap%%100+100,elap/=100,ss=elap%%60+100,elap/=60,mm=elap%%60+100,hh=elap/60+100"
echo Start: %startTime%
echo End: %endTime%
echo Elapsed: %hh:~1%%COLON%%mm:~1%%COLON%%ss:~1%%DOT%%cc:~1% & rem AveYo: display as regional
pause
*
"Lean and Mean" TIMER with Regional format, 24h and mixed input support
Adapting Aacini's substitution method body, no IF's, just one FOR (my regional fix)
1: File timer.bat placed somewhere in %PATH% or the current dir
#echo off & rem :AveYo: compact timer function with Regional format, 24-hours and mixed input support
if not defined timer_set (if not "%~1"=="" (call set "timer_set=%~1") else set "timer_set=%TIME: =0%") & goto :eof
(if not "%~1"=="" (call set "timer_end=%~1") else set "timer_end=%TIME: =0%") & setlocal EnableDelayedExpansion
for /f "tokens=1-6 delims=0123456789" %%i in ("%timer_end%%timer_set%") do (set CE=%%i&set DE=%%k&set CS=%%l&set DS=%%n)
set "TE=!timer_end:%DE%=%%100)*100+1!" & set "TS=!timer_set:%DS%=%%100)*100+1!"
set/A "T=((((10!TE:%CE%=%%100)*60+1!%%100)-((((10!TS:%CS%=%%100)*60+1!%%100)" & set/A "T=!T:-=8640000-!"
set/A "cc=T%%100+100,T/=100,ss=T%%60+100,T/=60,mm=T%%60+100,hh=T/60+100"
set "value=!hh:~1!%CE%!mm:~1!%CE%!ss:~1!%DE%!cc:~1!" & if "%~2"=="" echo/!value!
endlocal & set "timer_end=%value%" & set "timer_set=" & goto :eof
Usage:
timer & echo start_cmds & timeout /t 3 & echo end_cmds & timer
timer & timer "23:23:23,00"
timer "23:23:23,00" & timer
timer "13.23.23,00" & timer "03:03:03.00"
timer & timer "0:00:00.00" no & cmd /v:on /c echo until midnight=!timer_end!
Input can now be mixed, for those unlikely, but possible time format changes during execution
2: Function :timer bundled with the batch script (sample usage below):
#echo off
set "TIMER=call :timer" & rem short macro
echo.
echo EXAMPLE:
call :timer
timeout /t 3 >nul & rem Any process here..
call :timer
echo.
echo SHORT MACRO:
%TIMER% & timeout /t 1 & %TIMER%
echo.
echo TEST INPUT:
set "start=22:04:04.58"
set "end=04.22.44,22"
echo %start% ~ start & echo %end% ~ end
call :timer "%start%"
call :timer "%end%"
echo.
%TIMER% & %TIMER% "00:00:00.00" no
echo UNTIL MIDNIGHT: %timer_end%
echo.
pause
exit /b
:: to test it, copy-paste both above and below code sections
rem :AveYo: compact timer function with Regional format, 24-hours and mixed input support
:timer Usage " call :timer [input - optional] [no - optional]" :i Result printed on second call, saved to timer_end
if not defined timer_set (if not "%~1"=="" (call set "timer_set=%~1") else set "timer_set=%TIME: =0%") & goto :eof
(if not "%~1"=="" (call set "timer_end=%~1") else set "timer_end=%TIME: =0%") & setlocal EnableDelayedExpansion
for /f "tokens=1-6 delims=0123456789" %%i in ("%timer_end%%timer_set%") do (set CE=%%i&set DE=%%k&set CS=%%l&set DS=%%n)
set "TE=!timer_end:%DE%=%%100)*100+1!" & set "TS=!timer_set:%DS%=%%100)*100+1!"
set/A "T=((((10!TE:%CE%=%%100)*60+1!%%100)-((((10!TS:%CS%=%%100)*60+1!%%100)" & set/A "T=!T:-=8640000-!"
set/A "cc=T%%100+100,T/=100,ss=T%%60+100,T/=60,mm=T%%60+100,hh=T/60+100"
set "value=!hh:~1!%CE%!mm:~1!%CE%!ss:~1!%DE%!cc:~1!" & if "%~2"=="" echo/!value!
endlocal & set "timer_end=%value%" & set "timer_set=" & goto :eof
Based on previous answers, here are reusable "procedures" and a usage example for calculating the elapsed time:
#echo off
setlocal
set starttime=%TIME%
echo Start Time: %starttime%
REM ---------------------------------------------
REM --- PUT THE CODE YOU WANT TO MEASURE HERE ---
REM ---------------------------------------------
set endtime=%TIME%
echo End Time: %endtime%
call :elapsed_time %starttime% %endtime% duration
echo Duration: %duration%
endlocal
echo on & goto :eof
REM --- HELPER PROCEDURES ---
:time_to_centiseconds
:: %~1 - time
:: %~2 - centiseconds output variable
setlocal
set _time=%~1
for /F "tokens=1-4 delims=:.," %%a in ("%_time%") do (
set /A "_result=(((%%a*60)+1%%b %% 100)*60+1%%c %% 100)*100+1%%d %% 100"
)
endlocal & set %~2=%_result%
goto :eof
:centiseconds_to_time
:: %~1 - centiseconds
:: %~2 - time output variable
setlocal
set _centiseconds=%~1
rem now break the centiseconds down to hors, minutes, seconds and the remaining centiseconds
set /A _h=%_centiseconds% / 360000
set /A _m=(%_centiseconds% - %_h%*360000) / 6000
set /A _s=(%_centiseconds% - %_h%*360000 - %_m%*6000) / 100
set /A _hs=(%_centiseconds% - %_h%*360000 - %_m%*6000 - %_s%*100)
rem some formatting
if %_h% LSS 10 set _h=0%_h%
if %_m% LSS 10 set _m=0%_m%
if %_s% LSS 10 set _s=0%_s%
if %_hs% LSS 10 set _hs=0%_hs%
set _result=%_h%:%_m%:%_s%.%_hs%
endlocal & set %~2=%_result%
goto :eof
:elapsed_time
:: %~1 - time1 - start time
:: %~2 - time2 - end time
:: %~3 - elapsed time output
setlocal
set _time1=%~1
set _time2=%~2
call :time_to_centiseconds %_time1% _centi1
call :time_to_centiseconds %_time2% _centi2
set /A _duration=%_centi2%-%_centi1%
call :centiseconds_to_time %_duration% _result
endlocal & set %~3=%_result%
goto :eof
Fixed Gynnad's leading 0 Issue. I fixed it with the two Lines
SET STARTTIME=%STARTTIME: =0%
SET ENDTIME=%ENDTIME: =0%
Full Script ( CalculateTime.cmd ):
#ECHO OFF
:: F U N C T I O N S
:__START_TIME_MEASURE
SET STARTTIME=%TIME%
SET STARTTIME=%STARTTIME: =0%
EXIT /B 0
:__STOP_TIME_MEASURE
SET ENDTIME=%TIME%
SET ENDTIME=%ENDTIME: =0%
SET /A STARTTIME=(1%STARTTIME:~0,2%-100)*360000 + (1%STARTTIME:~3,2%-100)*6000 + (1%STARTTIME:~6,2%-100)*100 + (1%STARTTIME:~9,2%-100)
SET /A ENDTIME=(1%ENDTIME:~0,2%-100)*360000 + (1%ENDTIME:~3,2%-100)*6000 + (1%ENDTIME:~6,2%-100)*100 + (1%ENDTIME:~9,2%-100)
SET /A DURATION=%ENDTIME%-%STARTTIME%
IF %DURATION% == 0 SET TIMEDIFF=00:00:00,00 && EXIT /B 0
IF %ENDTIME% LSS %STARTTIME% SET /A DURATION=%STARTTIME%-%ENDTIME%
SET /A DURATIONH=%DURATION% / 360000
SET /A DURATIONM=(%DURATION% - %DURATIONH%*360000) / 6000
SET /A DURATIONS=(%DURATION% - %DURATIONH%*360000 - %DURATIONM%*6000) / 100
SET /A DURATIONHS=(%DURATION% - %DURATIONH%*360000 - %DURATIONM%*6000 - %DURATIONS%*100)
IF %DURATIONH% LSS 10 SET DURATIONH=0%DURATIONH%
IF %DURATIONM% LSS 10 SET DURATIONM=0%DURATIONM%
IF %DURATIONS% LSS 10 SET DURATIONS=0%DURATIONS%
IF %DURATIONHS% LSS 10 SET DURATIONHS=0%DURATIONHS%
SET TIMEDIFF=%DURATIONH%:%DURATIONM%:%DURATIONS%,%DURATIONHS%
EXIT /B 0
:: U S A G E
:: Start Measuring
CALL :__START_TIME_MEASURE
:: Print Message on Screen without Linefeed
ECHO|SET /P=Execute Job...
:: Some Time pending Jobs here
:: '> NUL 2>&1' Dont show any Messages or Errors on Screen
MyJob.exe > NUL 2>&1
:: Stop Measuring
CALL :__STOP_TIME_MEASURE
:: Finish the Message 'Execute Job...' and print measured Time
ECHO [Done] (%TIMEDIFF%)
:: Possible Result
:: Execute Job... [Done] (00:02:12,31)
:: Between 'Execute Job... ' and '[Done] (00:02:12,31)' the Job will be executed
Here is my attempt to measure time difference in batch.
It respects the regional format of %TIME% without taking any assumptions on type of characters for time and decimal separators.
The code is commented but I will also describe it here.
It is flexible so it can also be used to normalize non-standard time values as well
The main function :timediff
:: timediff
:: Input and output format is the same format as %TIME%
:: If EndTime is less than StartTime then:
:: EndTime will be treated as a time in the next day
:: in that case, function measures time difference between a maximum distance of 24 hours minus 1 centisecond
:: time elements can have values greater than their standard maximum value ex: 12:247:853.5214
:: provided than the total represented time does not exceed 24*360000 centiseconds
:: otherwise the result will not be meaningful.
:: If EndTime is greater than or equals to StartTime then:
:: No formal limitation applies to the value of elements,
:: except that total represented time can not exceed 2147483647 centiseconds.
:timediff <outDiff> <inStartTime> <inEndTime>
(
setlocal EnableDelayedExpansion
set "Input=!%~2! !%~3!"
for /F "tokens=1,3 delims=0123456789 " %%A in ("!Input!") do set "time.delims=%%A%%B "
)
for /F "tokens=1-8 delims=%time.delims%" %%a in ("%Input%") do (
for %%A in ("#h1=%%a" "#m1=%%b" "#s1=%%c" "#c1=%%d" "#h2=%%e" "#m2=%%f" "#s2=%%g" "#c2=%%h") do (
for /F "tokens=1,2 delims==" %%A in ("%%~A") do (
for /F "tokens=* delims=0" %%B in ("%%B") do set "%%A=%%B"
)
)
set /a "#d=(#h2-#h1)*360000+(#m2-#m1)*6000+(#s2-#s1)*100+(#c2-#c1), #sign=(#d>>31)&1, #d+=(#sign*24*360000), #h=(#d/360000), #d%%=360000, #m=#d/6000, #d%%=6000, #s=#d/100, #c=#d%%100"
)
(
if %#h% LEQ 9 set "#h=0%#h%"
if %#m% LEQ 9 set "#m=0%#m%"
if %#s% LEQ 9 set "#s=0%#s%"
if %#c% LEQ 9 set "#c=0%#c%"
)
(
endlocal
set "%~1=%#h%%time.delims:~0,1%%#m%%time.delims:~0,1%%#s%%time.delims:~1,1%%#c%"
exit /b
)
Example:
#echo off
setlocal EnableExtensions
set "TIME="
set "Start=%TIME%"
REM Do some stuff here...
set "End=%TIME%"
call :timediff Elapsed Start End
echo Elapsed Time: %Elapsed%
pause
exit /b
:: put the :timediff function here
Explanation of the :timediff function:
function prototype :timediff <outDiff> <inStartTime> <inEndTime>
Input and output format is the same format as %TIME%
It takes 3 parameters from left to right:
Param1: Name of the environment variable to save the result to.
Param2: Name of the environment variable to be passed to the function containing StartTime string
Param3: Name of the environment variable to be passed to the function containing EndTime string
If EndTime is less than StartTime then:EndTime will be treated as a time in the next day
in that case, the function measures time difference between a maximum distance of 24 hours minus 1 centisecond
time elements can have values greater than their standard maximum value ex: 12:247:853.5214
provided than the total represented time does not exceed 24*360000 centiseconds or (24:00:00.00) otherwise the result will not be meaningful.
If EndTime is greater than or equals to StartTime then:
No formal limitation applies to the value of elements,
except that total represented time can not exceed 2147483647 centiseconds.
More examples with literal and non-standard time values
Literal example with EndTime less than StartTime:
#echo off
setlocal EnableExtensions
set "start=23:57:33,12"
set "end=00:02:19,41"
call :timediff dif start end
echo Start Time: %start%
echo End Time: %end%
echo,
echo Difference: %dif%
echo,
pause
exit /b
:: put the :timediff function here
Output:
Start Time: 23:57:33,12
End Time: 00:02:19,41
Difference: 00:04:46,29
Normalize non-standard time:
#echo off
setlocal EnableExtensions
set "start=00:00:00.00"
set "end=27:2457:433.85935"
call :timediff normalized start end
echo,
echo %end% is equivalent to %normalized%
echo,
pause
exit /b
:: put the :timediff function here
Output:
27:2457:433.85935 is equivalent to 68:18:32.35
Last bonus example:
#echo off
setlocal EnableExtensions
set "start=00:00:00.00"
set "end=00:00:00.2147483647"
call :timediff normalized start end
echo,
echo 2147483647 centiseconds equals to %normalized%
echo,
pause
exit /b
:: put the :timediff function here
Output:
2147483647 centiseconds equals to 5965:13:56.47
Using a single function with the possibility of custom unit of measure or formatted.
Each time the function is called without parameters we restarted the initial time.
#ECHO OFF
ECHO.
ECHO DEMO timer function
ECHO --------------------
SET DELAY=4
:: First we call the function without any parameters to set the starting time
CALL:timer
:: We put some code we want to measure
ECHO.
ECHO Making some delay, please wait...
ECHO.
ping -n %DELAY% -w 1 127.0.0.1 >NUL
:: Now we call the function again with the desired parameters
CALL:timer elapsed_time
ECHO by Default : %elapsed_time%
CALL:timer elapsed_time "s"
ECHO in Seconds : %elapsed_time%
CALL:timer elapsed_time "anything"
ECHO Formatted : %elapsed_time% (HH:MM:SS.CS)
ECHO.
PAUSE
:: Elapsed Time Function
:: -----------------------------------------------------------------------
:: The returned value is in centiseconds, unless you enter the parameters
:: to be in another unit of measure or with formatted
::
:: Parameters:
:: <return> the returned value
:: [formatted] s (for seconds), m (for minutes), h (for hours)
:: anything else for formatted output
:: -----------------------------------------------------------------------
:timer <return> [formatted]
SetLocal EnableExtensions EnableDelayedExpansion
SET _t=%time%
SET _t=%_t::0=: %
SET _t=%_t:,0=, %
SET _t=%_t:.0=. %
SET _t=%_t:~0,2% * 360000 + %_t:~3,2% * 6000 + %_t:~6,2% * 100 + %_t:~9,2%
SET /A _t=%_t%
:: If we call the function without parameters is defined initial time
SET _r=%~1
IF NOT DEFINED _r (
EndLocal & SET TIMER_START_TIME=%_t% & GOTO :EOF
)
SET /A _t=%_t% - %TIMER_START_TIME%
:: In the case of wanting a formatted output
SET _f=%~2
IF DEFINED _f (
IF "%_f%" == "s" (
SET /A "_t=%_t% / 100"
) ELSE (
IF "%_f%" == "m" (
SET /A "_t=%_t% / 6000"
) ELSE (
IF "%_f%" == "h" (
SET /A "_t=%_t% / 360000"
) ELSE (
SET /A "_h=%_t% / 360000"
SET /A "_m=(%_t% - !_h! * 360000) / 6000"
SET /A "_s=(%_t% - !_h! * 360000 - !_m! * 6000) / 100"
SET /A "_cs=(%_t% - !_h! * 360000 - !_m! * 6000 - !_s! * 100)"
IF !_h! LSS 10 SET "_h=0!_h!"
IF !_m! LSS 10 SET "_m=0!_m!"
IF !_s! LSS 10 SET "_s=0!_s!"
IF !_cs! LSS 10 SET "_cs=0!_cs!"
SET "_t=!_h!:!_m!:!_s!.!_cs!"
SET "_t=!_t:00:=!"
)
)
)
)
EndLocal & SET %~1=%_t%
goto :EOF
A test with a delay of 94 sec
DEMO timer function
--------------------
Making some delay, please wait...
by Default : 9404
in Seconds : 94
Formatted : 01:34.05 (HH:MM:SS.CS)
Presione una tecla para continuar . . .
CMD doesn't have time arithmetic. The following code, however gives a workaround:
set vid_time=11:07:48
set srt_time=11:16:58
REM Get time difference
set length=%vid_time%
for /f "tokens=1-3 delims=:" %i in ("%length%") do (
set /a h=%i*3600
set /a m=%j*60
set /a s=%k
)
set /a t1=!h!+!m!+!s!
set length=%srt_time%
for /f "tokens=1-3 delims=:" %i in ("%length%") do (
set /a h=%i*3600
set /a m=%j*60
set /a s=%k
)
set /a t2=!h!+!m!+!s!
cls
set /a diff=!t2!-!t1!
Above code gives difference in seconds. To display in hh:mm:ss format, code below:
set ss=!diff!
set /a hh=!ss!/3600 >nul
set /a mm="(!ss!-3600*!hh!)/60" >nul
set /a ss="(!ss!-3600*!hh!)-!mm!*60" >nul
set "hh=0!hh!" & set "mm=0!mm!" & set "ss=0!ss!"
echo|set /p=!hh:~-2!:!mm:~-2!:!ss:~-2!
set START=23:05:15
set END=07:02:05
set options="tokens=1-4 delims=:."
for /f %options% %%a in ("%start%") do set start_h=%%a&set /a start_m=100%%b %% 100&set /a start_s=100%%c %% 100&set /a start_ms=100%%d %% 100
for /f %options% %%a in ("%end%") do set end_h=%%a&set /a end_m=100%%b %% 100&set /a end_s=100%%c %% 100&set /a end_ms=100%%d %% 100
set /a hours=%end_h%-%start_h%
set /a mins=%end_m%-%start_m%
set /a secs=%end_s%-%start_s%
set /a ms=%end_ms%-%start_ms%
if 1%ms% lss 100 set ms=0%ms%
if %ms% lss 0 set /a secs = %secs% - 1 & set /a ms = 100%ms%
if %secs% lss 0 set /a mins = %mins% - 1 & set /a secs = 60%secs%
if %mins% lss 0 set /a hours = %hours% - 1 & set /a mins = 60%mins%
if %hours% lss 0 set /a hours = 24%hours%
set hours=0%hours%
set hours=%hours:~-2%
set mins=0%mins%
set mins=%mins:~-2%
set secs=0%secs%
set secs=%secs:~-2%
set /a totalsecs = %hours%*3600 + %mins%*60 + %secs%
echo Command took %hours%:%mins%:%secs%.%ms% (%totalsecs%.%ms%s total)
echo %date% %start% - %end% ( %hours%:%mins%:%secs% )
pause