<p>I need to do date arithmetic in Unix shell scripts that I use to control the execution of third party programs. </p>

<p>I'm using a function to increment a day and another to decrement: </p>

<pre><code>IncrementaDia(){
echo $1 | awk '
BEGIN {
        diasDelMes[1] = 31
        diasDelMes[2] = 28
        diasDelMes[3] = 31
        diasDelMes[4] = 30
        diasDelMes[5] = 31
        diasDelMes[6] = 30
        diasDelMes[7] = 31
        diasDelMes[8] = 31
        diasDelMes[9] = 30
        diasDelMes[10] = 31
        diasDelMes[11] = 30
        diasDelMes[12] = 31
}
{
        anio=substr($1,1,4)
        mes=substr($1,5,2)
        dia=substr($1,7,2)

        if((anio % 4 == 0 &amp;&amp; anio % 100 != 0) || anio % 400 == 0)
        {
                diasDelMes[2] = 29;
        }

        if( dia == diasDelMes[int(mes)] ) {
                if( int(mes) == 12 ) {
                        anio = anio + 1
                        mes = 1
                        dia = 1
                } else {
                        mes = mes + 1
                        dia = 1
                }
        } else {
                dia = dia + 1
        }
}
END {
        printf("%04d%02d%02d", anio, mes, dia)
}
'
}

if [ $# -eq 1 ]; then
        tomorrow=$1
else
        today=$(date +"%Y%m%d")
        tomorrow=$(IncrementaDia $hoy)
fi
</code></pre>

<p>but now I need to do more complex arithmetic. </p>

<p>What it's the best and more compatible way to do this?</p>

[{'answer_id': 6468, 'author': 'abyx', 'author_id': 573, 'author_profile': 'https://Stackoverflow.com/users/573', 'pm_score': 6, 'selected': False, 'text': '<p>Assuming you have <a href="http://www.gnu.org/software/coreutils/manual/coreutils.html#date-invocation" rel="noreferrer">GNU date</a>, like so:</p>\n\n<pre><code>date --date=\'1 days ago\' \'+%a\'\n</code></pre>\n\n<p>And <a href="http://www.gnu.org/software/coreutils/manual/coreutils.html#Date-input-formats" rel="noreferrer">similar phrases</a>.</p>\n'}, {'answer_id': 6471, 'author': 'ggasp', 'author_id': 527, 'author_profile': 'https://Stackoverflow.com/users/527', 'pm_score': 3, 'selected': False, 'text': "<pre><code>date --date='1 days ago' '+%a'\n</code></pre>\n\n<p>It's not a very compatible solution. It will work only in Linux. At least, it didn't worked in Aix and Solaris.</p>\n\n<p>It works in RHEL: </p>\n\n<pre><code>date --date='1 days ago' '+%Y%m%d'\n20080807\n</code></pre>\n"}, {'answer_id': 6474, 'author': 'Justin Standard', 'author_id': 92, 'author_profile': 'https://Stackoverflow.com/users/92', 'pm_score': 3, 'selected': False, 'text': '<p>Why not write your scripts using a language like perl or python instead which more naturally supports complex date processing?  Sure you <em>can</em> do it all in bash, but I think you will also get more consistency across platforms using python for example, so long as you can ensure that perl or python is installed.</p>\n\n<p>I should add that it is quite easy to wire in python and perl scripts into a containing shell script.</p>\n'}, {'answer_id': 6746, 'author': 'abyx', 'author_id': 573, 'author_profile': 'https://Stackoverflow.com/users/573', 'pm_score': 2, 'selected': False, 'text': "<p>Looking into it further, I think you can simply use date.\nI've tried the following on OpenBSD: I took the date of Feb. 29th 2008 and a random hour (in the form of 080229301535) and added +1 to the day part, like so:</p>\n\n<pre><code>$ date -j 0802301535\nSat Mar  1 15:35:00 EST 2008\n</code></pre>\n\n<p>As you can see, date formatted the time correctly...</p>\n\n<p>HTH</p>\n"}, {'answer_id': 12826, 'author': 'Jon Ericson', 'author_id': 1438, 'author_profile': 'https://Stackoverflow.com/users/1438', 'pm_score': 0, 'selected': False, 'text': '<p>If the GNU version of date works for you, why don\'t you grab the source and compile it on AIX and Solaris?</p>\n\n<p><a href="http://www.gnu.org/software/coreutils/" rel="nofollow noreferrer">http://www.gnu.org/software/coreutils/</a></p>\n\n<p>In any case, the source ought to help you get the date arithmetic correct if you are going to write you own code.</p>\n\n<p>As an aside, comments like "that solution is good but surely you can note it\'s not as good as can be. It seems nobody thought of tinkering with dates when constructing Unix." don\'t really get us anywhere.  I found each one of the suggestions so far to be very useful and on target.</p>\n'}, {'answer_id': 21642, 'author': 'caerwyn', 'author_id': 2406, 'author_profile': 'https://Stackoverflow.com/users/2406', 'pm_score': 3, 'selected': False, 'text': '<p>To do arithmetic with dates on UNIX you get the date as the number seconds since the UNIX epoch, do some calculation, then convert back to your printable date format. The date command should be able to both give you the seconds since the epoch and convert from that number back to a printable date. My local date command does this,</p>\n\n<pre><code>% date -n\n1219371462\n% date 1219371462\nThu Aug 21 22:17:42 EDT 2008\n% \n</code></pre>\n\n<p>See your local <code>date(1)</code> man page.\nTo increment a day add 86400 seconds.</p>\n'}, {'answer_id': 70745, 'author': 'Jonathan Bourke', 'author_id': 8361, 'author_profile': 'https://Stackoverflow.com/users/8361', 'pm_score': 2, 'selected': False, 'text': '<p>I have bumped into this a couple of times. My thoughts are:</p>\n\n<ol>\n<li>Date arithmetic is always a pain</li>\n<li>It is a bit easier when using EPOCH date format</li>\n<li>date on Linux converts to EPOCH, but not on Solaris</li>\n<li>For a portable solution, you need to do one of the following:\n\n<ol start="5">\n<li>Install gnu date on solaris (already\nmentioned, needs human interaction\nto complete)</li>\n<li>Use perl for the date part (<strong>most</strong> unix installs include\nperl, so I would generally assume\nthat this action does <strong>not</strong>\nrequire additional work).</li>\n</ol></li>\n</ol>\n\n<p>A sample script (checks for the age of certain user files to see if the account can be deleted):</p>\n\n<pre><code>#!/usr/local/bin/perl\n\n$today = time();\n\n$user = $ARGV[0];\n\n$command="awk -F: \'/$user/ {print \\$6}\' /etc/passwd";\n\nchomp ($user_dir = `$command`);\n\nif ( -f "$user_dir/.sh_history" ) {\n    @file_dates   = stat("$user_dir/.sh_history");\n    $sh_file_date = $file_dates[8];\n} else {\n    $sh_file_date = 0;\n}\nif ( -f "$user_dir/.bash_history" ) {\n    @file_dates     = stat("$user_dir/.bash_history");\n    $bash_file_date = $file_dates[8];\n} else {\n    $bash_file_date = 0;\n}\nif ( $sh_file_date &gt; $bash_file_date ) {\n    $file_date = $sh_file_date;\n} else {\n    $file_date = $bash_file_date;\n}\n$difference = $today - $file_date;\n\nif ( $difference &gt;= 3888000 ) {\n    print "User needs to be disabled, 45 days old or older!\\n";\n    exit (1);\n} else {\n    print "OK\\n";\n    exit (0);\n}\n</code></pre>\n'}, {'answer_id': 71794, 'author': 'Joe Watkins', 'author_id': 11928, 'author_profile': 'https://Stackoverflow.com/users/11928', 'pm_score': 2, 'selected': False, 'text': '<p>If you want to continue with awk, then the mktime and strftime functions are useful:</p>\n\n<pre><code>\nBEGIN { dateinit }\n      { newdate=daysadd(OldDate,DaysToAdd)}\n\n # daynum: convert DD-MON-YYYY to day count\n #-----------------------------------------\nfunction daynum(date,  d,m,y,i,n)\n{\n     y=substr(date,8,4)\n     m=gmonths[toupper(substr(date,4,3))]\n     d=substr(date,1,2)\n     return mktime(y" "m" "d" 12 00 00")\n}\n\n #numday: convert day count to DD-MON-YYYY\n #-------------------------------------------\nfunction numday(n,  y,m,d)\n{\n    m=toupper(substr(strftime("%B",n),1,3))\n    return strftime("%d-"m"-%Y",n)\n}\n\n # daysadd: add (or subtract) days from date (DD-MON-YYYY), return new date (DD-MON-YYYY)\n #------------------------------------------\nfunction daysadd(date, days)\n{\n    return numday(daynum(date)+(days*86400))\n}\n\n #init variables for date calcs\n #-----------------------------------------\nfunction dateinit(   x,y,z)\n{\n     # Stuff for date calcs\n     split("JAN:1,FEB:2,MAR:3,APR:4,MAY:5,JUN:6,JUL:7,AUG:8,SEP:9,OCT:10,NOV:11,DEC:12", z)\n     for (x in z)\n     {\n        split(z[x],y,":")\n        gmonths[y[1]]=y[2]\n     }\n}\n</code></pre>\n'}, {'answer_id': 76473, 'author': 'joast', 'author_id': 8674, 'author_profile': 'https://Stackoverflow.com/users/8674', 'pm_score': 2, 'selected': False, 'text': '<p>The book "Shell Script Recipes: A Problem Solution Approach" (ISBN: 978-1-59059-471-1) by Chris F.A. Johnson has a date functions library that might be helpful. The source code is available at <a href="http://apress.com/book/downloadfile/2146" rel="nofollow noreferrer">http://apress.com/book/downloadfile/2146</a> (the date functions are in Chapter08/data-funcs-sh within the tar file).</p>\n'}, {'answer_id': 3125174, 'author': 'Larry Morell', 'author_id': 376990, 'author_profile': 'https://Stackoverflow.com/users/376990', 'pm_score': 5, 'selected': True, 'text': '<p>I have written a bash script for converting dates expressed in English into conventional\nmm/dd/yyyy dates.  It is called <strong>ComputeDate</strong>.</p>\n\n<p>Here are some examples of its use.  For brevity I have placed the output of each invocation\non the same line as the invocation, separarted by a colon (:).   The quotes shown below are <em>not</em> necessary when running <strong>ComputeDate</strong>:</p>\n\n<pre><code>$ ComputeDate \'yesterday\': 03/19/2010\n$ ComputeDate \'yes\': 03/19/2010\n$ ComputeDate \'today\': 03/20/2010\n$ ComputeDate \'tod\': 03/20/2010\n$ ComputeDate \'now\': 03/20/2010\n$ ComputeDate \'tomorrow\': 03/21/2010\n$ ComputeDate \'tom\': 03/21/2010\n$ ComputeDate \'10/29/32\': 10/29/2032\n$ ComputeDate \'October 29\': 10/1/2029\n$ ComputeDate \'October 29, 2010\': 10/29/2010\n$ ComputeDate \'this monday\': \'this monday\' has passed.  Did you mean \'next monday?\'\n$ ComputeDate \'a week after today\': 03/27/2010\n$ ComputeDate \'this satu\': 03/20/2010\n$ ComputeDate \'next monday\': 03/22/2010\n$ ComputeDate \'next thur\': 03/25/2010\n$ ComputeDate \'mon in 2 weeks\': 03/28/2010\n$ ComputeDate \'the last day of the month\': 03/31/2010\n$ ComputeDate \'the last day of feb\': 2/28/2010\n$ ComputeDate \'the last day of feb 2000\': 2/29/2000\n$ ComputeDate \'1 week from yesterday\': 03/26/2010\n$ ComputeDate \'1 week from today\': 03/27/2010\n$ ComputeDate \'1 week from tomorrow\': 03/28/2010\n$ ComputeDate \'2 weeks from yesterday\': 4/2/2010\n$ ComputeDate \'2 weeks from today\': 4/3/2010\n$ ComputeDate \'2 weeks from tomorrow\': 4/4/2010\n$ ComputeDate \'1 week after the last day of march\': 4/7/2010\n$ ComputeDate \'1 week after next Thursday\': 4/1/2010\n$ ComputeDate \'2 weeks after the last day of march\': 4/14/2010\n$ ComputeDate \'2 weeks after 1 day after the last day of march\': 4/15/2010\n$ ComputeDate \'1 day after the last day of march\': 4/1/2010\n$ ComputeDate \'1 day after 1 day after 1 day after 1 day after today\': 03/24/2010\n</code></pre>\n\n<p>I have included this script as an answer to this problem because it illustrates how\nto do date arithmetic via a set of bash functions and these functions may prove useful\nfor others.  It handles leap years and leap centuries correctly:</p>\n\n<pre><code>#! /bin/bash\n#  ConvertDate -- convert a human-readable date to a MM/DD/YY date\n#\n#  Date ::= Month/Day/Year\n#        |  Month/Day\n#        |  DayOfWeek\n#        |  [this|next] DayOfWeek\n#        |  DayofWeek [of|in] [Number|next] weeks[s]\n#        |  Number [day|week][s] from Date\n#        |  the last day of the month\n#        |  the last day of Month\n#\n#  Month ::= January | February | March | April | May | ...  | December\n#  January  ::= jan | january | 1\n#  February  ::= feb | january | 2\n#  ...\n#  December ::=  dec | december | 12\n#  Day   ::= 1 | 2 | ... | 31\n#  DayOfWeek ::= today | Sunday | Monday | Tuesday | ...  | Saturday\n#  Sunday    ::= sun*\n#  ...\n#  Saturday  ::= sat*\n#\n#  Number ::= Day | a\n#\n#  Author: Larry Morell\n\nif [ $# = 0 ]; then\n   printdirections $0\n   exit\nfi\n\n\n\n# Request the value of a variable\nGetVar () {\n   Var=$1\n   echo -n "$Var= [${!Var}]: "\n   local X\n   read X\n   if [ ! -z $X ]; then\n      eval $Var="$X"\n   fi\n}\n\nIsLeapYear () {\n   local Year=$1\n   if [ $[20$Year % 4]  -eq  0 ]; then\n      echo yes\n   else\n      echo no\n   fi\n}\n\n# AddToDate -- compute another date within the same year\n\nDayNames=(mon tue wed thu fri sat sun )  # To correspond with \'date\' output\n\nDay2Int () {\n   ErrorFlag=\n   case $1 in\n      -e )\n         ErrorFlag=-e; shift\n         ;;\n   esac\n   local dow=$1\n   n=0\n   while  [ $n -lt 7 -a $dow != "${DayNames[n]}" ]; do\n      let n++\n   done\n   if [ -z "$ErrorFlag" -a $n -eq 7 ]; then\n      echo Cannot convert $dow to a numeric day of wee\n      exit\n   fi\n   echo $[n+1]\n\n}\n\nMonths=(31 28 31 30 31 30 31 31 30 31 30 31)\nMonthNames=(jan feb mar apr may jun jul aug sep oct nov dec)\n# Returns the month (1-12) from a date, or a month name\nMonth2Int () {\n   ErrorFlag=\n   case $1 in\n      -e )\n         ErrorFlag=-e; shift\n         ;;\n   esac\n   M=$1\n   Month=${M%%/*}  # Remove /...\n   case $Month in\n      [a-z]* )\n         Month=${Month:0:3}\n         M=0\n         while [ $M -lt 12 -a ${MonthNames[M]} != $Month ]; do\n            let M++\n         done\n         let M++\n   esac\n   if [  -z "$ErrorFlag" -a $M -gt 12 ]; then\n      echo "\'$Month\' Is not a valid month."\n      exit\n   fi\n   echo $M\n}\n\n# Retrieve month,day,year from a legal date\nGetMonth() {\n   echo ${1%%/*}\n}\n\nGetDay() {\n   echo $1 | col / 2\n}\n\nGetYear() {\n   echo ${1##*/}\n}\n\n\nAddToDate() {\n\n   local Date=$1\n   local days=$2\n   local Month=`GetMonth $Date`\n   local Day=`echo $Date | col / 2`   # Day of Date\n   local Year=`echo $Date | col / 3`  # Year of Date\n   local LeapYear=`IsLeapYear $Year`\n\n   if [ $LeapYear = "yes" ]; then\n      let Months[1]++\n   fi\n   Day=$[Day+days]\n   while [ $Day -gt ${Months[$Month-1]} ]; do\n       Day=$[Day -  ${Months[$Month-1]}]\n       let Month++\n   done\n   echo "$Month/$Day/$Year"\n}\n\n# Convert a date to normal form\nNormalizeDate () {\n   Date=`echo "$*" | sed \'sX  *X/Xg\'`\n   local Day=`date +%d`\n   local Month=`date +%m`\n   local Year=`date +%Y`\n   #echo Normalizing Date=$Date &gt; /dev/tty\n   case $Date in\n      */*/* )\n         Month=`echo $Date | col / 1 `\n         Month=`Month2Int $Month`\n         Day=`echo $Date | col / 2`\n         Year=`echo $Date | col / 3`\n         ;;\n      */* )\n         Month=`echo $Date | col / 1 `\n         Month=`Month2Int $Month`\n         Day=1\n         Year=`echo $Date | col / 2 `\n         ;;\n      [a-z]* ) # Better be a month or day of week\n         Exp=${Date:0:3}\n         case $Exp in\n            jan|feb|mar|apr|may|june|jul|aug|sep|oct|nov|dec )\n               Month=$Exp\n               Month=`Month2Int $Month`\n               Day=1\n               #Year stays the same\n               ;;\n            mon|tue|wed|thu|fri|sat|sun )\n               # Compute the next such day\n               local DayOfWeek=`date +%u`\n               D=`Day2Int $Exp`\n               if [ $DayOfWeek -le $D ]; then\n                  Date=`AddToDate $Month/$Day/$Year $[D-DayOfWeek]`\n               else\n                  Date=`AddToDate $Month/$Day/$Year $[7+D-DayOfWeek]`\n               fi\n\n               # Reset Month/Day/Year\n               Month=`echo $Date | col / 1 `\n               Day=`echo $Date | col / 2`\n               Year=`echo $Date | col / 3`\n               ;;\n            * ) echo "$Exp is not a valid month or day"\n                exit\n               ;;\n            esac\n         ;;\n      * ) echo "$Date is not a valid date"\n          exit\n         ;;\n   esac\n   case $Day in\n      [0-9]* );;  # Day must be numeric\n      * ) echo "$Date is not a valid date"\n          exit\n         ;;\n   esac\n      [0-9][0-9][0-9][0-9] );;  # Year must be 4 digits\n      [0-9][0-9] )\n          Year=20$Year\n      ;;\n   esac\n   Date=$Month/$Day/$Year\n   echo $Date\n}\n# NormalizeDate jan\n# NormalizeDate january\n# NormalizeDate jan 2009\n# NormalizeDate jan 22 1983\n# NormalizeDate 1/22\n# NormalizeDate 1 22\n# NormalizeDate sat\n# NormalizeDate sun\n# NormalizeDate mon\n\nComputeExtension () {\n\n   local Date=$1; shift\n   local Month=`GetMonth $Date`\n   local Day=`echo $Date | col / 2`\n   local Year=`echo $Date | col / 3`\n   local ExtensionExp="$*"\n   case $ExtensionExp in\n      *w*d* )  # like 5 weeks 3 days or even 5w2d\n            ExtensionExp=`echo $ExtensionExp | sed \'s/[a-z]/ /g\'`\n            weeks=`echo $ExtensionExp | col  1`\n            days=`echo $ExtensionExp | col 2`\n            days=$[7*weeks+days]\n            Due=`AddToDate $Month/$Day/$Year $days`\n      ;;\n      *d )    # Like 5 days or 5d\n            ExtensionExp=`echo $ExtensionExp | sed \'s/[a-z]/ /g\'`\n            days=$ExtensionExp\n            Due=`AddToDate $Month/$Day/$Year $days`\n      ;;\n      * )\n            Due=$ExtensionExp\n      ;;\n   esac\n   echo $Due\n\n}\n\n\n# Pop -- remove the first element from an array and shift left\nPop () {\n   Var=$1\n   eval "unset $Var[0]"\n   eval "$Var=(\\${$Var[*]})"\n}\n\nComputeDate () {\n   local Date=`NormalizeDate $1`; shift\n   local Expression=`echo $* | sed \'s/^ *a /1 /;s/,/ /\' | tr A-Z a-z `\n   local Exp=(`echo $Expression `)\n   local Token=$Exp  # first one\n   local Ans=\n   #echo "Computing date for ${Exp[*]}" &gt; /dev/tty\n   case $Token in\n      */* ) # Regular date\n         M=`GetMonth $Token`\n         D=`GetDay $Token`\n         Y=`GetYear $Token`\n         if [ -z "$Y" ]; then\n            Y=$Year\n         elif [ ${#Y} -eq 2 ]; then\n            Y=20$Y\n         fi\n         Ans="$M/$D/$Y"\n         ;;\n      yes* )\n         Ans=`AddToDate $Date -1`\n         ;;\n      tod*|now )\n         Ans=$Date\n         ;;\n      tom* )\n         Ans=`AddToDate $Date 1`\n         ;;\n      the )\n         case $Expression in\n            *day*after* )  #the day after Date\n               Pop Exp;   # Skip the\n               Pop Exp;   # Skip day\n               Pop Exp;   # Skip after\n               #echo Calling ComputeDate $Date ${Exp[*]} &gt; /dev/tty\n               Date=`ComputeDate $Date ${Exp[*]}` #Recursive call\n               #echo "New date is " $Date &gt; /dev/tty\n               Ans=`AddToDate $Date 1`\n               ;;\n            *last*day*of*th*month|*end*of*th*month )\n               M=`date +%m`\n               Day=${Months[M-1]}\n               if [ $M -eq 2 -a `IsLeapYear $Year` = yes ]; then\n                  let Day++\n               fi\n               Ans=$Month/$Day/$Year\n               ;;\n            *last*day*of* )\n               D=${Expression##*of }\n               D=`NormalizeDate $D`\n               M=`GetMonth $D`\n               Y=`GetYear $D`\n               # echo M is $M &gt; /dev/tty\n               Day=${Months[M-1]}\n               if [ $M -eq 2 -a `IsLeapYear $Y` = yes ]; then\n                  let Day++\n               fi\n               Ans=$[M]/$Day/$Y\n               ;;\n            * )\n               echo "Unknown expression: " $Expression\n               exit\n               ;;\n         esac\n         ;;\n      next* ) # next DayOfWeek\n         Pop Exp\n         dow=`Day2Int $DayOfWeek` # First 3 chars\n         tdow=`Day2Int ${Exp:0:3}` # First 3 chars\n         n=$[7-dow+tdow]\n         Ans=`AddToDate $Date $n`\n         ;;\n      this* )\n         Pop Exp\n         dow=`Day2Int $DayOfWeek`\n         tdow=`Day2Int ${Exp:0:3}` # First 3 chars\n         if [ $dow -gt $tdow ]; then\n            echo "\'this $Exp\' has passed.  Did you mean \'next $Exp?\'"\n            exit\n         fi\n         n=$[tdow-dow]\n         Ans=`AddToDate $Date $n`\n         ;;\n      [a-z]* ) # DayOfWeek ...\n\n         M=${Exp:0:3}\n         case $M in\n            jan|feb|mar|apr|may|june|jul|aug|sep|oct|nov|dec )\n               ND=`NormalizeDate ${Exp[*]}`\n               Ans=$ND\n               ;;\n            mon|tue|wed|thu|fri|sat|sun )\n               dow=`Day2Int $DayOfWeek`\n               Ans=`NormalizeDate $Exp`\n\n               if [ ${#Exp[*]} -gt 1 ]; then # Just a DayOfWeek\n                  #tdow=`GetDay $Exp` # First 3 chars\n                  #if [ $dow -gt $tdow ]; then\n                     #echo "\'this $Exp\' has passed.  Did you mean \'next $Exp\'?"\n                     #exit\n                  #fi\n                  #n=$[tdow-dow]\n               #else  # DayOfWeek in a future week\n                  Pop Exp  # toss monday\n                  Pop Exp  # toss in/off\n                  if [ $Exp = next ]; then\n                     Exp=2\n                  fi\n                  n=$[7*(Exp-1)]   # number of weeks\n                  n=$[n+7-dow+tdow]\n                  Ans=`AddToDate $Date $n`\n               fi\n               ;;\n         esac\n         ;;\n      [0-9]* ) # Number  weeks [from|after] Date\n         n=$Exp\n         Pop Exp;\n         case $Exp in\n            w* ) let n=7*n;;\n         esac\n\n         Pop Exp; Pop Exp\n         #echo Calling ComputeDate $Date ${Exp[*]} &gt; /dev/tty\n         Date=`ComputeDate $Date ${Exp[*]}` #Recursive call\n         #echo "New date is " $Date &gt; /dev/tty\n         Ans=`AddToDate $Date $n`\n         ;;\n   esac\n   echo $Ans\n}\n\nYear=`date +%Y`\nMonth=`date +%m`\nDay=`date +%d`\nDayOfWeek=`date +%a |tr A-Z a-z`\n\nDate="$Month/$Day/$Year"\nComputeDate $Date $*\n</code></pre>\n\n<p>This script makes extensive use of another script I wrote (called <strong>col</strong> ... many apologies to those who use the standard <strong>col</strong> supplied with Linux).  This version of\n<strong>col</strong> simplifies extracting columns from the stdin.  Thus,</p>\n\n<pre><code>$ echo a b c d e | col 5 3 2\n</code></pre>\n\n<p>prints</p>\n\n<pre><code>e c b\n</code></pre>\n\n<p>Here it the <strong>col</strong> script:</p>\n\n<pre><code>#!/bin/sh\n# col -- extract columns from a file\n# Usage:\n#    col [-r] [c] col-1 col-2 ...\n#   where [c] if supplied defines the field separator\n#   where each col-i represents a column interpreted according to  the presence of -r as follows:\n#        -r present : counting starts from the right end of the line\n#        -r absent  : counting starts from the left side of the line\nSeparator=" "\nReverse=false\ncase "$1" in\n -r )  Reverse=true; shift;\n ;;\n [0-9]* )\n ;;\n * )Separator="$1"; shift;\n ;;\nesac\n\ncase "$1" in\n -r )  Reverse=true; shift;\n ;;\n [0-9]* )\n ;;\n * )Separator="$1"; shift;\n ;;\nesac\n\n#  Replace each col-i with $i\nCols=""\nfor  f in $*\ndo\n  if [ $Reverse = true ]; then\n     Cols="$Cols \\$(NF-$f+1),"\n  else\n     Cols="$Cols \\$$f,"\n  fi\n\ndone\n\nCols=`echo "$Cols" | sed \'s/,$//\'`\n#echo "Using column specifications of $Cols"\nawk -F "$Separator"  "{print $Cols}"\n</code></pre>\n\n<p>It also uses <strong>printdirections</strong> for printing out directions when the script is invoked improperly:</p>\n\n<pre><code>#!/bin/sh\n#\n#  printdirections -- print header lines of a shell script\n#\n#  Usage:\n#      printdirections path\n#  where\n#      path is a *full* path to the shell script in question\n#      beginning with \'/\'\n#\n#  To use printdirections, you must include (as comments at the top\n#  of your shell script) documentation for running the shell script.\n\nif [ $# -eq 0 -o "$*" = "-h" ]; then\n   printdirections $0\n   exit\nfi\n#  Delete the command invocation at the top of the file, if any\n#  Delete from the place where printdirections occurs to the end of the file\n#  Remove the # comments\n#  There is a bizarre oddity here.\n   sed \'/#!/d;/.*printdirections/,$d;/ *#/!d;s/# //;s/#//\' $1 &gt; /tmp/printdirections.$$\n\n#  Count the number of lines\nnumlines=`wc -l /tmp/printdirections.$$ | awk \'{print $1}\'`\n\n#  Remove the last   line\nnumlines=`expr $numlines - 1`\n\n\nhead -n $numlines /tmp/printdirections.$$\nrm /tmp/printdirections.$$\n</code></pre>\n\n<p>To use this place the three scripts in the files <strong>ComputeDate</strong>, <strong>col</strong>, and <strong>printdirections</strong>, respectively.  Place the file in directory named by your PATH, typically, ~/bin.  Then make them executable with:</p>\n\n<pre><code>$ chmod a+x ComputeDate col printdirections\n</code></pre>\n\n<p>Problems? Send me some emaiL: morell AT cs.atu.edu   Place <strong>ComputeDate</strong> in the subject.</p>\n'}, {'answer_id': 8244643, 'author': 'Harun Prasad', 'author_id': 222765, 'author_profile': 'https://Stackoverflow.com/users/222765', 'pm_score': 4, 'selected': False, 'text': '<p>Here is an easy way for doing date computations in shell scripting.</p>\n\n<pre><code>meetingDate=\'12/31/2011\' # MM/DD/YYYY Format\nreminderDate=`date --date=$meetingDate\'-1 day\' +\'%m/%d/%Y\'`\necho $reminderDate\n</code></pre>\n\n<p>Below are more variations of date computation that can be achieved using <code>date</code> utility.\n<a href="http://www.cyberciti.biz/tips/linux-unix-get-yesterdays-tomorrows-date.html" rel="noreferrer">http://www.cyberciti.biz/tips/linux-unix-get-yesterdays-tomorrows-date.html</a>\n<a href="http://www.cyberciti.biz/faq/linux-unix-formatting-dates-for-display/" rel="noreferrer">http://www.cyberciti.biz/faq/linux-unix-formatting-dates-for-display/</a></p>\n\n<p>This worked for me on RHEL.</p>\n'}, {'answer_id': 10315034, 'author': 'Johnny Baloney', 'author_id': 779449, 'author_profile': 'https://Stackoverflow.com/users/779449', 'pm_score': 0, 'selected': False, 'text': '<p>Here are my two pennies worth - a script wrapper making use of <code>date</code> and <code>grep</code>.</p>\n\n<p><em>Example Usage</em></p>\n\n<pre><code>&gt; sh ./datecalc.sh "2012-08-04 19:43:00" + 1s\n2012-08-04 19:43:00 + 0d0h0m1s\n2012-08-04 19:43:01\n\n&gt; sh ./datecalc.sh "2012-08-04 19:43:00" - 1s1m1h1d\n2012-08-04 19:43:00 - 1d1h1m1s\n2012-08-03 18:41:59\n\n&gt; sh ./datecalc.sh "2012-08-04 19:43:00" - 1d2d1h2h1m2m1s2sblahblah\n2012-08-04 19:43:00 - 1d1h1m1s\n2012-08-03 18:41:59\n\n&gt; sh ./datecalc.sh "2012-08-04 19:43:00" x 1d\nBad operator :-(\n\n&gt; sh ./datecalc.sh "2012-08-04 19:43:00"\nMissing arguments :-(\n\n&gt; sh ./datecalc.sh gibberish + 1h\ndate: invalid date `gibberish\'\nInvalid date :-(\n</code></pre>\n\n<p><em>Script</em></p>\n\n<pre><code>#!/bin/sh\n\n# Usage:\n#\n# datecalc "&lt;date&gt;" &lt;operator&gt; &lt;period&gt;\n#\n# &lt;date&gt; ::= see "man date", section "DATE STRING"\n# &lt;operator&gt; ::= + | -\n# &lt;period&gt; ::= INTEGER&lt;unit&gt; | INTEGER&lt;unit&gt;&lt;period&gt;\n# &lt;unit&gt; ::= s | m | h | d\n\nif [ $# -lt 3 ]; then\necho "Missing arguments :-("\nexit; fi\n\ndate=`eval "date -d \\"$1\\" +%s"`\nif [ -z $date ]; then\necho "Invalid date :-("\nexit; fi\n\nif ! ([ $2 == "-" ] || [ $2 == "+" ]); then\necho "Bad operator :-("\nexit; fi\nop=$2\n\nminute=$[60]\nhour=$[$minute*$minute]\nday=$[24*$hour]\n\ns=`echo $3 | grep -oe \'[0-9]*s\' | grep -m 1 -oe \'[0-9]*\'`\nm=`echo $3 | grep -oe \'[0-9]*m\' | grep -m 1 -oe \'[0-9]*\'`\nh=`echo $3 | grep -oe \'[0-9]*h\' | grep -m 1 -oe \'[0-9]*\'`\nd=`echo $3 | grep -oe \'[0-9]*d\' | grep -m 1 -oe \'[0-9]*\'`\nif [ -z $s ]; then s=0; fi\nif [ -z $m ]; then m=0; fi\nif [ -z $h ]; then h=0; fi\nif [ -z $d ]; then d=0; fi\n\nms=$[$m*$minute]\nhs=$[$h*$hour]\nds=$[$d*$day]\n\nsum=$[$s+$ms+$hs+$ds]\nout=$[$date$op$sum]\nformattedout=`eval "date -d @$out +\\"%Y-%m-%d %H:%M:%S\\""`\n\necho $1 $2 $d"d"$h"h"$m"m"$s"s"\necho $formattedout\n</code></pre>\n'}, {'answer_id': 26878392, 'author': 'sj26', 'author_id': 158252, 'author_profile': 'https://Stackoverflow.com/users/158252', 'pm_score': 3, 'selected': False, 'text': '<p>For BSD / OS X compatibility, you can also use the date utility with <code>-j</code> and <code>-v</code> to do date math. See the <a href="http://www.freebsd.org/cgi/man.cgi?query=date" rel="noreferrer">FreeBSD manpage for date</a>. You could combine the previous Linux answers with this answer which might provide you with sufficient compatibility.</p>\n\n<p>On BSD, as Linux, running <code>date</code> will give you the current date:</p>\n\n<pre><code>$ date\nWed 12 Nov 2014 13:36:00 AEDT\n</code></pre>\n\n<p>Now with BSD\'s date you can do math with <code>-v</code>, for example listing tomorrow\'s date (<code>+1d</code> is <em>plus one day</em>):</p>\n\n<pre><code>$ date -v +1d\nThu 13 Nov 2014 13:36:34 AEDT\n</code></pre>\n\n<p>You can use an existing date as the base, and optionally specify the parse format using strftime, and make sure you use <code>-j</code> so you don\'t change your system date:</p>\n\n<pre><code>$ date -j -f "%a %b %d %H:%M:%S %Y %z" "Sat Aug 09 13:37:14 2014 +1100"\nSat  9 Aug 2014 12:37:14 AEST\n</code></pre>\n\n<p>And you can use this as the base of date calculations:</p>\n\n<pre><code>$ date -v +1d -f "%a %b %d %H:%M:%S %Y %z" "Sat Aug 09 13:37:14 2014 +1100"\nSun 10 Aug 2014 12:37:14 AEST\n</code></pre>\n\n<p>Note that <code>-v</code> implies <code>-j</code>.</p>\n\n<p>Multiple adjustments can be provided sequentially:</p>\n\n<pre><code>$ date -v +1m -v -1w\nFri  5 Dec 2014 13:40:07 AEDT\n</code></pre>\n\n<p>See the manpage for more details.</p>\n'}, {'answer_id': 38362241, 'author': 'Tulio Galdamez', 'author_id': 6586682, 'author_profile': 'https://Stackoverflow.com/users/6586682', 'pm_score': -1, 'selected': False, 'text': "<p>This works for me:</p>\n\n<pre><code>TZ=GMT+6;\nexport TZ\nmes=`date --date='2 days ago' '+%m'`\ndia=`date --date='2 days ago' '+%d'`\nanio=`date --date='2 days ago' '+%Y'`\nhora=`date --date='2 days ago' '+%H'`\n</code></pre>\n"}]
['https://Stackoverflow.com/questions/6467', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/527/']