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?


18 Answers 18


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'  # Note: answer should be 92 days but in my timezone, DST starts between the dates.
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
  • I am trying to implement the "adding n-minutes", my problem is that I have the number of minutes written down in my variable timeStudy and I can't find a way to plug it in place of 3 (in your code). Any help? Commented Mar 23, 2020 at 19:55
  • 1
    @TommasoSeneci: date -d "now + ${timeStudy} minutes" - using double-quotes instead of single quotes allows you to do variable expansion within the string.
    – camh
    Commented Mar 23, 2020 at 21:59
  • Awesome, problem solved!! Commented Mar 24, 2020 at 12:15
  • Interesting. Exactly the same commands, mine returns "92 days".
    – kennyut
    Commented Apr 2, 2021 at 14:09
  • 2
    @kennyut The answer should actually be 92 days. The problem in my example is that in my timezone, daylight saving time starts between the two dates so the number of seconds between the two dates is short by 3600 (1 hour). If your timezone does not have DST (or you do but DST ends between the dates), then you will get the correct answer of 92 days. See the revision by Gilles for the correct calculation
    – camh
    Commented Apr 3, 2021 at 0:28

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

or in weeks and days:

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


dadd 2011-11-15 21w

Important note regarding Ubuntu installation:

These very same dateutils are available as a Ubuntu package, and hence installable through sudo apt install dateutils.

However, commands need to be preceded by a dateutils. prefix, as in dateutils.ddiff 2019-03-28 2019-05-16

They are also available in Fedora and in Fedora EPEL for RHEL or CentOS with sudo dnf install dateutils. In Fedora, these packages do not require a prefix but use the long names — e.g, datediff and dateadd instead of ddiff and dadd.

For Arch Linux, install the package with sudo pacman -Sy dateutils. The binaries are called datediff and dateadd, just like in Fedora.

  • Do you have window's binary distrib of dateutils?
    – mosh
    Commented Feb 12, 2017 at 4:18
  • @mosh no, and I don't have the facilities to try.
    – hroptatyr
    Commented Feb 12, 2017 at 8:21
  • Late to the party here, but dateutils is on cygwin if you need to use on Windows.
    – gregb212
    Commented Mar 5, 2019 at 21:32
  • FYI - as one might expect, everything mentioned above for Ubuntu also applies to Debian. Commented Aug 7, 2020 at 16:51
  • FYI, if you're on macOS with homebrew, do brew install dateutils. ddiff is so awesome, thanks! Commented Jul 6, 2021 at 22:00

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
  • 4
    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
    Commented Jul 14, 2015 at 14:56
  • 2
    this works for me python3 -c "from datetime import date as d; print (d.today() - d(2016, 1, 9))" days at the end is not required Commented Oct 14, 2016 at 5:18
  • If you don't want days and 00:00 in the output and JUST want the actual count of days, you still need the .days, but to make it work with the Python3 print you need an extra ( around the statement so you can get the .days before the print() is called. python3 -c "from datetime import date as d; print ((d(2033, 10, 23) - d.today()).days)" which happens to be looking to the future, just flip the d.today() with the specific date if you are looking in the past.
    – dragon788
    Commented Jan 25, 2021 at 18:09
  • 1
    Thanks! There's a little mistake though: you call days field on the result of print call ☺
    – Hi-Angel
    Commented Feb 25, 2021 at 13:34

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.

In a shell environment/script 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.

Converting from utime (1320361200 format) into a date is very simple to do in for instance C, PHP or perl, using standard libraries. With GNU date, the -d argument can be prepended with @ to indicate "Seconds since the Epoch" format.


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


You can use qalculate (a calculator with an emphasis on unit conversions, it comes with a GTK and KDE interface, and a command line tool qalc). 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.

For the command line, valid examples are:

qalc -t 'days(1900-05-21, 1900-01-01)'
qalc -t '"1900-05-21" - "1900-01-01"'
qalc -t 'today - "1900-01-01"'

(Thank you Sparhawk, sierrasdetandil, Denilson Sá Maia for the helpful comments, I added your content to this answer.)

  • 3
    That's great, and even more so because qalculate does have a CLI. Try qalc, then help days.
    – Sparhawk
    Commented Jul 4, 2014 at 12:19
  • qcalc example: qalc -t 'days(1900-05-21, 1900-01-01)' -> 140 Commented Mar 25, 2019 at 15:39
  • There is an even simpler syntax: "1900-05-21" - "1900-01-01" or even today - "2023-01-01" Commented Jun 9, 2023 at 2:00

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

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 "$DAYSdif"
  • 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.

  • 1
    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
    Commented Feb 28, 2015 at 13:29
  • You don't need $ inside arithmetic expression: $((DATElastnum - DATEfirstnum)) will also work.
    – Ruslan
    Commented Jun 2, 2018 at 14:26

With the help of dannas solutions this can be done in one line with following code:

python -c "from datetime import date as d; print(d.today() - d(2016, 7, 26))"

(Works in both Python 2.x and Python 3.)


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

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

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

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

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)'

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

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

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

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


date and bash can do date differences (OS X options shown). Place the latter date first.

echo $((($(date -jf%D "04/03/16" +%s) - $(date -jf%D "03/02/16" +%s)) / 86400))
# 31
  • What does the -jf%D OSX date option stand for ?
    – SebMa
    Commented Jun 29, 2020 at 12:47

datediff.sh on github:gist

#Simplest calculator two dates difference. By default in days

# Usage:
# ./datediff.sh first_date second_date [-(s|m|h|d) | --(seconds|minutes|hours|days)]

first_date=$(date -d "$1" "+%s")
second_date=$(date -d "$2" "+%s")

case "$3" in
"--seconds" | "-s") period=1;;
"--minutes" | "-m") period=60;;
"--hours" | "-h") period=$((60*60));;
"--days" | "-d" | "") period=$((60*60*24));;

datediff=$(( ($first_date - $second_date)/($period) ))
echo $datediff

This is a rather old thread, but I discovered something interesting. I'm stuck on an embedded system with BusyBox (v1.19) and was a bit disappointed that the super handy now - 10 days notation fails there. In fact, a very limited set of input formats is available for the -d option.

Recognized TIME formats:
    YYYY-MM-DD hh:mm[:ss]

However, some math can be done on an input date.

Given those formats, I found setting the day of the date to display to zero gives you the last day of the previous month:

$ date -d 2020.03.00-12:00
Sat Feb 29 12:00:00 EST 2020 

And inputting negative numbers goes back even further:

$ date -d 2020.03.-10-12:00
Wed Feb 19 12:00:00 EST 2020

Can anyone confirm if this also works on GNU date? (Although the now - 10 days is very much better)


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)


camh's answer takes care of most of it, but we can improve to deal with rounding, time zones, etc., plus we get some extra precision and the ability to pick our units:

datediff() {
#convert dates to decimal seconds since 1970-01-01 00:00:00 UTC
date1seconds=$(date +%s.%N -d "$date1")
date2seconds=$(date +%s.%N -d "$date2")

#Calculate time difference in various time units
timeseconds=$(printf "%0.8f\n" $(bc <<<"scale=9; ($date2sec-$date1sec)"))
timeminutes=$(printf "%0.8f\n" $(bc <<<"scale=9; ($date2sec-$date1sec)/60"))
  timehours=$(printf "%0.8f\n" $(bc <<<"scale=9; ($date2sec-$date1sec)/3600"))
   timedays=$(printf "%0.8f\n" $(bc <<<"scale=9; ($date2sec-$date1sec)/86400"))
  timeweeks=$(printf "%0.8f\n" $(bc <<<"scale=9; ($date2sec-$date1sec)/604800"))

-d tells date that we're supplying a date-time to convert. +%s.%N changes the date-time to seconds.nanoseconds since 1970-01-01 00:00:00 UTC. bc calculates the difference between the two numbers, and the dividing factor gets us the different units. printf adds a 0 before the decimal place if needed (bc does not) and ensures rounding goes to nearest (bc just truncates). You could also use awk.

Now let's run this with a funky test case,

date1='Tue Jul  9 10:18:04.031 PST 2020'
date2='Wed May  8 15:19:34.447 CDT 2019'
datediff "$date1" "$date2"

echo $timeseconds seconds
-36971909.584000000 seconds

echo $timeminutes minutes
-616198.493066667 minutes

echo $timehours hours
-10269.974884444 hours

echo $timedays days
-427.915620185 days

echo $timeweeks weeks
-61.130802884 weeks

Note that since the length of a month or year isn't always the same, there isn't a single "correct" answer there, though one could use 365.24 days as a reasonable approximation nowadays.


This is a rather old thread, but I discovered something interesting in BusyBox, at least pertaining to adding and subtracting N units of time from a known date. Not so much for going the other way to determine the interval between two known dates though.

I'm stuck on an embedded system with BusyBox and the super handy now - 10 days notation fails there. In fact, a very limited set of input formats is available for the -d option.

$ busybox date --help
BusyBox v1.19.0 (2019-07-14 10:52:19 UTC) multi-call binary.

Usage: date [OPTIONS] [+FMT] [TIME]

Display time (using +FMT), or set time

        [-s,--set] TIME Set time to TIME
        -u,--utc        Work in UTC (don't convert to local time)
        -R,--rfc-2822   Output RFC-2822 compliant date string
        -I[SPEC]        Output ISO-8601 compliant date string
                        SPEC='date' (default) for date only,
                        'hours', 'minutes', or 'seconds' for date and
                        time to the indicated precision
        -r,--reference FILE     Display last modification time of FILE
        -d,--date TIME  Display TIME, not 'now'
        -D FMT          Use FMT for -d TIME conversion

Recognized TIME formats:
        YYYY-MM-DD hh:mm[:ss]

However, some math can be done on an input date. Given those formats, I found setting the day number of the input date to zero gives you the last day of the previous month:

$ date -d 2020.03.00-12:00
Sat Feb 29 12:00:00 EST 2020 

Further, inputting negative numbers keeps subtracting backwards, which I certainly did not expect:

$ date -d 2020.03.-10-12:00
Wed Feb 19 12:00:00 EST 2020

So you can do some quick math on your known date to generate values to feed into the -d option in the BusyBox date call:

#Other shells not tested.



new_year=$(( $start_year + $offset_year ))
new_month=$(( $start_month + $offset_month ))
new_day=$(( $start_day + $offset_day ))

new_date$( busybox date -d $new_year.$new_month.$new_day-12:00 +%Y-%m-%d )
echo $new_date



Can anyone confirm if this also works with GNU, BSD, or MacOS date? I certainly appreciate that the GNU now - 10 days is very much better, but I'm interested in making this as portable as possible. I have confirmed that Android is very different (neither approach works there).


For a simple diff of days we can use the command date:

$ dateA="2020-11-10"
$ dateB="2020-12-28"
$ echo "(`date -d $dateB +%s` - `date -d $dateA +%s`)/86400" | bc
  • -d, --date=STRING display time described by STRING
  • %s seconds since 1970-01-01 00:00:00 UTC
  • 86400 represents the amount of seconds in a day

The echo command will just build a string with the desired math expression, then the bc will resolve it.

For add n weeks i think the @camh's answer is a great approach:

$ dateA="2020-11-10"
$ date -d "$dateA + 3 weeks"
ter dez  1 00:00:00 -03 2020

Furthermore, you can change the weeks for days, months, years, etc.

  • 1
    Is there a reason why you pass the data through bc instead of calculating the result using an arithmetic substitution in the shell (using $(( ... )))? Also, you appear to only have addressed half the question.
    – Kusalananda
    Commented Nov 10, 2020 at 12:52
  • The only reason for use the older command substitution `` and bc is to keep it more succinct. I changed the answer to cover the second question as well. Thanks for the tip. Commented Nov 11, 2020 at 13:57

date -d is specific to GNU Date and is not defined by POSIX:


As others have mentioned, this is something that might be better suited to a proper programming language. For example PHP:

$o1 = date_create('2020-02-26');
$o2 = date_create('2020-02-16');
$o3 = date_diff($o2, $o1);
echo 'days: ', $o3->d, "\n";


days: 10

units is my favourite cli calculator. It is dimensioned, and knows a lot of conversions to boot:

$ units 135J/45N
    Definition: 3 m

Time? sure

$ units 1628675579s time
    51 year + 223 day + 1 hr + 25 min + 54.291401 sec

It does know about months, but as has been pointed out, it's dodgy to use months in period measurements.

$ units 1628675579s 'year;month;day;hr;min;sec'
    51 year + 7 month + 10 day + 2 min + 27.472839 sec

Oh, and don't forget that it's a calculator foremost, not a conversion tool.

$ units 1628675579s-1628600000s hms
    20 hr + 59 min + 39 sec

You must log in to answer this question.

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