Adding a List of Times

Hey gang, I have a list of times I need to sum up. This list can vary from a few to a few thousand entries. Now I had found a closed reference to adding time titled "add up time with xx:yy format in bash how?" In it, the example works great for that formatted list of times... This is the reply code from user AGAMA in that thread:

list="1:11 0:13 2:06 1:38 1:36 0:06 0:31 0:33 0:38 0:44"

h=0
m=0
for x in ${list}
do
    h=$(( h + ${x%%:*} ))   # add hours and minutes
    m=$(( m + ${x##*:} ))
done

h=$(( h + (m /60) ))    # minutes are likely more than 60, calc hours and add in 
m=$(( m % 60 ))     # adjust minutes

echo "${h}hrs ${m}min

"

BUT what I'm getting hung up on is how would I introduce the third parameter of seconds to this, if my times had the three columns?

I'm not quite verse enough in scripting to understand the formatting of the read in the 'do' loop. Are the '%' and '#' arbitrary or necessary for what they're representing?

In the other thread Agama tries to explain parameter expansion in a latter reply, but try as I may, I'm not wrapping my shrunken brain around this. I do need a bit of assistance trying to figure it out and adding in the seconds to tally up.

Thank you.

Maybe something more like:

#!/bin/ksh
list="1:11:59 0:13:58 2:06:57 1:38:56 1:36:55 0:06:54 0:31:53 0:33:52 0:38:51 0:44:50"

h=0
m=0
s=0
while IFS=':' read H M S
do
    h=$(( h + H ))   # add hours, minutes, and seconds
    m=$(( m + M ))
    s=$(( s + S ))
done <<-EOF
	$(printf '%s\n' $list)
EOF

m=$(( m + (s / 60) ))	# Add seconds overflow into minutes.
s=$(( s % 60 ))		# Remove whole minutes from seconds.

h=$(( h + (m / 60) ))	# Add minutes overflow into hours.
m=$(( m % 60 ))		# Remove whole hours from minutes.

printf '%d:%02d:%02d\n' "$h" "$m" "$s"

which produces the output:

9:25:05
2 Likes
#!/bin/bash

list="1:11:00 0:13:03 2:06:45"

h=0
m=0
s=0
for x in ${list}
do
        echo ${x}
        h=$(( h + ${x%%:*} ))
        Min=$(echo ${x%:*} | cut -d: -f2)
        m=$(( m + ${Min} ))
        s=$(( s + ${x##*:} ))
        #echo "H : ${h}"
        #echo "M : ${m}"
        #echo "S : ${s}"
done

m=$(( m + (s/60) ))
h=$(( h + (m/60) ))
m=$(( m % 60 ))
s=$(( s % 60 ))

echo "${h}hrs ${m}min ${s}sec
1 Like

Note that I used a Korn shell in post #2 and itkamaraj used bash in post #3. If you use bash , you will need to strip leading zeros from minutes and seconds values 08 and 09 . The Korn shell treats those strings as decimal 8 and 9 , respectively; but bash treats them as invalid octal numbers.

You can also do this just using variable expansions without resorting to using cut to extract the minutes with something more like:

#!/bin/bash

list="1:51:59 0:33:08 2:06:41"

h=0
m=0
s=0
for x in ${list}
do
        echo ${x}
	S=${x##*:}	# Extract seconds.
	x=${x%:$S}	# Strip seconds from x.
	S=${S#0}	# Strip leading 0 from S.

	M=${x##*:}	# Extract minutes.
	M=${M#0}	# Strip leading 0 from M.

	H=${x%:*}	# Extract hours (assume there is no leading 0 here.

        h=$(( h + H ))
        m=$(( m + M ))
        s=$(( s + S ))
        #echo "H : ${h}"
        #echo "M : ${m}"
        #echo "S : ${s}"
done

m=$(( m + (s/60) ))
h=$(( h + (m/60) ))
m=$(( m % 60 ))
s=$(( s % 60 ))

echo "${h}hrs ${m}min ${s}sec"

which produces the following output when run with either bash or ksh :

1:51:59
0:33:08
2:06:41
4hrs 31min 48sec

Note that I modified the time values in $list to provoke bash complaints with bad octal values. If we change the code in post #3 to use the above list (and add the missing " at the end of the echo statement on the last line), we get the output:

1:51:59
0:33:08
script_name: line 14: s + 08: value too great for base (error token is "08")
2hrs 24min 59sec
3 Likes

Hi.

The dateutils suite contains a code that can add date/time durations: dadd . However, the format of the durations needs to be not in a form like 1:51:59 , but rather 1h51m59s . That can be done with a little pipeline, and then presented to dadd , like so:

#!/usr/bin/env bash

# @(#) s1       Demonstrate addition of a list of times, dateutils.dadd

# Utility functions: print-as-echo, print-line-with-visual-space, debug.
# export PATH="/usr/local/bin:/usr/bin:/bin"
LC_ALL=C ; LANG=C ; export LC_ALL LANG
pe() { for _i;do printf "%s" "$_i";done; printf "\n"; }
pl() { pe;pe "-----" ;pe "$*"; }
em() { pe "$*" >&2 ; }
db() { ( printf " db, ";for _i;do printf "%s" "$_i";done;printf "\n" ) >&2 ; }
db() { : ; }
C=$HOME/bin/context && [ -f $C ] && $C dateutils.dadd

list="1:11:59 0:13:58 2:06:57 1:38:56 1:36:55 0:06:54 0:31:53 0:33:52 0:38:51 0:44:50"

pl " Input data list:"
pe "$list"

pl " Input list modified:"
v1=$( echo "$list" |
tr ' ' '\n' |
sed 's/:/h/;s/:/m/;s/$/s/' |
tr '\n' ' ')
echo "$v1"

pl " Results:"
dateutils.dadd -f "%T" 00:00:00 $v1 

exit 0

producing:

$ ./s1

Environment: LC_ALL = C, LANG = C
(Versions displayed with local utility "version")
OS, ker|rel, machine: Linux, 3.16.0-4-amd64, x86_64
Distribution        : Debian 8.4 (jessie) 
bash GNU bash 4.3.30
dateutils.dadd dadd 0.3.1

-----
 Input data list:
1:11:59 0:13:58 2:06:57 1:38:56 1:36:55 0:06:54 0:31:53 0:33:52 0:38:51 0:44:50

-----
 Input list modified:
1h11m59s 0h13m58s 2h06m57s 1h38m56s 1h36m55s 0h06m54s 0h31m53s 0h33m52s 0h38m51s 0h44m50s 

-----
 Results:
09:25:05

The dateutils suite can be found at dateutils, and in repositories for ArchLinux, Debian, Fedora, FreeBSD, Gentoo, NetBSD, OpenSuSE, OS, Slackware, Ubuntu

Best wishes ... cheers, drl

1 Like

Gang, this does put things in a better light in my brain...
Don... yes, I am using KSH...
But seeing this same problem solved in multiple ways helps me grasp the entire processing concept.
And now I do have a running process that's cranking out and summing up time numbers. But if I am stuck in another shell or environment, well, now I have these other options also.
Thank you very much for your time gang!
Bruce