44

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

gregv21v
  • 68
  • 1
  • 8
Danny Gloudemans
  • 2,307
  • 6
  • 36
  • 54
  • 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. – Joey Mar 29 '12 at 09:23
  • 1
    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? – Danny Gloudemans Mar 29 '12 at 09:26
  • You could at least use WSH or PowerShell for automation. There are plenty of options nowadays. While I write batch files myself quite often and know how to solve many obscure things it's not a technology you should base a build or deployment process on if you can help it. – Joey Mar 29 '12 at 09:28
  • I just found the answer I think so. I'm going to try it now: @nusi stackoverflow.com/questions/605522/print-time-in-a-batch-file-milliseconds – Danny Gloudemans Mar 29 '12 at 09:28
  • 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.. – Danny Gloudemans Mar 29 '12 at 09: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 – jeb Mar 29 '12 at 09: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? – Danny Gloudemans Mar 29 '12 at 10:04
  • Maybe something like this - http://www.thesysadminhimself.com/2011/03/calculating-time-difference-in-batch.html - but as others have said it is far more complex in batch than it is in a real programming language – Bali C Mar 29 '12 at 10:17
  • @BaliC thanks mate! I already found it see my fourth posts above! I know it is more complex in a batch file. I'm studing Java, so I know that there are alot more methods then in a batch file. – Danny Gloudemans Mar 29 '12 at 10:54
  • @Gynnad Ah yeah I just read your other comments :) Great, Java will make your life much easier! – Bali C Mar 29 '12 at 11:17

11 Answers11

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

Aacini
  • 59,374
  • 12
  • 63
  • 94
  • 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. – Mike Q Jun 20 '13 at 14:53
  • 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). – BrainSlugs83 Sep 16 '14 at 19:25
  • And for the updated answer you have to replace `[...]Time:.` with `[...]Time:,` if you have ',' as decimal separator (locales are horrible). – Arsenal Jun 13 '17 at 12:20
  • @Arsenal: yes, but the locale independent adjustment is very simple. See my second edit... – Aacini Jun 14 '17 at 18:52
  • Thanks for that. For a reason out of my understanding the locale dependent solution stopped working today. So the update is highly appreciated. – Arsenal Jun 19 '17 at 06:46
  • There are unbalanced braces here: "elap=((((10!end:%time:~2,1%=%%100)*60+1!%%100)-((((10!start:%time:~2,1%=%%100)*60+1!%%100)" – Eugene Maksimov Feb 18 '18 at 17:13
  • @EugeneMaksimov: This is my count: the `((((` at beginning = 4 left parens. The `!end:%time:~2,1%=%%100)*60+1!` part contains one right paren that is !replaced! one time _for each colon in `end` variable_, and `end` variable contains _two_ colons. One right paren was already replaced in the previous line by the decimal point, so the count here is: 3 right parens. Plus the closing paren in `%%100)` part totals 4 right parens... The same count apply to `start` variable at right side of the minus sign. Where are the unbalanced parentheses? – Aacini Feb 18 '18 at 17:52
  • Add `if %elap% LEQ 0 set /A elap+=24*60*60*100` to properly go over midnight. – JosefZ Jan 18 '19 at 23:36
  • @JosefZ: Simpler: add `, elap-=(elap>>31)*24*60*60*100` at end of the same expression. I did it already in the code. Thanks! (a little bit too-much later) **`^_^`** – Aacini Jun 05 '20 at 19:37
42

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
Community
  • 1
  • 1
Danny Gloudemans
  • 2,307
  • 6
  • 36
  • 54
  • 2
    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.. – Mike Q Jun 20 '13 at 14:49
  • 5
    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. – Philm Mar 05 '14 at 11:16
  • 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! – DraxDomax Nov 17 '15 at 14:12
  • 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. – MKPS Apr 16 '16 at 00:11
22

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
Mike Q
  • 5,006
  • 2
  • 41
  • 53
  • ... 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). – BrainSlugs83 Sep 16 '14 at 19:26
  • Yes, when I used this in my script I called it like a function. – Mike Q Sep 18 '14 at 18:39
  • 1
    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" )` – David Jan 07 '19 at 19:50
  • How to use your script inside FOR cycle? – Andrey Apr 18 '19 at 15:33
  • @David Thanks David, I updated the code. Seemed to work but I didn't test if fully yet. – Mike Q Apr 30 '20 at 15:23
  • @Andrey Save yourself headache and create a function which you could call to do this discrete work. Very easy to set up, use global vars and you can use a CALL to set that data and print it as needed. Putting in side of for loops manually is far more work. – Mike Q Apr 30 '20 at 15:28
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
yinon
  • 819
  • 6
  • 11
  • 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` – Jesse Chisholm Jun 28 '18 at 17:10
  • Not needed in this example script, as duration is always non-negative. – Jesse Chisholm Jun 28 '18 at 17:11
2

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
Dirk Schiller
  • 430
  • 5
  • 14
2

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
Community
  • 1
  • 1
AveYo
  • 151
  • 4
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

sst
  • 1,234
  • 1
  • 10
  • 13
2

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)); }"
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 . . .
Community
  • 1
  • 1
PHC
  • 37
  • 1
  • 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 – PHC May 18 '15 at 11:22
0

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! 
Zimba
  • 1,095
  • 9
  • 13
0
@echo off

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 %hours% lss 0 set /a hours = 24%hours%
if %mins% lss 0 set /a hours = %hours% - 1 & set /a mins = 60%mins%
if %secs% lss 0 set /a mins = %mins% - 1 & set /a secs = 60%secs%
if %ms% lss 0 set /a secs = %secs% - 1 & set /a ms = 100%ms%
if 1%ms% lss 100 set ms=0%ms%

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
mhm313
  • 1
  • 2
    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](https://meta.stackexchange.com/q/114762/269535), 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! – Brian Tompsett - 汤莱恩 May 09 '21 at 10:57
  • 1
    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 – jeb May 09 '21 at 13:20