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 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 .