1

I am trying to calculate the number of days between two dates like this:

$ echo $((($(date +%s -d 2016/11/22)-$(date +%s -d 2016/11/20))/(3600*24))) days
2 days

That is the expected answer and perfectly consistent with this:

$ date -d '2016/11/22 - 2 days'
Sun Nov 20 00:00:00 CET 2016

However, these two seem to be inconsistent:

$ echo $((($(date +%s -d 2020/06/28)-$(date +%s -d 2016/11/20))/(3600*24))) days
1315 days

$ date -d '2020/06/28 - 1315 days'
Mon Nov 21 00:00:00 CET 2016

Am I missing something? Why don't I get 1316 days (instead of 1315) in the third command I used?

I have done a few more tests changing the month in the date 2020/06/28. It seems that up to March I get the expected answer (i.e., I get Nov 20 in the fourth command), but from April on the inconsistency is present (i.e., I get Nov 21 in the fourth command). Any hints?

1
  • integer overflow? wrong arithmetic expansion?
    – alecxs
    Aug 4, 2020 at 16:55

2 Answers 2

4

Consider the following:

$ date +%s -d 2020/06/28
1593316800
$ date +%s -d 2016/11/20
1479618000
$ echo $(( 1593316800 - 1479618000 ))
113698800
$ echo $(( 113698800 / (3600*24) ))
1315                # integer, no rounding

$ echo $(( (3600*24) ))
86400
$ echo "113698800/86400" | bc
1315
$ echo "113698800/86400" | bc -l
1315.9583333333333333333
$ printf "%.0f\n" $(echo "113698800/86400" | bc -l)
1316                 # rounded to nearest integer

Similar issue with ksh2020 calculations:

$ printf "%(%s)T\n" 2020/06/28
1593365691
$ printf "%(%s)T\n" 2016/11/20
1479667133
$ echo $(( 1593365691 - 1479667133 ))
113698558
$ echo $(( 113698558/86400 ))
1315
$ echo "113698558/86400" | bc -l
1315.95553240740740740740

Setting the timezone to UTC solves the issue.

$ cat test.sh
#!/bin/bash

edate="$1"
sdate="$2"

medate=$(date +%s -d $edate)
msdate=$(date +%s -d $sdate)
secs=$(( medate - msdate ))

echo $(( secs / 86400))
echo "$secs / 86400" | bc -l
$
$ ./test.sh 2020/06/28 2016/11/20
1315
1315.95833333333333333333
$ sudo timedatectl set-timezone UTC
$  ./test.sh 2020/06/28 2016/11/20
1316
1316.00000000000000000000

Alternatively, and easier, you can use the date -u option to produce the correct result.

-u, --utc, --universal print or set Coordinated Universal Time (UTC)

It would appear to be a daylight savings issue, possibly specific to your timezone setting.

2

Your answer is spot on, fpmurphy. Thank you very much.

The change from CET to CEST took place on the last Sunday of March, 2020/03/29. Hence the missing 3600 seconds from April on.

I get the correct answer just adding the option -u to date:

$ echo $((($(date +%s -u -d 2020/06/28)-$(date +%s -u -d 2016/11/20))/(3600*24))) days
1316 days

Obviously you would get the same just by adding the missing hour (3600 seconds) manually:

$ echo $((($(date +%s -d 2020/06/28)-$(date +%s -d 2016/11/20)+3600)/(3600*24))) days
1316 days

(If you try with 3599 instead of 3600, it goes back to 1315 days).

1
  • "Obviously you would get the same just by adding the missing hour (3600 seconds) manually" only for timezones that switch by an hour twice yearly. You shouldn't assume that
    – roaima
    Feb 15, 2021 at 20:24

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .