5

I attempted to get the difference between two dates with:

date -d $(echo $(date -d "Sun Dec 29 02:22:19 2013" +%s) - $(date -d "Sun Dec 28 03:22:19 2013" +%s) | bc)

However, this returns

date: invalid date ‘82800’

Why is this, and how can I get the desired result?

gandalf3
  • 1,636
  • 4
  • 24
  • 40
  • 1
    What format do you want the difference to appear as? 82800 is the number of seconds in 23 hours, which is the difference in time. But the outer `date -d` wants something recognizable as a date. – Jonathan Leffler Dec 30 '13 at 00:41
  • You might find http://www.unix.com/tips-tutorials/31944-simple-date-time-calulation-bash.html helpful – Floris Dec 30 '13 at 00:43
  • @JonathanLeffler The same format as the two input dates – gandalf3 Dec 30 '13 at 00:45
  • 1
    That's nonsense - an interval of time cannot be represented as a date. Or, rather, it can, but the answer (a) involves fudging the units and (b) ends up as a time on 1970-01-01. – Jonathan Leffler Dec 30 '13 at 00:46
  • @JonathanLeffler Good point. Is there some way I can specify the date format with the usual `+%` sequences? – gandalf3 Dec 30 '13 at 00:50

1 Answers1

9

You can use:

date1="Sat Dec 28 03:22:19 2013"
date2="Sun Dec 29 02:22:19 2013"
date -d @$(( $(date -d "$date2" +%s) - $(date -d "$date1" +%s) )) -u +'%H:%M:%S'

And the output is:

23:00:00

However, for arbitrary differences over 24 hours, you have to start worrying about how the value for days will be represented. Negative values will also present problems.

The problem with this is that if you drop the format string, the date presented is:

Thu Jan  1 23:00:00 UTC 1970

So, while this works for the two given date/time values, it is not really a general solution.

Consider adding %j for the day of year; it will work semi-sanely for up to 1 year's difference.

Otherwise, capture the difference in a variable and present the results with a separate calculation.

date1="Sat Dec 28 03:22:19 2013"
date2="Sun Dec 29 02:22:19 2013"
delta=$(( $(date -d "$date2" +%s) - $(date -d "$date1" +%s) ))
if [[ $delta -lt 0 ]]
then sign="-"; delta=${delta/^-/}
else sign="+"
fi
ss=$(( $delta % 60 ))
delta=$(( $delta / 60 ))
mm=$(( $delta % 60 ))
delta=$(( delta / 60 ))
hh=$(( $delta % 24 ))
dd=$(( $delta / 24 ))
printf "$sign%d %.2d:%.2d:%.2d\n" $dd $hh $mm $ss

Output:

+0 23:00:00

(Meaning: a positive difference of 0 days, 23 hours, 0 minutes, 0 seconds.) Splitting a number of days such as 1000 into years, months and days is fraught without any reference dates. Here, there are reference dates to help, but it would still be ... fun.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • your code will be clearer if you write it like this: ``if (( delta < 0 ))`` and ``((ss=delta % 60))`` and so on – Aleks-Daniel Jakimenko-A. Dec 30 '13 at 13:09
  • Why are both Dec 28 and 29 Sundays? – Edasaur Jan 29 '15 at 18:52
  • @Edasur: I thought I'd go for an unusual calendar system! Thanks; fixed. It goes to show that the date conversion process doesn't pay attention to day of the week when parsing the date string. – Jonathan Leffler Jan 29 '15 at 18:56
  • I had to run `LANG=en_US date` to get compatible output being on a non-English system. Always remember to check your code for international compatibility. – Zdenek Feb 07 '22 at 09:16