Tool in UNIX to subtract dates

Unfortunately, none of the POSIX command line utilities provide arithmetic on dates. date -d and date +%s are the way to go if you have them, but they're GNU extensions.

There's a clumsy hack with touch that sort of works for checking that a date is at least n days in the past:

touch -t 201009090000 stamp
if [ -n "$(find stamp -mtime +42)" ]; then ...

(Note that this code may be off by one if DST started or stopped in the interval and the script runs before 1am.)

Several people have ended up implementing date manipulation libraries in Bourne or POSIX shell. There are a few examples and links in the comp.unix.shell FAQ.

Installing GNU tools may be the way of least pain.


I would try using the date command which is part of POSIX so it is just about everywhere. UPDATE: Unfortunately, it seems that -d is not part of POSIX date and likely not there on Solaris. Thus this likely won't answer the OPs question.

d1=`date -d 20100909 +%s`
d2=`date -d 20001010 +%s`

Now d1 and d2 are integers that correspond to seconds since the unix epoch. Thus to get the difference between the two, we subtract( $((d1-d2)) in bash) and covert to whatever units we want. Days are the easiest:

echo "$(((d1-d2)/86400)) days"

How to do the conversion will likely be different if you don't have bash. The most portable way may be to use expr (expr's posix man page).


Here is an awk script I just wrote up, should work with an POSIX awk. You'll have to try the Solaris version; remember that there are two versions of Awk on Solaris as well, one in /bin and one in /usr/xpg4/bin/awk (which is nawk, I believe).

BEGIN {
    daysofmonth["01"] = 0; daysofmonth["02"] = 31; daysofmonth["03"] = 59;
    daysofmonth["04"] = 90; daysofmonth["05"] = 120; daysofmonth["06"] = 151;
    daysofmonth["07"] = 181; daysofmonth["08"] = 212; daysofmonth["09"] = 243;
    daysofmonth["10"] = 273; daysofmonth["11"] = 304; daysofmonth["12"] = 334;
    fullday = 86400;
}
/[12][09][0-9][0-9][01][0-9][0123][0-9]/ {
    year = substr($0, 1, 4); month = substr($0, 5, 2); day = substr($0, 7, 2);
    date = ((year - 1970) * 365.25) + daysofmonth[month] + day - 1;
    if ((year % 4) == 0 && month > 2) { date = date + 1; }
    print date * fullday - (25200);
}
{}

Pass a YYYYmmdd date string through and it will be converted to number of seconds since the Epoch (with a bit of give for being on day boundaries). Then you will be able to subtract the two.

today=`echo 20110210 | awk -f convdate.awk`
then=`echo 20001231 | awk -f convdate.awk`
sincethen=`expr $today - $then`