How to add/subtract sexagesimal time values in Bash?

Viewed 180

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
2 Answers

Another AWK solution

This awk program can accept more than just two 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
}' <<< "$*"
}

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?

Related