2

I'm building a script to automate some audio file editing with ffmpeg, but I'm currently having to add and subtract time values by hand. How can I do this programmatically? Like:

$ foo 1:45.5 + 1:30.2
3:15.7
oguz ismail
  • 1
  • 16
  • 47
  • 69
BlueBaron
  • 29
  • 2
  • Basically, you would convert them manually to seconds (splitting on the colon), add the seconds and then convert it back to the desired output format. Since bash can not handle fractions, you would use some "language" which can do this (awk, zsh, Perl, Ruby,...), at least for the part of adding the seconds. Basically I wouldn't do the whole stuff in bash, but in a language which does have a time data type (such as Ruby). – user1934428 Jul 05 '21 at 05:52
  • 2
    You can refer to this.-- https://stackoverflow.com/questions/38491986/increment-hours-minutes-seconds-of-date-in-yyyymmddhhmmss-using-bash – Learner Jul 05 '21 at 05:59
  • 1
    You could consider [this answer to *Convert date time string to epoch in Bash*](https://stackoverflow.com/a/33918105/1765658) – F. Hauri - Give Up GitHub Jul 05 '21 at 06:25
  • 1
    ```date``` itself can handle fractions and is a great "language" for converting seconds into a time line ( human readable ;-) ) output. Try this shellfunction: ```dtlfe(){(date --date="${1}" +'date time line format example%n%H:%M:%S.%N')}``` and use: ```dtlfe @-3600.0000000001``` – koyaanisqatsi Jul 05 '21 at 08:27
  • 1
    What should happen when the result is bigger than 60 minutes, or even 24 hours? For example, what is the expected output for `59:00.0 + 1:00.0`? – Socowi Jul 05 '21 at 08:31
  • 1
    @Socowi `59:00 + 1:00` should equal 1:00:00. AV editing doesn't really deal with days so in this case `24:00:00 + 1:00:00` should equal 25:00:00. – BlueBaron Jul 06 '21 at 02:22

2 Answers2

1

Another AWK solution

This awk program can accept an arbitrary number of operands.

The syntax is a bit strict, a space is mandatory between operand and operators, i.e. "1+1" does not work. Also, it should be made more robust against malformed time intervals by improving the regexp.

$ cat timesum.awk
BEGIN {
  FS = ":"
  RS = " "
  sum = 0
  sign = "+"
}

/[+-]/ {
  sign = $0
}

/[[:digit:]]+/ {
  b = (sign == "+") ? 1 : -1
  for (i=NF; i>0; --i) {
    sum += $i * b
    b *= 60
  }
}

END {
  if (sum < 0) {
    sum = -sum
    printf "-"
  }
  printf "%d:%02d:%06.3f\n", sum/3600, sum%3600/60, sum%60
}

Some examples:

$ awk -f timesum.awk <<< "1:59:59.1 + 0.9"
2:00:00.000
$ awk -f timesum.awk <<< "20 + 20 + 20"
0:01:00.000
$ awk -f timesum.awk <<< "20 + 20 - 40"
0:00:00.000
$ awk -f timesum.awk <<< "1:00 - 1:00:00"
-0:59:00.000

Edit

If you want to skip leading zero fields, replace the last line with this:

  mf = "%d:"
  sf = "%.3f\n"
  if (sum >= 3600) {
    printf "%d:", sum/3600
    mf = "%02d:"
  }
  if (sum >= 60) {
    printf mf, sum%3600/60
    sf = "%06.3f\n"
  }
  printf sf, sum%60

Edit 2

Here's a version you can include and use in existing bash scripts:
function timecalc() { awk '
BEGIN { FS = ":"; RS = " "; base = 1 }
/^\+$/ { base = 1 }
/^-$/ { base = -1 }
/[[:digit:]]+/ {
  for (i=NF; i>0; --i) { sum += $i * base; base *= 60 }
  base = 1 # assume + if operand is missing
}
END {
  if (sum < 0) { sum = -sum; printf "-" }
  printf "%d:%02d:%06.3f\n", sum/3600, sum%3600/60, sum%60
}' <<< "$*"
}
David
  • 166
  • 6
0

awk solution

Edit 1: Hours added

Edit 2: Sum and subtract operators added

Create an sh file:

$ cat calctime.sh 
#!/bin/bash
first=$1
operator=$2
second=$3
printf "$first:$second" |
awk -F: -v oper=$operator '{ 
    if(oper=="+") 
        secs = (60*($1+$3) + ($2+$4));
    else if(oper=="-") 
        secs = ((60*$1+$2) - (60*$3+$4));
    if(secs>=3600)
        printf "%02d:%02d:%s\n", secs/3600, secs%3600/60, secs%60;
    else    
        printf "%02d:%s\n", secs%3600/60, secs%60}'

Expected output:

$ sh calctime.sh 40:25.4 - 25:10.7
15:14.7
$ sh calctime.sh 40:25.4 + 25:10.7
01:05:36.1
$ sh calctime.sh 8:51.5 + 25:10.7
34:2.2

Please refer to this post for awk usage https://stackoverflow.com/a/2181764/14320738.

Does this answer your question?