# Quickly calculate date differences

I often want to make some quick date calculations, such as:

• What is the difference between these two dates?
• What is the date n weeks after this other date?

I usually open a calendar and count the days, but I think there should be a program/script that I can use to do these kinds of calculations. Any suggestions?

-
See also Tool in UNIX to subtract dates for when GNU date is not available. – Gilles Nov 15 '11 at 23:26

The "n weeks after a date" is easy with GNU date(1):

``````\$ date -d 'now + 3 weeks'
Tue Dec  6 23:58:04 EST 2011
\$ date -d 'Aug 4 + 3 weeks'
Thu Aug 25 00:00:00 EST 2011
\$ date -d 'Jan 1 1982 + 11 weeks'
Fri Mar 19 00:00:00 EST 1982
``````

I don't know of a simple way to calculate the difference between two dates, but you can wrap a little logic around date(1) with a shell function.

``````datediff() {
d1=\$(date -d "\$1" +%s)
d2=\$(date -d "\$2" +%s)
echo \$(( (d1 - d2) / 86400 )) days
}
\$ datediff '1 Nov' '1 Aug'
91 days
``````

Swap `d1` and `d2` if you want the date calculation the other way, or get a bit fancier to make it not matter. Furthermore, in case there is a non-DST to DST transition in the interval, one of the days will be only 23 hours long; you can compensate by adding ½ day to the sum.

``````echo \$(( (((d1-d2) > 0 ? (d1-d2) : (d2-d1)) + 43200) / 86400 )) days
``````
-

There's also GNU unit's time calculations combined with GNU date:

``````\$ gunits \$(gdate +%s)sec-\$(gdate +%s -d -1234day)sec 'yr;mo;d;hr;min;s'
3 yr + 4 mo + 16 d + 12 hr + 37 min + 26.751072 s
\$ gunits \$(gdate +%s -d '2015-1-2 3:45:00')sec-\$(gdate +%s -d '2013-5-6 7:43:21')sec 'yr;mo;d;hr;min;s'
1 yr + 7 mo + 27 d + 13 hr + 49 min + 26.206759 s
``````

(gunits is units in Linux, gdate is date)

-

This came up when using `date -d "\$death_date - \$y years - \$m months - \$d days"` to get a birth date (for genealogy). That command is WRONG. Months aren't all the same length, so `(date + offset) - offset != date`. Ages, in year/month/day, are measures going forwards from the date of birth.

``````\$ date --utc -d 'mar 28 1867 +72years +11months +2days'
Fri Mar  1 00:00:00 UTC 1940

\$ date --utc -d 'mar 1 1940 -72years -11months -2days'
Sat Mar 30 00:00:00 UTC 1867
# (2 days later than our starting point)
``````

Date gives the correct output in both cases, but in the second case you were asking the wrong question. It matters WHICH 11 months of the year the +/- 11 cover, before adding/subtracting days. For example:

``````\$ date --utc -d 'mar 31 1939  -1month'
Fri Mar  3 00:00:00 UTC 1939
\$ date --utc -d 'mar 31 1940  -1month' # leap year
Sat Mar  2 00:00:00 UTC 1940
\$ date --utc -d 'jan 31 1940  +1month' # leap year
Sat Mar  2 00:00:00 UTC 1940
``````

For subtracting to be the inverse operation of adding, the order of operations would have to be reversed. Adding adds years, THEN months, THEN days. If subtracting used the opposite order, then you'd get back to your starting point. It doesn't, so you don't, if the days offset crosses a month boundary in a different length month.

If you need to work backwards from an end date and age, you could do it with multiple invocations of `date`. First subtract the days, then the months, then the years. (I don't think it's safe to combine the years and months in a single `date` invocation, because of leap years altering the length of February.)

-

Another way to calculate the difference between two dates of the same calendar year you could use this:

``````date_difference.sh
1  #!/bin/bash
2  DATEfirstnum = `date -d "2014/5/14" +"%j"`
3  DATElastnum = `date -d "12/31/14" +"%j"`
4  DAYSdif=\$((\$DATElastnum - \$DATEfirstnum))
5  echo \$DAYSdir
``````
• Line 1 declares to the shell which interpreter to use.
• Line 2 assigns the value from the out of `date` to the variable DATEfirstnum. The `-d` flag displays the string in a time format in this case May 14th 2014 and `+"%j"` tells `date` to format the output to just the day of the year (1-365).
• Line 3 is the same as Line 2 but with a different date and different format for the string, December 31st, 2014.
• Line 4 assigns the value `DAYSdif` to the difference of the two days.
• Line 5 displays the value of `DAYSdif`.

This works with the GNU version of `date`, but not on the PC-BSD/FreeBSD version. I installed `coreutils` from ports tree and used the command `/usr/local/bin/gdate` instead.

-
This script will not run. There is a typo on the last line and spaces around the variable assignments, so bash is attempting to run a program called DATEfirst name with two arguments. Try this: `DATEfirstnum=\$(date -d "\$1" +%s)` `DATElastnum=\$(date -d "\$2" +%s)` Also, this script will not be able to calculate the difference between two different years. `+%j` refers to day of year (001..366) so `./date_difference.sh 12/31/2001 12/30/2014` outputs -1. As other answers have noted you need to convert both dates into seconds since 1970-01-01 00:00:00 UTC. – Six Feb 28 '15 at 13:29

If a graphical tool is OK for you, I heartily recommend `qalculate` (a calculator with an emphasis on unit conversions, it comes with a GTK and KDE interface, IIRC). There you can say e.g.

``````days(1900-05-21, 1900-01-01)
``````

to get the number of days (140, since 1900 was not a leap year) between the dates, but of course you can also do the same for times:

``````17:12:45 − 08:45:12
``````

yields `8.4591667` hours or, if you set the output to time formatting, `8:27:33`.

-
That's great, and even more so because qalculate does have a CLI. Try `qalc`, then `help days`. – Sparhawk Jul 4 '14 at 12:19

I frequently use SQL for date calculations. For example MySQL, PostgreSQL or SQLite:

``````bash-4.2\$ mysql <<< "select datediff(current_date,'1980-06-14')"
datediff(current_date,'1980-06-14')
11477

bash-4.2\$ psql <<< "select current_date-'1980-06-14'"
?column?
----------
11477
(1 row)

bash-4.2\$ sqlite2 <<< "select julianday('now')-julianday('1980-06-14');"
11477.3524537035
``````

Other times I just feel in mood for JavaScript. For example SpiderMonkey, WebKit, Seed or Node.js:

``````bash-4.2\$ js -e 'print((new Date()-new Date(1980,5,14))/1000/60/60/24)'
11477.477526192131

bash-4.2\$ jsc-1 -e 'print((new Date()-new Date(1980,5,14))/1000/60/60/24)'
11477.47757960648

bash-4.2\$ seed -e '(new Date()-new Date(1980,5,14))/1000/60/60/24'
11477.4776318287

bash-4.2\$ node -pe '(new Date()-new Date(1980,5,14))/1000/60/60/24'
11624.520061481482
``````

(Watch out when passing the month to the JavaScript `Date` object's constructor. Starts with 0.)

-

For a set of portable tools try my very own dateutils. Your two examples would boil down to one-liners:

``````ddiff 2011-11-15 2012-04-11
=>
148
``````

or in weeks and days:

``````ddiff 2011-11-15 2012-04-11 -f '%w %d'
=>
21 1
``````

and

``````dadd 2011-11-15 21w
=>
2012-04-10
``````
-
+1 your tools rock (though `dateadd -i '%m%d%Y' 01012015 +1d` doesn't seem to work, it just hangs there indefinitely... it does work if the date specs are separated by a char, any char... any idea what's wrong ?) – don_crissti Nov 22 '15 at 2:17
@don_crissti The parser couldn't distinguish between numerals-only dates and durations, it's fixed in the current master (d0008f98) – hroptatyr Nov 23 '15 at 6:21
... that was fast ! Thanks a lot ! – don_crissti Nov 23 '15 at 14:41

I usually prefer having the time/date in unix utime format (number of seconds since the epoch, when the seventies begun, UTC). That way it always boils down to plain subtraction or addition of seconds.

The problem the usually becomes transforming a date/time into this format.

If you have GNU `date`, you can get it with `date '+%s'` At the time of writing, the current time is `1321358027`.

To compare with 2011-11-04 (my birthday), `date '+%s' -d 2011-11-04`, yielding `1320361200`. Subtract: `expr 1321358027 - 1320361200` gives `996827` seconds, which is `expr 996827 / 86400` = 11 days ago.

The problem is converting from utime (1320361200 format) into a date. I don't know of a readily available tool to do this, but it's very simple to do in for instance C or perl.

-
With GNU date, `date -d @1234567890` converts from seconds since the epoch to whatever date format you specify. – Gilles Nov 15 '11 at 23:21
@Gilles: that is brilliant. Couldn't find that on the manpage. Where did you learn that? – MattBianco Nov 16 '11 at 9:48
@MattBianco, see `info date`, especially the Seconds since the Epoch node: “If you precede a number with `@', it represents an internal time stamp as a count of seconds.” – manatwork Nov 16 '11 at 10:15

A python example for calculating the number of days I've walked the planet:

``````\$ python
>>> from datetime import date as D
>>> print (D.today() - D(1980, 6, 14)).days
11476
``````
-
Just in case someone wants this to behave just like a single command, instead of typing in an interactive interpreter : `ychaouche@ychaouche-PC ~ \$ python -c "from datetime import date as d; print (d.today() - d(1980, 6, 14)).days"` `12813` `ychaouche@ychaouche-PC ~ \$` – ychaouche Jul 14 '15 at 14:56