36

What's an easy way to convert 00:20:40.28 (HH:MM:SS) to seconds with a Bash script?

Split seconds can be cut out, it’s not essential.

codeforester
  • 39,467
  • 16
  • 112
  • 140
Mint
  • 14,388
  • 30
  • 76
  • 108

13 Answers13

64

Try awk. As a bonus, you can keep the split seconds.

echo "00:20:40.25" | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }'
dreamlax
  • 93,976
  • 29
  • 161
  • 209
  • 1
    Thanks, this is a very clean way of doing it, just what I wanted. As well maths isn’t really my strong point. – Mint Feb 02 '10 at 04:47
  • 1
    Quite useful, thanks! To decimal hours: `echo "00:20:40.25" | awk -F: '{ print $1 + ($2/60) + ($3/3600) }'` – Nikos Alexandris Jan 23 '13 at 12:53
  • @dreamlax, if i may, can you please explain the command in the part of the awk -f? also, is there any option to output the value as minutes and not as seconds? – edwio Jun 29 '22 at 12:12
  • And for [the opposite direction](https://stackoverflow.com/a/13422982/4970442), `date --utc --date @1240.25 +"%T"` – Pablo Bianchi Jul 22 '23 at 21:26
7

This would work even if you don't specify hours or minutes: echo "04:20:40" | sed -E 's/(.*):(.+):(.+)/\1*3600+\2*60+\3/;s/(.+):(.+)/\1*60+\2/' | bc

Lri
  • 26,768
  • 8
  • 84
  • 82
4

Try this:

T='00:20:40.28'
SavedIFS="$IFS"
IFS=":."
Time=($T)
Seconds=$((${Time[0]}*3600 + ${Time[1]}*60 + ${Time[2]})).${Time[3]}
IFS="$SavedIFS"

echo $Seconds

($<string>) splits <string> based on the splitter (IFS).

${<array>[<index>]} returns the element of the <array> at the <index>.

$((<arithmetic expression>)) performs the arithmetic expression.

Hope this helps.

NawaMan
  • 25,129
  • 10
  • 51
  • 77
3

With GNU date, you can perform the conversion if the duration is less than 24 hours, by treating it as a time of day on the epoch:

to_seconds() {
    local epoch=$(date --utc -d @0 +%F)
    date --utc -d "$epoch $1" +%s.%09N
}

Running it with the example from the question:

$ to_seconds 00:20:40.29
1240.290000000

Note that --utc, @, %s and %N are all GNU extensions not necessarily supported by other implementations.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
3
echo "40.25" | awk -F: '{ if (NF == 1) {print $NF} else if (NF == 2) {print $1 * 60 + $2} else if (NF==3) {print $1 * 3600 + $2 * 60 + $3} }'
40.25
echo "10:40.25" | awk -F: '{ if (NF == 1) {print $NF} else if (NF == 2) {print $1 * 60 + $2} else if (NF==3) {print $1 * 3600 + $2 * 60 + $3} }'
640.25
echo "20:10:40.25" | awk -F: '{ if (NF == 1) {print $NF} else if (NF == 2) {print $1 * 60 + $2} else if (NF==3) {print $1 * 3600 + $2 * 60 + $3} }'
72640.25
wgzhao
  • 560
  • 5
  • 5
2

If you are processing a time from ps, mind you that the format 2-18:01 is also possible for 2 days, 19 hours, 1 minute. In that case you'll want to checkout: Parse ps' "etime" output and convert it into seconds

Community
  • 1
  • 1
kvz
  • 5,517
  • 1
  • 42
  • 33
2

You can convert minutes to hour, seconds, or minutes with bc command.

By example:

How many minutes for 1000 sec ?

$ echo 'obase=60;1000' | bc
02 00

then -> 2 min

How much hour for 375 min ?

$ echo 'obase=60;375'| bc
06 15

then -> 06h15

How days for 56 hours?

$ echo 'obase=24;56' | bc
02 08

then 02 days and 08 hours

bc with obase is extra!

Martin Tournoij
  • 26,737
  • 24
  • 105
  • 146
gnutux95
  • 31
  • 4
1

If you don't know what exactly do you have - SS, MM:SS or HH:MM:SS, like after youtube-dl --get-duration, then awk magic could be useful:

echo 12 | awk -F\: '{ for(k=NF;k>0;k--) sum+=($k*(60^(NF-k))); print sum }'
12
echo 35:12 | awk -F\: '{ for(k=NF;k>0;k--) sum+=($k*(60^(NF-k))); print sum }'
2112
echo 1:35:12 | awk -F\: '{ for(k=NF;k>0;k--) sum+=($k*(60^(NF-k))); print sum }'
5712
mikevmk
  • 11
  • 2
  • and works for this too: `echo 1:35:12.3 | awk -F\: '{ for(k=NF;k>0;k--) sum+=($k*(60^(NF-k))); print sum }'` gave `5712.3` – Badr Elmers May 18 '19 at 00:44
1

I think the easiest would be to count from the epoch time as:

$ date_var="00:20:40.28";
$ date +'%s.%3N' -d "1970-01-01 $date_var" --utc
1240.280

You can also easily change the format to your need.

Zstack
  • 4,046
  • 1
  • 19
  • 22
0

I haven't tested this but, I think this is how you'd split the string. Followed by multiplying by the appropriate amounts for hours and minutes.

mytime=’00:20:40.28′
part1=${mytime%%:*}; rest=${mytime#*:}
part2=${rest%%:*}; rest=${rest#*:}
part3=${rest%%:*};
netricate
  • 1,708
  • 2
  • 12
  • 13
0

with the shell,

#!/bin/bash

d="00:20:40.28"
IFS=":"
set -- $d
hr=$(($1*3600))
min=$(($2*60))
sec=${3%.*}
echo "total secs: $((hr+min+sec))"
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
0

I have this old shell function (/bin/sh compatible in the sense of POSIX shell, not bash) which does this conversion in integer math (no fractions in the seconds):

tim2sec() {
    mult=1
    arg="$1"
    res=0
    while [ ${#arg} -gt 0 ]; do
        prev="${arg%:*}"
        if [ "$prev" = "$arg" ]; then
            curr="${arg#0}"  # avoid interpreting as octal
            prev=""
        else
            curr="${arg##*:}"
            curr="${curr#0}"  # avoid interpreting as octal
        fi
        curr="${curr%%.*}"  # remove any fractional parts
        res=$((res+curr*mult))
        mult=$((mult*60))
        arg="$prev"
    done
    echo "$res"
}

Outputs:

$ tim2sec 1:23:45.243
5025

It works with SS, MM:SS and HH:MM:SS only :)

tzot
  • 92,761
  • 29
  • 141
  • 204
0
TZ=utc date +'%s' -d "1970-01-01 00:00:38"
张馆长
  • 1,321
  • 10
  • 11