Code listing below.  Click here for program description.

You can cut-n-paste the code below or you can
access an ASCII text version of the code (which you can Save As...) by clicking here.


#!/usr/bin/nawk -f
# SccsId[] = "@(#)dateplus.awk 2.7 08/04/06"
#----------------------------------------------------------------------#
#                            dateplus.awk                              #
# -------------------------------------------------------------------- #
#                                                                      #
#   Copyright (c) 1995-2008 by Bob Orlando.  All rights reserved.      #
#                                                                      #
#   Permission to use, copy, modify and distribute this software       #
#   and its documentation for any purpose and without fee is hereby    #
#   granted, provided that the above copyright notice appear in all    #
#   copies, and that both the copyright notice and this permission     #
#   notice appear in supporting documentation, and that the name of    #
#   Bob Orlando not be used in advertising or publicity pertaining     #
#   to distribution of the software without specific, written prior    #
#   permission.  Bob Orlando makes no representations about the        #
#   suitability of this software for any purpose.  It is provided      #
#   "as is" without express or implied warranty.                       #
#                                                                      #
#   BOB ORLANDO DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS           #
#   SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY      #
#   AND FITNESS.  IN NO EVENT SHALL BOB ORLANDO BE LIABLE FOR ANY      #
#   SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES          #
#   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER    #
#   IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,     #
#   ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF     #
#   THIS SOFTWARE.                                                     #
#                                                                      #
#----------------------------------------------------------------------#

#----------------------------------------------------------------------#
#   This program is the Awk equivalent of my C program, dateplus.c.    #
#   However, this program still supported/maintained as part of my     #
#   shell library for those sites which restrict C-compiler access.    #
# -------------------------------------------------------------------- #
#   Program documentation and notes located at the bottom of script.   #
#----------------------------------------------------------------------#
BEGIN \
{
  progname = "dateplus.awk"
  #------------------------------------------------------------#
  # If user supplies no args (ARGC == 1), then show usage and  #
  # exit.  Else, continue and see if they gave us options.     #
  #------------------------------------------------------------#
  if (ARGC == 1) # Zero-based numbering drives me nuts!
    exit_usage()

  #------------------------------------------------------------#
  # Set up the following arrays.                               #
  #------------------------------------------------------------#
  split("0 31 59 90 120 151 181 212 243 273 304 334 365", julian_days)
  split("0 31 60 91 121 152 182 213 244 274 305 335 366", julian_leap)

  adj   = 0
  opt_v = 0

  #------------------------------------------------------------#
  # If last arg looks like a valid date, parse, validate, and  #
  # use it, else, use the current yyyymmdd.                    #
  #------------------------------------------------------------#
  if (ARGV[ARGC-1] ~ /^[0-2][0-9][0-9][0-9][0-1][0-9][0-3][0-9]$/)
  {
    yyyy = substr(ARGV[ARGC-1],1,4)
    mm   = substr(ARGV[ARGC-1],5,2)
    dd   = substr(ARGV[ARGC-1],7,2)
    validate_yyyymmdd(yyyy,mm,dd)
  }
  else
  {
    #-------------------------------------------------------#
    # Calling date with "+\%Y\%m\%d" (with escaped percent  #
    # signs) avoids hassles with SCCS and percentYpercent.  #
    #-------------------------------------------------------#
    "date +\%Y\%m\%d" | getline yyyymmdd
    yyyy = substr(yyyymmdd,1,4)
    mm   = substr(yyyymmdd,5,2)
    dd   = substr(yyyymmdd,7,2)
  }

  #------------------------------------------------------------#
  # Verbose (development) output wanted?                       #
  #------------------------------------------------------------#
  for (n=1; n<ARGC; n++)
  {
    if (ARGV[n] == "-v") # Verbose option
    {
      opt_v = 1
      ARGV[n] = ""
      shift_ARGV()
      break
    }
  }

  if (opt_v) print "Using "yyyy""mm""dd

  #------------------------------------------------------------#
  # Validate and process remaining options.                    #
  #------------------------------------------------------------#
  if      (ARGV[1] == "-y")       # -y = Yesterday's date
    calculate_date(yyyy,mm,dd,-1)
  else if (ARGV[1] == "-t")       # -t = Tomorrow's date
    calculate_date(yyyy,mm,dd,1)
  else if (ARGV[1] == "-b")       # -b = Basedate for today|yyyymmdd
    basedate(yyyy,mm,dd,1)
  else if (ARGV[1] ~ /^-[Ww]$/)   # -[Ww] = Weekday for today|yyyymmdd
    calc_weekday(yyyy,mm,dd)
  else if (ARGV[1] == "-S")       # -S = Days since yyyymmdd (unsigned)
    days_since(yyyy,mm,dd,"S")
  else if (ARGV[1] == "-s")       # -s = Days since yyyymmdd
    days_since(yyyy,mm,dd)
  else if (ARGV[1] == "-u")       # -u = Days since Unix epoch date
    days_since(yyyy,mm,dd,"u")
  else if (ARGV[1] == "-j")       # -j = Julian date
    julian_date(yyyy,mm,dd)
  else if (ARGV[1] == "-H")       # -H = Full documentation
    show_documentation(progname)
  else if (ARGV[1] == "-h")       # -h = Summary help (Usage)
    exit_usage()
  else if (ARGV[1] ~ /^-[0-9]+$/) # Negative adjustment value
    adj =  ARGV[1]
  else if (ARGV[1] ~ /^-/ && ARGV[1] !~ /^-[0-9]+$/) # Oops
    exit_usage("Invalid "progname" option, '"ARGV[1]"'.")

  #------------------------------------------------------------#
  # Options (and negative adj days) look good; skip past'em.   #
  #------------------------------------------------------------#
  if (ARGV[1] ~ /^-[bhstuwy]$/ || ARGV[1] ~ /^-[0-9]+$/)
    shift_ARGV()

  #------------------------------------------------------------#
  # Positive (or unsigned) integer for adj days next?          #
  #------------------------------------------------------------#
  if (ARGV[1] ~ /^\+?[0-9]+$/ && adj == 0)
  {
    adj = ARGV[1]
    shift_ARGV()
  }

  #------------------------------------------------------------#
  # OK, do it the long way--see if I care :-))                 #
  #------------------------------------------------------------#
  if (opt_v)
    print "calculate_date("yyyy","mm","dd","adj")"
           calculate_date( yyyy , mm , dd , adj )
  exit 0
} # E.O.BEGIN

#======================================================================#
#                     U S E R    F U N C T I O N S                     #
#                        (in alphabetical order)                       #
#----------------------------------------------------------------------#
function basedate(                                               \
                   YYYY,MM,DD,OPT_B,                             \
                   yyyy,cc,ddddd,leapdays,cc_leapdays,accum_days \
                 )                                                     #
# -------------------------------------------------------------------- #
# Globals: julian_days[] and julian_leap[]                             #
#   Calls: leap_year()                                                 #
#----------------------------------------------------------------------#
{
  if (opt_v)
  {
    print "basedate(YYYY,MM,DD,OPT_B)"
    print "basedate("YYYY","MM","DD","OPT_B")"
  }
  #--------------------------------------------------------#
  # First, decrement the year and get the number of days   #
  # for all the years preceeding it (in the Common Era).   #
  #--------------------------------------------------------#
  yyyy = YYYY
  if ((yyyy - 1) > 0)
  {                                                   ;if (opt_v) print "   YYYY       =|"YYYY       "|"
    yyyy--                                            ;if (opt_v) print "   yyyy       =|"yyyy       "|"
    cc          = int(yyyy / 100)                     ;if (opt_v) print "   cc         =|"cc         "|"
    ddddd       = yyyy * 365                          ;if (opt_v) print "   ddddd      =|"ddddd      "|"
    leapdays    = int(yyyy / 4)                       ;if (opt_v) print "   leapdays   =|"leapdays   "|"
    cc_leapdays = (cc > 0) ? (int(cc / 4)) : 0        ;if (opt_v) print "   cc_leapdays=|"cc_leapdays"|"
    ddddd       = ddddd + leapdays - cc + cc_leapdays ;if (opt_v) print "   ddddd      =|"ddddd      "|"
    yyyy++                                            ;if (opt_v) print "   yyyy       =|"yyyy       "|"
  }
  ddddd += DD                                         ;if (opt_v) print "   ddddd      =|"ddddd      "|"

  #---------------------------------------------------------#
  # Add the previous month YTD days to our ddddd.  If it    #
  # is a leap year, assign julian_leap[] to accum_days[].   #
  # Then add in YTD days for previous months of this year   #
  #---------------------------------------------------------#
  if (leap_year(yyyy))
    for (i=0; i<13; i++) accum_days[i] = julian_leap[i]
  else
    for (i=0; i<13; i++) accum_days[i] = julian_days[i]
  ddddd += accum_days[MM += 0] # 'MM += 0' removes leading zero

  if (OPT_B) # If all they want is basedate, print it and exit.
  {
    print ddddd
    exit 0
  }

  if (opt_v)
    print "==> return ("ddddd")"
               return ( ddddd )
} # function basedate

#----------------------------------------------------------------------#
function calc_weekday(YYYY,MM,DD,                    ddddd,weekdays,i) #
#----------------------------------------------------------------------#
{
  if (opt_v)
  {
    print "calculate_weekday(YYYY,MM,DD)"
    print "calculate_weekday("YYYY","MM","DD")"
  }

  if (ARGV[1] == "-w") # -[Ww] = Weekday for today|yyyymmdd
    split("Sun Mon Tue Wed Thu Fri Sat", weekdays)
  else
    split("Sunday Monday Tuesday Wednesday Thursday Friday Saturday", weekdays)

  ddddd = (basedate(YYYY,MM,DD) % 7)
  print weekdays[ddddd+1]
  exit ddddd # ddddd = day number [0-6]
  #------------------------------------------------------------------#
  # http://www.tondering.dk/claus/cal/calendar26.txt                 #
  # In day of the week calculations, the divisions are integer       #
  # divisions, in which remainders are discarded.                    #
  # a = (14 - month) / 12                                            #
  # y = year - a                                                     #
  # m = month + 12*a - 2                                             #
  # Julian d = (5 + day + y + y/4 + (31*m)/12) mod 7                 #
  # Gregorian d = (day + y + y/4 - y/100 + y/400 + (31*m)/12) mod 7  #
  #------------------------------------------------------------------#
} # function calc_weekday

#----------------------------------------------------------------------#
function calculate_date(YYYY,MM,DD,ADJ,                  i,month_days) #
# -------------------------------------------------------------------- #
# Calls: leap_year(), ddddd_to_yyyymmdd(), and basedate()              #
#----------------------------------------------------------------------#
{
  if (opt_v)
  {
    print "calculate_date(YYYY,MM,DD,ADJ)"
    print "calculate_date("YYYY","MM","DD","ADJ")"
  }

  split("31 28 31 30 31 30 31 31 30 31 30 31", month_days)
  if (leap_year(YYYY) && mm == 2) month_days[2] = 29
  if (ADJ < 0 && ADJ > (DD * -1))                       # Shortcut -n
  {
    if (opt_v)
      print "printf(\"%4d%02d%02d\n\","YYYY","MM",("DD" + "ADJ"))"
             printf( "%4d%02d%02d\n" , YYYY , MM ,( DD  +  ADJ ))
  }
  else if (ADJ > 0 && ((DD + ADJ) <= month_days[MM+0])) # Shortcut +n
  {
    if (opt_v)
      print "printf(\"%4d%02d%02d\n\","YYYY","MM",("DD" + "ADJ"))"
             printf( "%4d%02d%02d\n" , YYYY , MM ,( DD  +  ADJ ))
  }
  else                                                  # Long way
  {
    if (opt_v)
      print "ddddd_to_yyyymmdd(basedate("YYYY","MM","DD") + "ADJ")"
      print  ddddd_to_yyyymmdd(basedate( YYYY , MM , DD ) +  ADJ )
  }
  exit 0
} # function calculate_date

#----------------------------------------------------------------------#
function days_since(                                      \
                     YYYY,MM,DD,OPT,                      \
                     yyyymmdd,mm,dd,yyyy,ddddd,ddddd_yyyy \
                   ) # Global variables: progname                      #
#----------------------------------------------------------------------#
{
  ddddd = basedate(YYYY,MM,DD)
  if (OPT != "u")
  {
    ddddd_yyyy = ddddd
   "date +\%Y\%m\%d" | getline yyyymmdd # Today's date
    yyyy = substr(yyyymmdd,1,4)
    mm   = substr(yyyymmdd,5,2)
    dd   = substr(yyyymmdd,7,2)

    ddddd = basedate(yyyy,mm,dd)
    ddddd -= ddddd_yyyy
    if (OPT == "S" && ddddd < 0) ddddd = ddddd - ddddd - ddddd
  }
  else
  {
    if (YYYY >= 1970)
    {
      ddddd_yyyy = basedate(1970,01,01)
      ddddd -= ddddd_yyyy
    }
    else
    {
      printf("This is a trick question, right?  ")         |"cat 1>&2"
      printf("You requested the number of days since\n")   |"cat 1>&2"
      printf("Jan. 1, 1970, (-u option) but you provided ")|"cat 1>&2"
      printf("a date that is older than that.\n")          |"cat 1>&2"
      printf("%s terminated.\n", progname)                 |"cat 1>&2"
      exit 6
    }
  }

  print ddddd
  exit 0
} # function days_since

#----------------------------------------------------------------------#
function ddddd_to_yyyymmdd(DDDDD,                   ddddd,yyyy,mmdd,i) #
# -------------------------------------------------------------------- #
# Globals: julian_days[] and julian_leap[]                             #
#----------------------------------------------------------------------#
{
  if (opt_v)
  {
    print "ddddd_to_yyyymmdd(DDDDD)"
    print "ddddd_to_yyyymmdd("DDDDD")"
  }

  yyyy = ddddd = reduce(DDDDD)
  if (opt_v) print "yyyy = ddddd = "ddddd
  sub(/-.*$/,"", yyyy); yyyy  += 0 # "+= 0" ensures numeric context
  sub(/^.*-/,"",ddddd); ddddd += 0
  if (opt_v)
  {
    print "yyyy  = |"yyyy"|"
    print "ddddd = |"ddddd"|"
  }

  if (leap_year(yyyy))
    for (i in julian_leap)
      julian[i] = julian_leap[i]
  else
    for (i in julian_days)
      julian[i] = julian_days[i]

  #------------------------------------------------------------#
  # Reduce ddddd to month and day (we already have the year).  #
  #------------------------------------------------------------#
  for (i=13; i>0; i--)
  {
    if (opt_v)
    {
      print "if (julian["i"] < "ddddd")"
      print "if ("julian[i]" < "ddddd")"
    }
    if (julian[i] < ddddd)
    {
    # if (leap_year(yyyy) && i > 2) ddddd++ # 20080406.164435
      ddddd -= julian[i]
      mmdd = sprintf("%02d%02d", i, ddddd)
      break
    }
  }

  if (opt_v)
  {
    print "return (yyyy\"\"mmdd)"
    print "return ("yyyy""mmdd")"
  }
           return ( yyyy""mmdd )
} # function ddddd_to_yyyymmdd

#----------------------------------------------------------------------#
function exit_usage(ERRMSG) # Global vars: progname                    #
#----------------------------------------------------------------------#
{
  if (ERRMSG != "") print "\n"ERRMSG | "cat 1>&2"
  print "\nUsage: [gn]awk -f "progname" -- -bHhjSstuvWwy -|+adj yyyymmdd.\n",
        "\n       -- = Allows AWK to pass script options and args."    ,
        "\n       -b = Basedate for today or yyyymmdd."                ,
        "\n       -H = Full formal documentation (functions only"      ,
        "\n            when the current working directory is the"      ,
        "\n            program directory)."                            ,
        "\n       -h = Summary help (Usage)."                          ,
        "\n       -j = Julian day for today or yyyymmdd."              ,
        "\n       -S = Days since (or until) yyyymmdd (unsigned both).",
        "\n       -s = Days since (or until) yyyymmdd (signed future).",
        "\n       -t = Tomorrow's date."                               ,
        "\n       -u = Basedate of today|yyyymmdd since"               ,
        "\n            Jan. 1, 1970--the Unix epoch date"              ,
        "\n            (essentially 'dateplus -s 19700101')."          ,
        "\n       -v = Verbose output (primarily for development)."    ,
        "\n       -W = Date's long weekday"                            ,
        "\n       -w = Date's abbreviated weekday"                     ,
        "\n       -y = Yesterday's date.\n"                            ,
        "\n      adj = Adjustment days that may be positive or"        ,
        "\n            negative (prefix sign annotation only)."        ,
        "\n            By itself (with no yyyymmdd date following,"    ,
        "\n            yields an adjusted date that is calculated"     ,
        "\n            from today's date.\n"                           ,
        "\n yyyymmdd = Optional date from which the adjusted date"     ,
        "\n            is calculated.\n"                               ,
        "\n            "progname" terminated.\n"
  exit 7
} # function exit_usage

#----------------------------------------------------------------------#
function julian_date(YYYY,MM,DD,   ddd) # Global vars: progname        #
#----------------------------------------------------------------------#
{
  ddd = reduce(basedate(YYYY,MM,DD))
  sub(/^.*-/,"",ddd)
  printf("%03d\n", ddd)

  exit 0
} # function julian_date

#----------------------------------------------------------------------#
function leap_year(YYYY) # YYYY = year.  Returns true|false (1|0).     #
#----------------------------------------------------------------------#
{
  return (((YYYY%4 == 0 && YYYY%100 != 0) || (YYYY%400 == 0)) ? 1 : 0)
} # function leap_year

#----------------------------------------------------------------------#
function reduce(DDDDD,           yyyy,ddddd,cc,leapdays,cc_leapdays,i) #
#----------------------------------------------------------------------#
{
  if (opt_v)
  {
    print "reduce(DDDDD)"
    print "reduce("DDDDD")"
  }

  for (i=1; ; i++)
  {                                            ;if (opt_v) print i". DDDDD      =|"DDDDD      "|"
    yyyy        = int(DDDDD / 365) - i         ;if (opt_v) print i". yyyy       =|"yyyy       "|"
    ddddd       = DDDDD - yyyy * 365           ;if (opt_v) print i". ddddd      =|"ddddd      "|"
    cc          = int(yyyy / 100)              ;if (opt_v) print i". cc         =|"cc         "|"
    leapdays    = int(yyyy / 4)                ;if (opt_v) print i". leapdays   =|"leapdays   "|"
    cc_leapdays = (cc > 0) ? (int(cc / 4)) : 0 ;if (opt_v) print i". cc_leapdays=|"cc_leapdays"|"
    leapdays    = leapdays - cc + cc_leapdays  ;if (opt_v) print i". leapdays   =|"leapdays   "|"
    ddddd      -= leapdays                     ;if (opt_v) print i". ddddd      =|"ddddd      "|"
    yyyy++                                     ;if (opt_v) print i". yyyy       =|"yyyy       "|"
    if (ddddd > 0) break
  }
  if (opt_v)
    print "return "yyyy"-"ddddd
           return  yyyy"-"ddddd
} # function reduce

#----------------------------------------------------------------------#
function shift_ARGV(i,j,k)                                             #
#----------------------------------------------------------------------#
{
  k = ARGC
  for (i=1; i<=ARGC; i++)
  {
    if (ARGV[i] == "")     # If the argument is empty, see is we
    {                      # can shift the next one down to it.
      for (j=i+1; j<=k; j++)
      {
        if (ARGV[j] == "") # If the next one is empty, try the one
          continue         # following that.
        ARGV[i] = ARGV[j]  # Once we find an argument, move it down,
        ARGV[j] = ""       # null where it was, decrement arg counter,
        break              # then do this with the next argument.
      }
    }
  }

  for (i=1; i<k; i++)      # Adjust ARGC
    if (ARGV[i] == "")     # If the argument is empty,
      ARGC--               # decrement ARGC
  return ARGC
} # function shift_ARGV

#----------------------------------------------------------------------#
function show_documentation(MOI,                               n,line) #
#----------------------------------------------------------------------#
{
  n = 0
  while (getline <MOI > 0) # Searching ourselves for the doc'n section.
  {
    #------------------------------------------#
    # Until we find the documentation section, #
    # keep looking at each line.               #
    #------------------------------------------#
    if (n == 0)
    {
      if ($0 ~ /^\043 +D O C U M E N T A T I O N/)
      {
        n = 1
        print line
        print $0
      }
      else
        line = $0

      continue
    }    #-----------------------------------#
    else # Once we find it, print until EOF. #
    {    #-----------------------------------#
      print
    }
  } # while (getline <MOI > 0)

  if (n == 0) # Means we did not find documentation section.
  {
    print "NO DOCUMENTATION section found for "MOI" in the",
          "current directory (cwd).\nTry again after first",
          "changing to the program directory." | "cat 1>&2"
    exit 10 # Exit failure
  }
  exit 11 # Exit failure anyway because we don'y live for this.
} # function show_documentation

#----------------------------------------------------------------------#
function validate_yyyymmdd(                                \
                            YYYY,MM,DD,                    \
                            yyyy,mm,dd,mm_days,month_names \
                          ) # Calls: leap_year()                       #
#----------------------------------------------------------------------#
{
  if (opt_v)
  {
    print "validate_yyyymmdd(YYYY,MM,DD)"
    print "validate_yyyymmdd("YYYY","MM","DD")"
  }

  yyyy = YYYY
  mm   = MM
  dd   = DD
  mm  += 0

  split("January  February March "     \
        "April    May      June "      \
        "July     August   September " \
        "October  November December",  month_names)

  if (mm==1||mm==3||mm==5||mm==7||mm==8||mm==10||mm==12)
    mm_days=31
  else if (mm==4||mm==6||mm==9||mm==11)
    mm_days=30
  else if (mm==2)
    mm_days = (leap_year(yyyy)) ? 29 : 28
  else
  {
    printf("Month %02d is invalid.\n", mm) | "cat 1>&2"
    exit 8
  }
  if (dd > mm_days)
  {
    print month_names[mm]", "yyyy,
      " does not have "dd" days!\n" | "cat 1>&2"
    exit 9
  }
  return 0
} # function validate_yyyymmdd

#======================================================================#
#                       D O C U M E N T A T I O N                      #
#======================================================================#
#                                                                      #
#      Author: Bob Orlando (Bob@OrlandoKuntao.com)                     #
#                                                                      #
#        Date: July 2, 1995                                            #
#                                                                      #
#  Program ID: dateplus.awk                                            #
#                                                                      #
#       Usage: [gn]awk -f dateplus.awk -- -bHhjSstuvWwy \              #
#                                         -|+adj yyyymmdd              #
#                                                                      #
#          nawk|gawk = Program runs with either. (See "Usage Note:"    #
#                      below for "#!" operation.)                      #
#                 -- = Allows AWK to pass script options and args.     #
#                 -b = Basedate for today or yyyymmdd.                 #
#                 -H = Full formal documentation (functions only       #
#                      when the current working directory is the       #
#                      program directory).                             #
#                 -h = Summary help (Usage).                           #
#                 -j = Julian day for today or yyyymmdd.               #
#                 -S = Days since (or until) yyyymmdd (unsigned both). #
#                 -s = Days since (or until) yyyymmdd (signed future). #
#                 -t = Tomorrow's date.                                #
#                 -u = Basedate of today|yyyymmdd since                #
#                      Jan. 1, 1970--the Unix epoch date               #
#                      (essentially "dateplus -s 19700101").           #
#                 -v = Verbose output (primarily for development).     #
#                 -W = Date's long weekday                             #
#                 -w = Date's abbreviated weekday                      #
#                      (see 'Exit Codes' below for -W and -w)          #
#                 -y = Yesterday's date.                               #
#                                                                      #
#                adj = Adjustment days that may be positive or         #
#                      negative (prefix sign annotation only).         #
#                      By itself (with no yyyymmdd date following,     #
#                      yields an adjusted date that is calculated      #
#                      from today's date.                              #
#           yyyymmdd = Optional date from which the adjusted date      #
#                      is calculated.                                  #
#                                                                      #
#  Usage Note: If your OS recognizes the "#!" (shebang) syntax,        #
#              you can place a "#!/usr/bin/nawk -f" (or gawk)          #
#              at the start of this program (as I have) thereby        #
#              allowing you skip the "[gn]awk -f" during invocation.   #
#                                                                      #
#     Purpose: This program calculates a date or dates that is         #
#              nnnnn days (+ or -) distant from either today's         #
#              date or another user-supplied date.                     #
#                                                                      #
# Description: This program is the Awk equivalent of my C program,     #
#              dateplus.c.  With the exception of -J (capital "J")     #
#              option and the iterations argument, all of the C        #
#              program options are available here.  This Awk           #
#              version of the dateplus.c program is supported and      #
#              maintained as part of my shell library for those        #
#              sites with limited C-compiler access.                   #
#                                                                      #
#              For all practical purposes, any date the user           #
#              wishes to use as a yyyymmdd (from 01/01/0001            #
#              through 12/31/9999) is accepted.  The resultant         #
#              date is returned via stdout in yyyymmdd form.           #
#              The program is especially useful for calculating        #
#              yesterday's, tomorrow's, or any other desired           #
#              date.  Additionally, by specifying an iteration         #
#              value (the last argument) the program generates         #
#              that number of dates, each adjusted on the              #
#              previous date (handy for calculating paydays and        #
#              the like).  Because the program writes to stdout        #
#              its output is easily redirected to a file where         #
#              the results can be subsequently edited or munged        #
#              as needed.  The program also comes with help            #
#              options (-h and -H for summary and detailed             #
#              outputs, respectively).                                 #
#                                                                      #
#  Exit Codes: For all options except weekday (-w)                     #
#                 Zero    = Normal   | Success                         #
#                 Nonzero = Abnormal | Failure                         #
#              For weekday (-w) option                                 #
#                 0-6 = Success (zero-based weekday: Sun = 0)          #
#                 7+  = Failure                                        #
#                                                                      #
#     History: The Gregorian correction to the Julian calendar         #
#              made in October, 1582 dropped 10 days.  That            #
#              is, October 4, 1582 was followed immediately by         #
#              October 15.  This Papal correction, although            #
#              scientifically correct, was not adopted by              #
#              non-Catholic countries until almost two                 #
#              centuries later in 1752.  In September, 1752            #
#              the English calendar was adjusted to Pope               #
#              Gregory's method of correction and 11 days were         #
#              dropped (September 14, followed September 2).           #
#                                                                      #
#              While this routine easily calculates the date           #
#              that far back, it does not drop October 5-14,           #
#              1582 or September 3-13, 1752.  If this routine          #
#              is used to calculate dates that far back, the           #
#              previously adjusted and dropped dates will              #
#              appear as if no calendar corrections were ever          #
#              made.  All of this is only for technical                #
#              purists.  Most real-world applications will not         #
#              go back that far.  This program's accuracy,             #
#              then, is sufficient for most purposes.                  #
#                                                                      #
#       Notes: I am also no mathematician.  Neither do I consider      #
#              myself an AWK expert.  If, therefore, any find          #
#              errors in my code, logic, or computations, please       #
#              advise me at Bob@OrlandoKuntao.com.  Your input         #
#              is sincerely appreciated.  Thank you in advance.        #
#                                                                      #
#      +----------------------------------------------------------+    #
#      |     For modifications, see 'Modified' section in the     |    #
#      |     source code immediately following this statement.    |    #
#      +----------------------------------------------------------+    #
#                                                                      #
#    Modified: 2008-04-06 Bob Orlando                                  #
#                 v2.7  * Bug fix: Commented out the following line    #
#                         ddddd_to_yyyymmdd function:                  #
#                                                                      #
#                           if (leap_year(yyyy) &&  i > 2) ddddd++     #
#                                                                      #
#                         The line was adding a day if the year        #
#                         is a leap year and the month is after        #
#                         February.  This was unnecessary because I    #
#                         already use separate tables for leap and     #
#                         normal years.                                #
#                                                                      #
#              2005-02-08 Bob Orlando                                  #
#                 v2.6  * Bug fix: Changed                             #
#                           if (leap_year(yyyy) && mm > 2) ddddd++     #
#                         to                                           #
#                           if (leap_year(yyyy) && i > 2) ddddd++      #
#                                                                      #
#----------------------------------------------------------------------#

©Copyright Bob Orlando, 1995-2008.
All rights reserved.
http://www.OrlandoKuntao.com
E-mail: Bob@OrlandoKuntao.com
Last update:  Apr. 6, 2008
by Bob Orlando