22

Is there any tool in Solaris UNIX (so no GNU tool available) to subtract dates? I know that in Linux we have gawk that can subtract one date from another. But in Solaris the maximum we have is nawk (improved awk) which cannot perform date calculations. Also I cannot use perl.

Is there any way to do date calculations like 20100909 - 20001010?

UPDATE: Is bc able to perform dates calculations?

11 Answers 11

10

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`
  • I checked your logic with Oracle. For some dates is ok, however for it generates a float number. I'm afraid I have to truncate to an integer number, is it? – jyz Feb 17 '11 at 15:42
  • The decimal is the fraction of a second and not necessarily needed. – Arcege Feb 17 '11 at 16:22
  • The epoch expires 20380119. Why not use the Julian day of year? Dupe... unix.stackexchange.com/questions/302266/… – jas- Feb 18 '18 at 18:55
13

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.

11

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

  • yeah this does not answer my question because it's Solaris... but thank you for the ideia and posting :) – jyz Sep 9 '10 at 20:20
8

For the record, dateutils is my attempt at providing a set of portable tools that cover date arithmetics. Your example boils down to

ddiff -i '%Y%m%d' 20001010 20100909
=>
  3621

The -i specifies the input format.

  • 2
    This package is awesome, and it exists in the Ubuntu universe libraries (at least for Trusty). On another system, I could compile it easily from scratch. Highly recommended. Thank you very much! – Robert Muil Oct 1 '14 at 16:20
  • Also packaged in Fedora and in EPEL, for the RH ecosystem. – mattdm Feb 1 '17 at 14:35
4

Perl is likely to be installed, and it's easy enough to grab modules from CPAN, install them in your home directory, and refer to them in your program. In this case, the Date::Calc module has a Delta_Days subroutine that will help.

3

Wrt GNU date on Solaris:

I say this again:

Too many Solaris SysAdmins take pride in deliberately not installing the official packages from Sun (now Oracle) that make a Solaris system "GNU compatible" when they configure a new host. Beats me why.

GNU Date is excellent and should be available by default on any Solaris host, IMHO.

On Solaris 10

Install package SFWcoreu from the Solaris Companion CD. After install you will find GNU date in /opt/sfw/bin/date

On Solaris 11

You may already have it as I believe it is part of the standard install. It is however called gdate in order to distinguish it from the Sun version of date.

Do this if not installed:

pkg install //solaris/file/gnu-coreutils
  • Thanks for the tip. Can you give me an example of how to use it? So I can ask sysadmin to install it... – jyz Feb 27 '13 at 14:23
  • On Solaris 11, it will be installed as both /usr/bin/gdate and /usr/gnu/bin/date so you can choose to use it either by name or by $PATH setting. – alanc Jul 6 '13 at 16:19
2

If you have Python:

from time import *
date1 = strptime("20100909","%Y%m%d")
date2 = strptime("20001010","%Y%m%d")
diff = mktime(date1) - mktime(date2)
print repr(d/86400) + " days"

You tagged your question "gawk" but you say "no GNU tools". Gawk has date arithmetic.

  • I didn't. Someone edited my post and tagged "gawk"... – jyz Sep 9 '10 at 22:37
2

python:

 from datetime import datetime as dt
 a = dt(2010, 9, 9)
 b = dt(2000, 10, 10)
 print (a - b).days
1

1. Define date1 and date2 in %j day of year (001..366) format

user@linux:~$ date1=`date -d 20100909 +%j` 
user@linux:~$ date2=`date -d 20001010 +%j`

2. Calculate the difference with expr

user@linux:~$ datediff=`expr $date2 - $date1`

3. So, the answer is 32 days

user@linux:~$ echo $datediff days
32 days
user@linux:~$ 

... or One-liner Solution

user@linux:~$ echo $(( $(date -d 20001010 +%j) - $(date -d 20100909 +%j) )) days
32 days
user@linux:~$ 

or

user@linux:~$ expr $(date -d 20001010 +%j) - $(date -d 20100909 +%j)
32
user@linux:~$

or

user@linux:~$ expr `date -d 20001010 +%j` - `date -d 20100909 +%j`
32
user@linux:~$ 
0

With date BSD for macOS :

echo "Quelle est la date de début ?"
read DATE_DE_DEBUT
echo "Quelle est la date de fin ?"
read DATE_DE_FIN
ANNEE=$(($(echo $DATE_DE_FIN | awk -F "/" '{print $3}')-$(echo $DATE_DE_DEBUT  | awk -F "/" '{print $3}')))

echo " "

if [[ $ANNEE > 0 ]]; then

for((annee=$ANNEE-1 ; $ANNEE ; annee++))

  do
  #echo $annee  
  for((mois=0 ; 13 - $mois ; mois++))
      do
  #   echo année:$annee mois:$mois
          for((jour=0 ; 32 - $jour ; jour++))
              do
 #             echo année:$annee mois:$mois jour:$jour
           TEST=$(date -Rjf"%d/%m/%Y" -v +"$annee"y -v +"$mois"m -v +"$jour"d $DATE_DE_DEBUT +"%d/%m/%Y")
           if [[ $TEST = $DATE_DE_FIN ]]; then
                  echo "Différence entre le $DATE_DE_DEBUT et le $DATE_DE_FIN";
                  echo "est de :";
                  echo "$annee année(s) $mois mois $jour jour(s)";
             exit 0;
            fi


          done 
  done
  done

 else 
 annee=0
 for((mois=0 ; 13 - $mois ; mois++))
      do
#      echo année:$annee mois:$mois
          for((jour=0 ; 32 - $jour ; jour++))
              do
              #echo année:$annee mois:$mois jour:$jour
              TEST=$(date -Rjf"%d/%m/%Y" -v +"$annee"y -v +"$mois"m -v +"$jour"d $DATE_DE_DEBUT +"%d/%m/%Y")
              if [[ $TEST = $DATE_DE_FIN ]]; then 
                      echo "Différence entre le $DATE_DE_DEBUT et le $DATE_DE_FIN";
                     echo "est de :";
                     echo "$annee année(s) $mois mois $jour jour(s)";
              exit 0;
              fi
              done
      done

fi
0

You can use the awk Velour library to return difference in seconds:

$ velour -n 'print t_utc(2010, 9, 9) - t_utc(2000, 10, 10)'
312854400

Or days:

$ velour -n 'print t_secday(t_utc(2010, 9, 9) - t_utc(2000, 10, 10))'
3621

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.