batch-filecmd

Arithmetic operations with HH:MM:SS times in batch file


In one of my batch scripts I need to calculate the duration of an interval in a video file. First the user is asked to input the start and end times:

set /p StartPosition=Start position (HH:MM:SS):
set /p EndPosition=End position (HH:MM:SS):

Then, I would like the batch script to calculate the duration in between.

How can I subtract %StartPosition% from %EndPosition% like this, for example:

00:10:40 - 00:10:30 = 00:00:10

The reason why I can't figure out how to do this is because these numbers are separated by colons.

Edit: This question is different to this question because I do not need the scrip to treat the numbers as time values.


Solution

  • @echo off
    setlocal
    
    set /p "StartPosition=Start position (HH:MM:SS): "
    set /p "EndPosition=End position (HH:MM:SS):   "
    
    set /A "ss=(((1%EndPosition::=-100)*60+1%-100)-(((1%StartPosition::=-100)*60+1%-100)"
    set /A "hh=ss/3600+100,ss%%=3600,mm=ss/60+100,ss=ss%%60+100"
    
    echo Duration=%hh:~1%:%mm:~1%:%ss:~1%
    

    EDIT: Some explanations added

    This program use the usual method to convert a time in HH:MM:SS format into a number of seconds via the standard formula: seconds = (HH*60+MM)*60+SS. However, the set /A command consider the numbers that start with 0 as written in octal base, and hence 08 and 09 would be invalid octal numbers. To avoid this problem, a digit 1 is placed before expand the number and a 100 is subtracted after, so if HH=08 then 1%HH%-100 correctly gives 8; that is:

    set /A seconds = ((1%HH%-100)*60+1%MM%-100)*60+1%SS%-100
    

    There are several methods to split a time given in HH:MM:SS format into its three parts. For example, if we take set EndPosition=HH:MM:SS as base, then we may use a for /F command this way:

    for /F "tokens=1-3 delims=:" %%a in ("%EndPosition%") do (
       set /A "seconds=((1%%a-100)*60+1%%b-100)*60+1%%c-100"
    )
    

    In this program a different method is used. If we match the original EndPosition=HH:MM:SS string with the desired formula, we may construct this mapping scheme:

         HH       :      MM       :      SS
    
    ((1  HH  -100)*60+1  MM  -100)*60+1  SS  -100
    

    In other words: if we replace the colons of the original string by -100)*60+1 and insert ((1 at beginning and -100 at end, we obtain the desired formula; that is:

    set /A "seconds=((1%EndPosition::=-100)*60+1%-100"
    

    This is a very efficient method that even allows to replace both EndPosition and StartPosition strings in the same formula (enclosing both parts in parentheses) and directly subtract them:

    set /A "ss=(((1%EndPosition::=-100)*60+1%-100)-(((1%StartPosition::=-100)*60+1%-100)"
    

    You may cancel the @echo off command and run the program to review the exact formula that is evaluated after the values of the variables are replaced. For example, when StartPosition=00:10:30 and EndPosition=00:10:40, this is the expression that is evaluated:

    set /A "ss=(((100-100)*60+110-100)*60+140-100)-(((100-100)*60+110-100)*60+130-100)"
    

    Just to complete this description, this is the "standard" way to evaluate the same formula using a for /F command:

    for /F "tokens=1-6 delims=:" %%a in ("%EndPosition%:%StartPosition%") do (
       set /A "ss=(((1%%a-100)*60+1%%b-100)*60+1%%c-100)-(((1%%d-100)*60+1%%e-100)*60+1%%f-100)"
    )
    

    The opposite conversion from number of seconds to HH:MM:SS parts is straightforward:

    HH=SS/3600, rest=SS%3600, MM=rest/60, SS=rest%60
    

    However, each part in the result must be displayed with two digits, but this formatting may be achieved in a very simple way. Instead of insert three if commands that check if each part is less than 10 and insert a padding zero in such a case, the number 100 is just added to the parts (converting an 8 into 108, for example), and when each part is displayed the first digit is omitted (so just 08 is shown). This is a very efficient method to format numbers that may be performed in the same set /A command used to obtain the parts. For example:

    set /A "hh=ss/3600+100,ss%%=3600,mm=ss/60+100,ss=ss%%60+100"
    
    echo Duration=%hh:~1%:%mm:~1%:%ss:~1%
    

    In this way, the conversion of two times into two number of seconds, their subtraction and the opposite conversion and formatting to HH:MM:SS is performed in two SET /A commands, that even may be written in a single, long line.

    Output examples:

    Start position (HH:MM:SS): 00:10:30
    End position (HH:MM:SS):   00:10:40
    Duration=00:00:10
    
    Start position (HH:MM:SS): 00:10:45
    End position (HH:MM:SS):   00:11:05
    Duration=00:00:20