# Calculate Difference In Time Over Several Days

I've seen many questions and examples showing how to calculate the difference in two times by converting the times to epoch, and then converting the difference in seconds... However, what is the best way to calculate the difference in time that may span over several days in a \$DAY days, \$HOUR, hour(s), \$MIN minute(s), and \$SEC second(s)?

For my purpose, I'm using the date format, date +"%m/%d/%y %T" (08/17/18 09:03:31).

Let's say my first date is 08/15/18 16:22:05, and the second is 08/17/18 09:03:31. How can I calculate the difference by echoing "The difference is 1 day(s), 16, hours, 41 minutes, and 26 seconds."

But also, with the same script, how can it recognize if the time difference is less than a day to not report "The difference is 0 day..."?

EDIT: I have a mostly working script below, however, there's a lot going on in order to preserve proper formatting. Everything seems to work aside from the appropriate spaces and commas depending on if a value equals zero...

The idea is that it should format it with "and" in the appropriate spot when removing values that equal "0"...

So, all values: 02:05:23:44 2 days, 5 hours, 23 minutes, and 44 seconds

No hours: 02:00:23:44 2 days, 23 minutes, and 44 seconds

No seconds: 02:05:23:00 2 days, 5 hours, and 23 seconds

I had it so close awhile ago, but with certain readings, it'd break. Any help on formatting how I'd like? The script is also a long-mess... Any help cleaning it up would be nice. I'd like to see how to make it more efficient.

``````#!/bin/bash
TIME1="08/17/2018 11:36:40"
TIME2="08/17/2018 12:37:41"
SEC1=`date +%s -d "\${TIME1}"`
SEC2=`date +%s -d "\${TIME2}"`
DIFF=`expr \${SEC2} - \${SEC1}`

CONVERTTIME() {
((d=\${1}/86400))
((h=(\${1}%86400)/3600))
((m=(\${1}%3600)/60))
((s=\${1}%60))

#DAYS
if [ \$d -eq 1 ]; then
DAYLABEL=day
else
DAYLABEL=days
fi
DAYCOMMA=", "
if [[ \$d -gt 0 || \$h && \$m && \$s -eq 0 ]]; then
DAYCOMMA=""
fi
DAYTEXT="\$d \$DAYLABEL\$DAYCOMMA"
if [ \$d -eq 0 ]; then
DAYTEXT=
else
if [ \$h -eq 0 ]; then
DAYTEXT="\$d \$DAYLABEL\$DAYCOMMA"
fi
fi
#HOURS
if [ \$h -eq 1 ]; then
HOURLABEL=hour
else
HOURLABEL=hours
fi
HOURCOMMA=", "
if [[ \$h -gt 0 || \$m && \$s -eq 0 ]]; then
HOURCOMMA=""
fi
HOURTEXT="\$h \$HOURLABEL\$HOURCOMMA"
if [ \$h -eq 0 ]; then
HOURTEXT=""
else
if [ \$m -eq 0 ]; then
HOURTEXT="\$h \$HOURLABEL\$HOURCOMMA"
fi
fi
#MINUTES
if [ \$m -eq 1 ]; then
MINLABEL=minute
else
MINLABEL=minutes
fi
MINCOMMA=", "
if [[ \$m -gt 0 || \$s -eq 0 ]]; then
MINCOMMA=""
fi
MINTEXT="\$m \$MINLABEL\$MINCOMMA"
if [ \$m -eq 0 ]; then
MINTEXT=""
else
if [ \$s -eq 0 ]; then
MINTEXT="\$d \$MINLABEL\$MINCOMMA"
fi
fi
#SECONDS
if [ \$s -eq 1 ]; then
SECLABEL=second
else
SECLABEL=seconds
fi
SECTEXT="\$s \$SECLABEL"
if [ \$s -eq 0 ]; then
SECTEXT=""
fi

ANDHOUR=
ANDMIN=
ANDSEC=
if [[ \$d && \$h && \$m && \$s -gt 0 || \$d && \$h && \$s -gt 0 || \$d && \$m && \$s -gt 0 || \$h && \$m && \$s -gt 0 || \$d && \$s -gt 0 || \$h && \$s -gt 0 || \$m && \$s -gt 0 ]]; then
ANDSEC="and "
else
#days hours AND minutes
#days AND minutes
#hours AND minutes
if [[ \$d && \$h && \$m -gt 0 || \$d && \$m -gt 0 || \$h && \$m -gt 0 ]]; then
ANDMIN="and "
else
#days AND hours
if [[ \$d && \$h -gt 0 ]]; then
ANDHOUR="and "
fi
fi
fi
echo -e "\$DAYTEXT\$ANDHOUR\$HOURTEXT\$ANDMIN\$MINTEXT\$ANDSEC\$SECTEXT"
}
echo
echo "TIME DIFFERENCE: \$(CONVERTTIME \$DIFF)"
echo
``````

In some lines (assuming GNU date):

``````\$ diff=\$((\$(date -ud '08/17/18 09:03:31' +'%s') - \$(date -ud '08/15/18 16:22:05' +'%s')))
\$ days=\$((\$(date -ud @\$diff +'%-j')-1))
\$ date -ud @"\$diff" +"\$days"'-day(s) %H:%M:%S'
1-day(s) 16:41:26
``````

Valid up to (about) 365 days.

As an script:

``````#!/bin/sh

tosec(){ secs=\$(date -ud "\$1" +'%s'); }

tosec "\$1"; sec1=\$secs
tosec "\$2"; sec2=\$secs
diff=\$((sec2-sec1))
eval "\$(date -ud "@\$diff" +'days=%-j time=%H:%M:%S')"
printf '%s\n' "\$((days-1))-day(s) \$time"
``````

Call it as:

``````\$ ./script '08/15/18 16:22:05'   '08/17/18 09:03:31'
``````