60

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

10
  • 3
    While it is possible (just search for date/time math in batch, honestly) you shouldn't do so. And I probably shouldn't even ask why a batch file is involved in serving HTML content, I guess. Commented Mar 29, 2012 at 9:23
  • 2
    Why I shouldn't do so? I do serving HTML because I deploy some plugins and the only way to look if the were succesful is to search through the logfile on the word 'succesful'.. And ofcourse I want feedback because it are more then 100 plugins. So I can see witch were succesful and witch failed. Have you any better method? Commented Mar 29, 2012 at 9:26
  • 3
    Why use PowerShell if it is also Possible in a batch file? I can use a batchfile in Windows 7, Vista, XP without installing anything. Powershell is only 'standard' on Windows 7. On the other OS's I need to install it.. It will cost more time, while it is possible in a batch file.. Commented Mar 29, 2012 at 9:32
  • 4
    I agree with Joey, don't write production processes in batch (and I'm a batch fanatic!), it has to much limitations and it's hard to implement complex tasks. I would choose phython/perl or some real language that will work independent of MS (that ensures it will still work in three years) and you can switch even to linux Commented Mar 29, 2012 at 9:48
  • 1
    I'm using it for testing of the builds where correct. Afterwards I will delete them. It is only a test if they deploy. When they are I will put a Succesful in the HTML file, when it fails I will put a Failed in the HTML file. Then I will delete the plugin. And so for all the plugins. It is not for production or something, it is only to see if it works. And I have now a batch file and it works very nice. So I don't understand why I should go to Powershell or anything other? Commented Mar 29, 2012 at 10:04

13 Answers 13

80
@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.

Sign up to request clarification or add additional context in comments.

16 Comments

Well done, I like this one a lot. I would only say that most likely people are setting the start time earlier in the script so I would set the variable in start / finish values instead of actually getting it and formatting it at the time it was recorded. I am going to post a re-hash of yours below.
This worked for me -- it does make locale specific assumptions -- that "," is used a decimal separator in the output (should be easy enough to detect this since you're parsing %time% anyway...), but with that one character of code change, it works for me, and looks right (in my locale).
And for the updated answer you have to replace [...]Time:. with [...]Time:, if you have ',' as decimal separator (locales are horrible).
@Andrew: Well, you are right if the base number have always 2 digits. In the first versions of this method, the Hour part could have 1 or 2 digits, so the 10 HH - 100 method would not work,,,
@Andrew: No. If H have two digits 10H have 4 digits and the %% 100 gives the last two. If H have one digit, the same 10H %% 100 also gives the last two, that is, the 1 digit of the H. The - 100 don't works this way...
|
52

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 duration is easy
set /A DURATION=%ENDTIME%-%STARTTIME%

rem we might have measured the time in-between days
if %ENDTIME% LSS %STARTTIME% set set /A DURATION=%STARTTIME%-%ENDTIME%

rem now break the centiseconds down to hours, 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

4 Comments

Aacini's below I think is better because it's more straight forward. Also your script spits out errors on my system and doesn't always perform the math operations. At times, I'm not sure what the issue was but it's not doing it now..
Not bad, but still a major bug left, as Mike stated already. There are (at least :-) two special behaviours an algorithm has to work around: 1. The first is that Batches think that numbers prefixed by zero are octal numbers. This is adressed here correctly by prefixing with a "1" and subtract 100 again. 2. But the other issue is that hours consist sometimes of one Digit ("2:") and sometimes of two ("12:"). Maybe this is locale specific, but I think, it is not. So the ":~02,2%" is failing here. Nevertheless, this does not work always, the solution from Aacini seems to work better.
Of course, regional settings apply here so you better know how any other user that might run this script is set up... Me, for example: when it's 01:35 AM, my "hours" become: "1:" (rather than "01", which would be fine) and that will fail the above script. Better add some logic to cover that!
Line 25: if %ENDTIME% LSS %STARTTIME% set set /A DURATION=%STARTTIME%-%ENDTIME% - I don't get it how it's suppose to work!? Think it should be if %ENDTIME% LSS %STARTTIME% set set /A DURATION=%DURATION%+8640000. Constant 8640000 is a number of centiseconds in one day.
23

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 fraction part for non-English localizations
    set STARTTIME=%STARTTIME:,=.%
    set ENDTIME=%ENDTIME:,=.%

    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

6 Comments

... you guys know batch files have GOSUB-like behavior, right? (via the CALL statement -- you can CALL a label, pass it parameters and it can pass back return values... you can google the syntax but it's not too hard).
Yes, when I used this in my script I called it like a function.
To account for ending after midnight, I would alter the second for loop to: 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" )
How to use your script inside FOR cycle?
@David Thanks David, I updated the code. Seemed to work but I didn't test if fully yet.
|
17

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)); }"

3 Comments

I chose this method because it only requires adding 2 lines of code to my existing batch files, all of which are running in environments that have PowerShell installed. K.I.S.S.
This is perfect for my needs. Change the last line perhaps to use the variable created on the line before it.
If you're using PowerShell anyway, use it directly for measuring command execution time with Measure-Command powershell -Command "(Measure-Command { timeout.exe 3 }).TotalSeconds"
4

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

Comments

3

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

Comments

2

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

2 Comments

Just in case centiseconds is negative, :centiseconds_to_time can check and prepend a minus sign. set "_n=" followed by if %_centiseconds% lss 0 set "_n=-" & set /A _centiseconds*=-1 then at the end: set _result=%_n%%_h%:%_m%:%_s%.%_hs% so that call :centiseconds_to_time -3000 test_neg gives test_neg = -00:00:30.00
Not needed in this example script, as duration is always non-negative.
2

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

Comments

2

CMD doesn't have time arithmetic. The following code, however does the job:

set end_time=11:07:48
set start_time=11:16:58


set length=%end_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
)

REM Get time1 in seconds
set /a t1=!h!+!m!+!s!

set length=%start_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
)

REM Get time2 in seconds
set /a t2=!h!+!m!+!s!
cls

REM Get time difference in seconds
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! 

Comments

1

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 . . .

1 Comment

A small update for an alternative of output format if we want that instead of hh:mm:ss,cs change the format to 00h 00m 00s we need to change the lines with: SET "_t=!_h!:!_m!:!_s!.!_cs!" SET "_t=!_t:00:=!" and replace them with the following code: IF "%_f%" == "hms" ( SET "_t=!_h!h !_m!m !_s!s" & SET "_t=!_t:00h=!" & SET "_t=!_t:00m=!" & SET "_t=!_t: =!" ) ELSE ( SET "_t=!_h!:!_m!:!_s!.!_cs!" & SET "_t=!_t:00:=!" ) Now we can use as a formatted parameter the sentence "hms" so that the output format would be 00h 00m 00s
1
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

2 Comments

While this code snippet may solve the problem, it doesn't explain why or how it answers the question. Please include an explanation for your code, as that really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. You can use the edit button to improve this answer to get more votes and reputation!
And by the way, there are at least 7 other answers with more or less the same solution, the accepted one is 9 years old. I can't see where your answer adds something new or a better approach. It looks even more complex and error prone than the other ones
0

I stumbled upon this issue today and used some of the above ideas to write this code snippet.

This is getting the result in just 4 lines of code! Which is much more compact than the other solutions mentioned.

@echo off

set strt=%time%
echo                  --- Started at : %strt% ---

set /P "=***Your process***"

set endt=%time%
echo                   --- Ended at : %endt% ---
echo/

:Calculating_Elapsed_time
set /A elpsd=(%endt:~0,2%-%strt:~0,2%)*360000 + (%endt:~3,2%-%strt:~3,2%)*6000 + (%endt:~6,2%-%strt:~6,2%)*100 + (%endt:~9,2%-%strt:~9,2%) 
if %elpsd% LSS 0 set /A elpsd=%elpsd% + 24*360000

:Display
set /A "cc=elpsd%%100+100,elpsd/=100,ss=elpsd%%60+100,elpsd/=60,mm=elpsd%%60+100,hh=elpsd/60+100"
echo Execution Time : %hh:~1%:%mm:~1%:%ss:~1%.%cc:~1%

echo/
pause

The +100s are to add zeros for displaying purposes. The if statement is correcting for next-day time.

Here is the output example:

                 --- Started at : 15:04:43.28 ---
***Your process***
                  --- Ended at : 15:04:46.56 ---

Execution Time : 00:00:03.28

Press any key to continue . . .

Comments

0

The prior answers offer a good summary, but if I call SET /A with two numbers starting with 0–e.g., set /a QQ=00-08—then SET /A interprets the values as octals and returns an "error":

08 is not a valid octal. The variable %time% must be corrected before doing the calculations.

To fix this, here is my version:


    C:\Users\mario.q59\Desktop>type ..\myScripts\tse.cmd
    @echo off & setlocal enabledelayedexpansion
    set N_ping=6
    
    echo\ "%~f0" %*
    echo\        Time "%time%"
    
    :::: for test
    :::: call this sripts with the value of your custom %time%
    if not "%~1" == "" (
        set "TIME_start=%~1" & REM custom time e.g " 0.00.00.00"
    ) else (
        set "TIME_start=%time%"
    )
    
    echo\  TIME_start="%TIME_start%"
    echo\-------------------------
    echo\ Wait
    echo\ ping -a -n %N_ping% localhost
    echo\-------------------------
    ping -a -n %N_ping% localhost >nul
    set "TIME_end=%time%"
    
    call :SANITIZE_time TS "%TIME_start%"
    call :SANITIZE_time TE "%TIME_end%"
    call :DIFF_time
    
    echo\ "%~f0" %*
    echo\    END %TIME_end%
    echo\  START %TIME_start%
    echo\-------------------
    if defined DAY_n (
        echo\ --dd=%DAY_n% %hh:~1%:%mm:~1%:%ss:~1%,%cc:~1%
    ) else (
        echo\   ---- %hh:~1%:%mm:~1%:%ss:~1%,%cc:~1%
    )
    :::::::::::::::::::::::::::::::::::::::::::
    :::::::::::::::::::::::::::::::::::::::::::
    :::: :SANITIZE_time waits for 2 parameters
    :::: %1 == TS or TE,
    :::: will give the name to the variable  %~1_hh %~1_mm %~1_ss %~1_cc
    ::::                         TIME Start   TS_hh  TS_mm  TS_ss  TS_cc
    ::::                         TIME End     TE_hh  TE_mm  TE_ss  TE_cc
    :::: %2 == "%time%" will give the value
    ::::                for the calculation  %~1_hh %~1_mm %~1_ss %~1_cc
    ::::
    :::: if I call "SET /A" with two numbers starting with 0
    :::: example "set /a QQ=00-08"
    :::: "SET /A" interprets the values as octals and returns "error".
    :::: 08 is not a valid octal
    :::: Here adjust the values.
    :SANITIZE_time
    if not "%0" == ":SANITIZE_time" goto :END_SANITIZE_time
        set "T=%~2"
        set "T=!T: =!"
        REM it is not important to know the delimiters of the LOCALE values
        set "%~1_cc=!T:~-2!" & REM last 2 numbers
        set "%~1_ss=!T:~-5,-3!"
        set "%~1_mm=!T:~-8,-6!"
        set "%~1_hh=!T:~0,-9!"
    
        if "!%~1_cc:~0,1!" EQU "0" set "%~1_cc=!%~1_cc:~1,2!"
        REM if not defined %~1_cc set "%~1_cc=0"
    
        if "!%~1_ss:~0,1!" EQU "0" set "%~1_ss=!%~1_ss:~1,2!"
        REM if not defined %~1_ss set "%~1_ss=0"
    
        if "!%~1_mm:~0,1!" EQU "0" set "%~1_mm=!%~1_mm:~1,2!"
        REM if not defined %~1_mm set "%~1_mm=0"
    
        if "!%~1_hh:~0,1!" EQU "0" set "%~1_hh=!%~1_hh:~1,2!"
        if not defined %~1_hh set "%~1_hh=0"
    goto :EOF
    :END_SANITIZE_time
    :DIFF_time
    if not "%0" == ":DIFF_time" goto :END_DIFF_time
        set /a "TIME_t=(%TE_hh%-%TS_hh%)*(60*60*100)"
        set /a "TIME_t=%TIME_t% + (%TE_mm%-%TS_mm%) * (60*100)"
        set /a "TIME_t=%TIME_t% + (%TE_ss%-%TS_ss%) * 100"
        set /a "TIME_t=%TIME_t% + (%TE_cc%-%TS_cc%)"
        if %TIME_t% LSS 0 set /a "TIME_t=%TIME_t% + 24*360000" & set /a DAY_n+=1
        set /a "cc=TIME_t%%100+100,TIME_t/=100,ss=TIME_t%%60+100,TIME_t/=60,mm=TIME_t%%60+100,hh=TIME_t/60+100"
    goto :EOF
    :END_DIFF_time
    :::::::::::::::::::::::::::::::::::::::::::
    :::::::::::::::::::::::::::::::::::::::::::
    
    :::: echo\
    :::: echo TS_hh="%TS_hh%" TS_mm="%TS_mm%" TS_ss="%TS_ss%" TS_cc="%TS_cc%"
    :::: echo TE_hh="%TE_hh%" TE_mm="%TE_mm%" TE_ss="%TE_ss%" TE_cc="%TE_cc%"
    :::: echo\
    :::: echo TIME_t = "%TIME_t%"
    endlocal
    
    C:\Users\mario.q59\Desktop>

Put the functions :SANITIZE_time-:DIFF_time in your scripts.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.