Arithmetic operations with HH:MM:SS times in batch

2020-01-29 03:08发布

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.

4条回答
神经病院院长
2楼-- · 2020-01-29 03:34
@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
查看更多
forever°为你锁心
3楼-- · 2020-01-29 03:37

Here's a working prototype:

@echo off 

set T1=00:10:45
set T2=00:11:05

set HOUR1=%T1:~,2%
set MIN1=%T1:~3,-3%
set SEC1=%T1:~-2%

set HOUR2=%T2:~,2%
set MIN2=%T2:~3,-3%
set SEC2=%T2:~-2%

set /A TOTAL_SEC1=%HOUR1%*3600+%MIN1%*60+SEC1
set /A TOTAL_SEC2=%HOUR2%*3600+%MIN2%*60+SEC2
set /A DIFF=%TOTAL_SEC2%-%TOTAL_SEC1%

echo %DIFF%

Output:

20

Its not complete, but its a reasonable start.

查看更多
在下西门庆
4楼-- · 2020-01-29 03:48

This is possible to do in pure batch by parsing each field as an independent string, then doing arithmetic on them. Many practical solutions call into some other program to do the date math.

The following code calls into PowerShell to use the .NET DateTime class to do the parsing for you.

C:\> set "StartPosition=00:10:30"
C:\> set "EndPosition=00:10:40"
C:\> PowerShell.exe -c "$span=([datetime]'%EndPosition%' - [datetime]'%StartPosition%'); '{0:00}:{1:00}:{2:00}' -f $span.Hours, $span.Minutes, $span.Seconds"
00:00:10

This executes two lines of PowerShell code; one to convert both times into DateTime objects and subtract them, and the other to output the result in the format you specified.

查看更多
成全新的幸福
5楼-- · 2020-01-29 04:00

I think, @Aacini has cleared Everything here. He got you, Before I Do. But, I want to Improved on him as - by using For Loop to make code Easier.

Note: Everything after 'REM' is a Comment for the sake of understanding easily...
All You need to DO is Copy It into Your Batch File. And, Use it as follows (in your main code):

Syntax: Call :Time [Your Time 1] [Operation] [Your Time 2]

And, You can Now apply - any operation - including 'Addition, Substraction, Division, Multiplication' ;) The Time Function
--------------Copy the Below Code----------

:Time [Start_Time] [Operation] [End_Time]
SetLocal EnableDelayedExpansion
REM Creating a portable Function for your Job. :)

REM Reading Start-time...
For /F "Tokens=1,2,3 Delims=:" %%A in ("%~1") Do (
Set _Start_Hour=%%A
Set _Start_Min=%%B
Set _Start_Sec=%%C
)

REM Reading End-time...
For /F "Tokens=1,2,3 Delims=:" %%A in ("%~3") Do (
Set _End_Hour=%%A
Set _End_Min=%%B
Set _End_Sec=%%C
)

REM Removing leading Zero's - if any... 'CMD assumes it as octal - otherwise'
For %%A In (Hour Min Sec) Do (
    For %%B In (Start End) Do (
        IF /I "!_%%B_%%A:~0,1!" == "0" (Set _%%B_%%A=!_%%B_%%A:~1!)
        )
)

REM Applying Operation on the given times.
For %%A In (Hour Min Sec) Do (Set /A _Final_%%A=!_Start_%%A! %~2 !_End_%%A!)

REM Handling a little Exceptional errors! - due to the nature of time (60 sec for a min.)
SET _Extra_Hour=0
SET _Extra_Min=0

REM Two Cases can arise in each part of time...
:Sec_loop
IF %_Final_Sec% GTR 59 (Set /A _Extra_Min+=1 & Set /A _Final_Sec-=60 & Goto :Sec_loop)
IF %_Final_Sec% LSS 0 (Set /A _Extra_Min-=1 & Set /A _Final_Sec+=60 & Goto :Sec_loop)

Set /A _Final_Min+=%_Extra_Min%

:Min_loop
IF %_Final_Min% GTR 59 (Set /A _Extra_Hour+=1 & Set /A _Final_Min-=60 & Goto :Min_loop)
IF %_Final_Min% LSS 0 (Set /A _Extra_Hour-=1 & Set /A _Final_Min+=60 & Goto :Min_loop)

Set /A _Final_Hour+=%_Extra_Hour%

REM Saving Everything into a Single Variable - string.
Set _Final_Time=%_Final_Hour%:%_Final_Min%:%_Final_Sec%

REM Displaying it on the console. ;)
Echo.%_Final_Time%
Goto :EOF
--------------End OF Code----------------------

You can Also visit, my Website - based on Batch Programming. (www.thebateam.org) You'll find alot of stuff there - to help you out. :) Here's the Final Output - When I saved the Code in Answer.bat File

查看更多
登录 后发表回答