#!/usr/bin/perl -w
use strict;
use Getopt::Std;
use English;
# ---------------------------------------------------------------------
#   if 1 arg is supplied; converts JD to MM DD YYYY
#   if 3 args are supplied converts MM DD YYYY to Julian Day
#   if no args are supplied converts current date to Julian Day
# ---------------------------------------------------------------------
use constant VRSN => '[2.1 p]';
# 2.05p  04/29/2002 acs Converted from Shell/Awk caljd.sh Vrsn 2.05
# 2.05p1 05/13/2002 acs no more than 6 -x args allowed
# 2.1 p  06/26/2002 acs added the -c argument to allow 12/31/2001 to be
#                       processed; added -i and -o options so that months
#                       could be input or output in 'Jan','Feb', ... format
#                       or weekdays could be output in 'Sun','Mon', ...
#                       format; the -I and -O options do the same but now full
#                       names are used. e.g. 'January','February' ...
#                       and 'Sunday','Monday', ... . For example,
#                       '04-July-1776' could be parsed as valid input
#                       with the '-c -e -I -S "-"' options; NLS support
#                       is provided with the -i,-o,-I, and -O options;
#                       examples added to usage(). 
# ---------------------------------------------------------------------
use constant FALSE          => 0;
use constant TRUE           => 1;
use constant DMY_NONE       => 0;
use constant DMY_DAY        => 1;
use constant DMY_MONTH      => 2;
use constant DMY_YEAR       => 3;
use constant MDY_MDY        => 0;
use constant MDY_DMY        => 1;
use constant MDY_YMD        => 2;
use constant MAX_MONTH      => 12;
use constant MAX_DAY        => 31;
use constant DAYS_PER_WK    => 7;
use constant HOLIDAY_FILE   => '/etc/acct/holidays';
# Month and weekday names constants
use constant WORDS_NONE     => 0;
use constant WORDS_ABREV    => 1;
use constant WORDS_FULL     => 2;
use constant DAY_ABREV      => 0;
use constant DAY_FULL       => 1;
use constant MONTH_ABREV    => 2;
use constant MONTH_FULL     => 3;
# --------------------------------------------------------------------------
my $in_words = WORDS_NONE;
my $out_words = WORDS_NONE;
my @lc_time_array;
sub problem
{
  my $msg = $_[0];
  my $err = $_[1];
  printf STDERR ("%s: %s (%d).\n",$PROGRAM_NAME,$msg,$err);
  printf STDERR ("\n");
  return($err);
} # problem 
# -----------------------------------------------------------------
sub examples
{
  my $prog = $PROGRAM_NAME;
  printf STDERR ("\n\nEXAMPLES:\n\n");
  printf STDERR ("  Determine the current Julian Day:\n");
  printf STDERR ("  JD=\$(%s)\n\n",$prog);
  printf STDERR ("  Determine the Julian Day 25 days from now in UTC:\n");
  printf STDERR ("  JD=\$(%s -U -n 25)\n\n",$prog);
  printf STDERR ("  Determine the Julian Day 5 days prior to now:\n");
  printf STDERR ("  JD=\$(%s -p 5)\n\n",$prog);
  printf STDERR ("  Determine the Julian Day 5 days from now unless that ");
  printf STDERR ("falls on a weekend:\n");
  printf STDERR ("  JD=\$(%s -n 5 -x 6 -x 0)\n\n",$prog);
  printf STDERR ("  Determine the Julian Day 5 days from now unless that ");
  printf STDERR ("falls on a weekend\n");
  printf STDERR ("  or a holiday:\n");
  printf STDERR ("  JD=\$(%s -n 5 -x 6 -x 0 -h)\n\n",$prog);
  printf STDERR ("  Determine the month 2 days from now unless that ");
  printf STDERR ("falls on a weekend\n");
  printf STDERR ("  or a holiday; output in numeric format:\n");
  printf STDERR ("  JD=\$(%s -n 2 -x 6 -x 0 -h -M)\n\n",$prog);
  printf STDERR ("  Determine the month 3 days from now unless that ");
  printf STDERR ("falls on a weekend\n");
  printf STDERR ("  or a holiday; output in numeric format:\n");
  printf STDERR ("  JD=\$(%s -n 3 -x 6 -x 0 -h -M -O)\n\n",$prog);
  printf STDERR ("  Determine the Julian Day of 26-Jun-2002 MM DD YYYY ");
  printf STDERR ("format:\n");
  printf STDERR ("  JD=\$(%s 6 26 2002)\n\n",$prog);
  printf STDERR ("  Determine the Julian Day of 26-Jun-2002 YYYY MM DD ");
  printf STDERR ("format:\n");
  printf STDERR ("  JD=\$(%s -y 2002 6 26)\n\n",$prog);
  printf STDERR ("  Determine the Julian Day of 26-Jun-2002 entered as ");
  printf STDERR ("'26-JUN-2002':\n");
  printf STDERR ("  JD=\$(%s -i -S \"-\" -e -c  \"26-JUN-2002\")\n\n",$prog);
  printf STDERR ("  Determine the Julian Day 7 days from 26-Jun-2002 ");
  printf STDERR ("entered as 'JUNE/26/2002':\n");
  printf STDERR ("  JD=\$(%s -I -S \"/\" -n 7  -c  \"JUNE/26/2002\")\n\n",
                 $prog);
  printf STDERR ("  Determine the calendar date of JD 2452452; output in ");
  printf STDERR ("MM DD YYYY\n");
  printf STDERR ("  format:\n");
  printf STDERR ("  CD=\$(%s 2452452)\n\n",$prog);
  printf STDERR ("  Determine the calendar date of JD 2452452; output in ");
  printf STDERR ("DD MM YYYY\n");
  printf STDERR ("  format:\n");
  printf STDERR ("  CD=\$(%s -e 2452452)\n\n",$prog);
  printf STDERR ("  Determine the calendar date 12 days after JD 2452452;\n");
  printf STDERR ("  output in YYYY MM DD format:\n");
  printf STDERR ("  CD=\$(%s -y -n 12 2452452)\n\n",$prog);
  printf STDERR ("  Determine the calendar date 2 days before JD 2452452\n");
  printf STDERR ("  unless that falls on a Sunday; output in YYYY MM DD ");
  printf STDERR ("format:\n");
  printf STDERR ("  CD=\$(%s -y -p 2 -x 0 2452452)\n\n",$prog);
  printf STDERR ("\f");
  printf STDERR ("\n");
  printf STDERR ("  Determine the calendar date 20 days after JD 2452452\n");
  printf STDERR ("  unless that falls on a holiday; output in ");
  printf STDERR ("DD-FullMonthName-YYYY\n");
  printf STDERR ("  format:\n");
  printf STDERR ("  CD=\$(%s -e -n 20 -h -O -S \"-\" 2452452)\n\n",$prog);
  printf STDERR ("  Determine the weekday 5 days after JD 2452452\n");
  printf STDERR ("  unless that falls on a holiday; output in numeric ");
  printf STDERR ("format:\n");
  printf STDERR ("  CD=\$(%s -n 5 -h -w 2452452)\n\n",$prog);
  printf STDERR ("  Determine the weekday 4 days prior to JD 2452452;\n");
  printf STDERR ("  output in abbreviated weekday name format:\n");
  printf STDERR ("  CD=\$(%s -p 4 -w -o 2452452)\n\n",$prog);
  printf STDERR ("  Get the calendar date in DD MM YYYY format 9 days from ");
  printf STDERR ("now unless that\n");
  printf STDERR ("  falls on a Saturday:\n");
  printf STDERR ("  CD=\$(%s -e \$(%s -n 9 -x 6))\n\n",$prog,$prog);
  printf STDERR ("  Get the calendar date in YYYYMMDD format 3 days prior ");
  printf STDERR ("to now unless that\n");
  printf STDERR ("  falls on a Sunday:\n");
  printf STDERR ("  CD=\$(%s -y -s \$(%s -p 3 -x 0))\n\n",$prog,$prog);
  printf STDERR ("  Get the calendar date in YYYYAbbrevMonthNameDD format ");
  printf STDERR ("3 days\n");
  printf STDERR ("  prior to now unless that falls on a Sunday:\n");
  printf STDERR ("  CD=\$(%s -y -s -o \$(%s -p 3 -x 0))\n\n",$prog,$prog);
  printf STDERR ("\f");
  return(254);
} # examples
sub usage
{
  printf STDERR 
    ("\nUsage: %s [-w|-D|-M|-Y] [-u] [-U] [-n days | -p days] \\\n",
     $PROGRAM_NAME);
  printf STDERR
    ("          [-h] [-x wday] [-v|-V]\n");
  printf STDERR ("        or\n");
  printf STDERR
    ("       %s [-w|-D|-M|-Y] [-n days | -p days] \\\n",
     $PROGRAM_NAME);
  printf STDERR
    ("          [-h] [-x wday] [-v|-V] [-c] MM DD YYYY\n");
  printf STDERR ("        or\n");
  printf STDERR
    ("       %s [-w|-D|-M|-Y] [-n days | -p days] \\\n",
     $PROGRAM_NAME);
  printf STDERR
   ("          [-h] [-x wday] [-v|-V] [-c] -e DD MM YYYY\n");
  printf STDERR ("        or\n");
  printf STDERR
    ("       %s [-w|-D|-M|-Y] [-n days | -p days] \\\n",
     $PROGRAM_NAME);
  printf STDERR
    ("          [-h] [-x wday] [-v|-V] [-c] -y YYYY MM DD\n");
  printf STDERR ("        or\n");
  printf STDERR
    ("       %s [-w|-D|-M|-Y] [-n days | -p days] [-e | -y]  \\\n",
     $PROGRAM_NAME);
  printf STDERR
    ("               [-h] [-x wday] [-v|-V] [-s] [-S sep] JulianDay\n");
  printf STDERR ("\n");
  printf STDERR ("If MM DD YYYY is supplied, convert to Julian Day or\n");
  printf STDERR ("weekday if -w is supplied.\n");
  printf STDERR ("If JulianDay is supplied, convert to MM DD YYYY or\n"); 
  printf STDERR ("weekday if -w is supplied.\n");
  printf STDERR ("If no args are supplied, convert current date to Julian\n");
  printf STDERR ("Day or weekday is -w is supplied alone.\n");
  printf STDERR ("\n");
  printf STDERR ("-w return Weekday 0-Sun 6-Sat\n");
  printf STDERR ("-D return Day of Month (01-31)\n");
  printf STDERR ("-M return Month Number (01-12)\n");
  printf STDERR ("-Y return Year (YYYY)\n"); 
  printf STDERR ("-u print this usage message on stderr and exit.\n");
  printf STDERR ("-U use UTC rather than local time to compute ");
  printf STDERR ("current date.\n");
  printf STDERR ("-n days - add days to result before outputting.\n");
  printf STDERR ("-p days - subtract days from result before outputting.\n");
  printf STDERR ("-s - remove spaces from output; MM DD YYYY --> MMDDYYYY.\n");
  printf STDERR ("-S sep - separate MM DD YYYY with sep (default space).\n"); 
  printf STDERR ("-x wday (wday = 0 - Sun; 6 - Sat.) skip past wday. ");
  printf STDERR ("Multiple\n");
  printf STDERR ("   -x arguments may to used. The skip is positive if ");
  printf STDERR ("using\n");
  printf STDERR ("   -n days (or no -n or -p argument) and negative if ");
  printf STDERR ("using\n");
  printf STDERR ("   -p days.\n");
  printf STDERR ("-h - skip past holidays as defined in %s for the\n",
                 HOLIDAY_FILE);
  printf STDERR ("     current year or in %s_yyyy for other years.\n",
                 HOLIDAY_FILE);
  printf STDERR ("-c - the supplied date is a calendar date. This is ");
  printf STDERR ("normally used\n");
  printf STDERR ("     in conjunction with the -S sep argument to identify ");
  printf STDERR ("the field\n");
  printf STDERR ("     separator. For example, -S \"-\" -c -y would accept\n");
  printf STDERR ("     \"2001-12-31\" as a calendar date and output a ");
  printf STDERR ("Julian Day\n");
  printf STDERR ("     just as if the normal three arguments, 2001 12 31, ");
  printf STDERR ("had been\n");
  printf STDERR ("     supplied. (Note the use of the -y argument to order ");
  printf STDERR ("the year,\n");
  printf STDERR ("     month, and day values.)\n");
  printf STDERR ("-i - on input, abbreviated case-insensitive month names\n");
  printf STDERR ("     (e.g. 'Jan','Feb','Mar') are recognized.\n");
  printf STDERR ("-o - On output, abbreviated month names and weekday ");
  printf STDERR ("names,\n");
  printf STDERR ("     (e.g. 'Sun','Mon','Tue') are used rather than ");
  printf STDERR ("numeric values.\n");
  printf STDERR ("-I - on input, full case-insensitive month names\n");
  printf STDERR ("     (e.g. 'January','February','March') are ");
  printf STDERR ("recognized.\n");
  printf STDERR ("-O - On output, full month names and weekday names\n");
  printf STDERR ("     (e.g. 'Sunday','Monday','Tuesday') ");
  printf STDERR ("are used rather than\n");
  printf STDERR ("     numeric values.\n");
  printf STDERR ("\n");
  printf STDERR ("     Native Language Support (NLS) is provided with the ");
  printf STDERR ("-i,-I,-o and\n");
  printf STDERR ("     -O options.\n");
  printf STDERR ("\f");
  printf STDERR ("\n");
  printf STDERR ("By default, year, month, and day arguments are input and\n");
  printf STDERR ("output in 'MM DD YYYY' order. This behavior can be ");
  printf STDERR ("changed\n");
  printf STDERR ("to DD MM YYYY with the '-e' argument or to 'YYYY MM DD' ");
  printf STDERR ("with\n");
  printf STDERR ("the '-y' argument.\n");
  printf STDERR ("\n");
  printf STDERR ("NOTE: Julian Days count sequentially from 4713 BCE; they ");
  printf STDERR ("are used by\n");
  printf STDERR ("      astronomers to simplify orbital calculations. ");
  printf STDERR ("Julian Days are actually\n");
  printf STDERR ("      floating point values that can represent days, ");
  printf STDERR ("hours, minutes, and\n");
  printf STDERR ("      seconds to any desired precision. They begin at ");
  printf STDERR ("noon UTC so that midnight\n");
  printf STDERR ("      is JD.500000. The values returned by this script ");
  printf STDERR ("are Truncated (integer)\n");
  printf STDERR ("      Julian Days and begin at midnight local time.\n"); 
  printf STDERR ("\n");
  printf STDERR ("Version: %s\n",VRSN);
  printf STDERR ("\f");
  examples();
  return(255);
} # usage 
# --------------------------------------------------------------------
sub Cal_Jdate
{
  use integer;
  my $mo = $_[0];
  my $da = $_[1];
  my $yr = $_[2];
  my $jd = $da - 32075 + (1461 * ($yr + 4800 + (($mo - 14)/12)) / 4) +
           ((367 * ($mo - 2 - (($mo - 14)/12) * 12))/12) -
           ((3 * (($yr + 4900 + (($mo - 14)/12))/100))/4);
  return($jd);
} # Cal_Jdate
sub Jdate_Cal
{
  use integer;
  my $jd = $_[0];
  my ($l,$n,$ll,$i,$j,$da,$mo,$yr);
  $l = $jd + 68569;
  $n = (4 * $l) / 146097;
  $ll = ((146097 * $n) + 3) / 4;
  $l -= $ll;
  $i = (4000 * ($l + 1)) / 1461001;
  $ll = (1461 * $i) / 4;
  $l  = $l - $ll + 31;
  $j = (80 * $l) / 2447;
  $da = $l - ((2447 * $j) / 80);
  $l = $j / 11;
  $mo = $j + 2 - (12 * $l);
  $yr = 100 * ($n - 49) + $i + $l;
  return($mo,$da,$yr);
} # Jdate_Cal
sub Wkday
{
  use integer;
  my $x = $_[0];
  return(($x + 1) % DAYS_PER_WK);
} # Wkday
sub Today
{
  use integer;
  my ($mo,$da,$yr) = (localtime)[4,3,5];
  $yr += 1900; # years returned by localtime() are years since 1900
  $mo++;       # months returned by localtime range from 0 to 11
  return($mo,$da,$yr)
} # Today
sub GM_Today
{
  use integer;
  my ($mo,$da,$yr) = (gmtime)[4,3,5];
  $yr += 1900; # years returned by gmtime() are years since 1900
  $mo++;       # months returned by localtime range from 0 to 11
  return($mo,$da,$yr)
} # GM_Today
sub Is_Holiday
{
  use integer;
  my $jdate_in = $_[0];
  my $utc = $_[1];
  my $is_hol = FALSE; 
  my $curr_yr = 0;
  my $fname = '';
  my ($dummy_mo,$dummy_da,$yr) = Jdate_Cal($jdate_in);
  # Get julian day of 1st day of $yr
  my $day1 = Cal_Jdate(1,1,$yr);
  my $target_day = $jdate_in - $day1 + 1;
  ($dummy_mo,$dummy_da,$curr_yr) = ($utc) ? GM_Today() : Today();
  if ($yr == $curr_yr)
    {
      $fname = HOLIDAY_FILE . sprintf("_%04d",$yr); 
      if (!(-e $fname && -r $fname && -f $fname))
        {
          $fname = HOLIDAY_FILE;
        }
    }
  else
    {
      $fname = HOLIDAY_FILE . sprintf("_%04d",$yr); 
    }
  if (-e $fname && -r $fname && -f $fname)
    {
      if (open(F1,"< $fname"))
        {
          my @holidays;
          my $s_tmp = '';
          while ($s_tmp = <F1>)
            {
              chomp($s_tmp);
              if (!($s_tmp =~ /^\*/))
                {
                  my @tmp_array = split(/\s+/,$s_tmp);
                  # strip off blank entries
                  my $cont = TRUE;
                  while ($#tmp_array >= 0 && $cont)
                    {
                      if (length($tmp_array[0]) < 1) 
                        {
                          splice(@tmp_array,0,1);
                        }
                      else
                        {
                          $cont = FALSE;
                        }
                    } # while
                  if ($#tmp_array >= 0)
                    {
                      if ($tmp_array[0] =~ /\d+$/)
                        {   
                          push(@holidays,$tmp_array[0] + 0);
                        }
                    }
                }
            } # while
          close F1;
          if ($#holidays > 0)
            {
              if ($yr == $holidays[0])
                {
                  my $i = 1;
                  while ($i <= $#holidays && !($is_hol))
                    {
                      $is_hol = ($target_day == $holidays[$i]);
                      ++$i;
                    } # while                 
                }
              else
                {
                  $s_tmp = sprintf(
                        "File %s year %d does not match expected year %d",
                                   $fname,$holidays[0],$yr);
                  problem($s_tmp,-2);
                }             
            }
        }
      else
        {
          problem(sprintf("Can't read holidays file '%s'",$fname),-1);
        }
    } 
  return($is_hol); 
} # Is_Holiday
sub Skip_Days
{
  use integer;
  my $jdate = $_[0];
  my $day_offset = $_[1];
  my $wdaysref = $_[2];
  my $holiday_flag = $_[3];
  my $utc = $_[4];    
  my $wdays_knt = $#$wdaysref + 1;
  if ($wdays_knt > 0 || $holiday_flag)
    {
      my $increment = ($day_offset >= 0) ? 1 : -1;
      my $fnd = TRUE;
      until (!($fnd)) 
        {
          $fnd = FALSE;
          if ($wdays_knt > 0)
            {
              my $weekday = Wkday($jdate);
              my $i = 0;
              while ($i < $wdays_knt && !($fnd))
                {
                  $fnd = ($weekday == $$wdaysref[$i]);
                  ++$i;
                } # while
            }
          if (!($fnd) && $holiday_flag)
            {
              $fnd = Is_Holiday($jdate,$utc);
            }
          if ($fnd)
            {
              $jdate += $increment; 
            }
        } # until
    }
  return($jdate);
} # Skip_Days
sub load_locale_lc_time
{
  use integer;
  # default values; load these and then try to replace with 'locale LC_TIME'
  # equivalents, but don't crash or complain if locale not found
  my @tmp_lc_time_array =
    (
      ["Sun","Mon","Tue","Wed","Thu","Fri","Sat",""],
      ["Sunday","Monday","Tuesday","Wednesday","Thursday",
       "Friday","Saturday",""],
      ["Jan","Feb","Mar","Apr","May","Jun","Jul",
       "Aug","Sep","Oct","Nov","Dec",""],
      ["January","February","March","April","May","June","July",
       "August","September","October","November","December",""],
    );
  # Now attempt to load the locale LC_TIME for NLS
  eval
    {
       # so Windows is ok
       my $null_dev = ($OSNAME =~ /MSW.*/i) ? 'NUL' : '/dev/null';
       my $pipe_cmd = sprintf("locale LC_TIME 2>%s |",$null_dev);
       my $knt = 0;
       open F2,$pipe_cmd or die "Locale LC_TIME Load Failed";
       my @tmp_arry1 = <F2>;
       close F2;
       chomp @tmp_arry1;
       while ($knt <= $#tmp_arry1 && $knt <= MONTH_FULL)
         {
           my @tmp_arry2 = split /;/,$tmp_arry1[$knt];
           foreach my $tmp_element (@tmp_arry2)
             {
               $tmp_element =~ s/^"//;
               $tmp_element =~ s/"$//;
             }
           push(@tmp_arry2,""); # for null entry; flag
           splice(@tmp_lc_time_array,$knt,1,\@tmp_arry2);
           ++$knt;
         }
     };
   return(@tmp_lc_time_array);
} # load_locale_lc_time
sub find_lc_time
{
  use integer;
  my $mode = $_[0];
  my $to_number = $_[1];
  my $key = $_[2];
  my $cc = 0;
  my $s_out = "";
  if ($#lc_time_array >= 0)
    {
      if (($mode >= DAY_ABREV) && ($mode <= MONTH_FULL))
        {
          my $fnd = FALSE;
          my $i = 0;
          if ($to_number)
            {
              my $lc_key = lc($key); 
              while ((length($lc_time_array[$mode][$i]) > 0) && (!($fnd)))
                {
                  if ($lc_key eq lc($lc_time_array[$mode][$i]))
                    {
                      $fnd = TRUE;
                    }
                  else
                    {
                      ++$i;
                    }
                }
              if ($fnd)
                {
                  $s_out = sprintf("%02d",$i + 1);
                }
              else
                {
                  $cc = 1;
                }
            }
          else
            {
              while (length($lc_time_array[$mode][$i]) > 0)
                {
                  ++$i;
                }
              if (($key >= 1) && ($key <= $i))
                {
                  $s_out = $lc_time_array[$mode][$key - 1];
                }
              else
                {
                  $cc = 1;
                }
            }
        }
      else
        {
          $cc = 255;
        }
    }
  else
    {
      $cc = 235;
    }
  return($cc,$s_out);
} # find_lc_time
# ------------------------------------------------------------------
# ---------------- Main Program ------------------------------------
# ------------------------------------------------------------------
use integer;
my @wdays;
my $i = 0;
my $cc = 0;
# First strip off the possible multiple -x or -X args and load @wdays
#
while ($i <= $#ARGV && $cc == 0)
  {
    if (lc($ARGV[$i]) eq '-x')
      {
        if ($i < $#ARGV)
          {
            if ($ARGV[$i + 1] =~ /^[0-6]$/)
              {
                push(@wdays,$ARGV[$i + 1] + 0);
                splice(@ARGV,$i,2);
              }
            else
              {
                $cc = 253;
                problem(sprintf("Illegal -x value '%s' must be in range 0-6",
                                $ARGV[$i + 1]),$cc);
              }
          }
        else
          {
            $cc = 246;
            problem("Missing argument after -x",$cc);
          }
      }
    else
      {
        ++$i;
      }
  } # while
if (($cc == 0) && ($#wdays >= (DAYS_PER_WK - 1)))
  {
    $cc = 239;
    problem(sprintf("No more than %d -x args allowed.",DAYS_PER_WK - 1),$cc);
  }
if ($cc != 0)
  {
    usage();
    exit($cc);
  }
# Now process the rest of the args using getopts()
# ---------------------------------------------------
my $dmy_mode = DMY_NONE;
my $dmy_knt = 0;
my $wkday = FALSE;
my $utc_arg = FALSE;
my $mdy_mode = MDY_MDY;
my $mdy_arg_knt = 0;
my $cal_mode = FALSE;
my $in_words_knt = 0;
my $out_words_knt = 0;
my $sep = ' ';
my $space_knt = 0;
my $day_offset = 0;
my $day_arg_knt = 0;
my $hol = FALSE;
our ($opt_D,$opt_M,$opt_Y,$opt_w,$opt_W,$opt_u,$opt_U,$opt_s,$opt_e,$opt_y,
     $opt_n,$opt_N,$opt_p,$opt_P,$opt_h,$opt_H,$opt_S,$opt_c,$opt_i,$opt_I,
     $opt_o,$opt_O);
if (!getopts('DMYwWuUciIoOseyn:N:p:P:hHS:'))
  {
    $cc = 252;
    usage();
    exit($cc);
  }
if (defined($opt_D))
  {
    $dmy_mode = DMY_DAY;
    ++$dmy_knt;
  }
if (defined($opt_M))
  {
    $dmy_mode = DMY_MONTH;
    ++$dmy_knt;
  }
if (defined($opt_Y))
  {
    $dmy_mode = DMY_YEAR;
    ++$dmy_knt;
  }
if (defined($opt_w) || defined($opt_W))
  {
    $wkday = TRUE;
  }
if (defined($opt_u))
  {
    $cc = 251;
  }
if (defined($opt_U))
  {
    $utc_arg = TRUE;
  }
if (defined($opt_c))
  {
    $cal_mode = TRUE;
  }
if (defined($opt_i))
  {
    $in_words = WORDS_ABREV;
    ++$in_words_knt;
  }
if (defined($opt_I))
  {
    $in_words = WORDS_FULL;
    ++$in_words_knt;
  }
if (defined($opt_o))
  {
    $out_words = WORDS_ABREV;
    ++$out_words_knt;
  }
if (defined($opt_O))
  {
    $out_words = WORDS_FULL;
    ++$out_words_knt;
  }
if (defined($opt_s))
  {
    $sep = '';
    ++$space_knt;
  }
if (defined($opt_e))
  {
    $mdy_mode = MDY_DMY;
    ++$mdy_arg_knt;
  }
if (defined($opt_y))
  {
    $mdy_mode = MDY_YMD;
    ++$mdy_arg_knt;
  }
if (defined($opt_n))
  {
    $day_offset = $opt_n;
    ++$day_arg_knt;
  }
if (defined($opt_N))
  {
    $day_offset = $opt_N;
    ++$day_arg_knt;
  }
if (defined($opt_p))
  {
    $day_offset = $opt_p * -1;
    ++$day_arg_knt;
  }
if (defined($opt_P))
  {
    $day_offset = $opt_P * -1;
    ++$day_arg_knt;
  }
if (defined($opt_h) || defined($opt_H))
  {
    $hol = TRUE;
  }
if (defined($opt_S))
  {
    $sep = $opt_S;
    ++$space_knt;
  }
# Now check for errors
#
if ($day_arg_knt > 1 && $cc == 0)
  {
    $cc = 255;
    problem("Only one -n or -p argument allowed",$cc);
  }
if (($dmy_knt + $wkday) > 1 && $cc == 0)
  {
    $cc = 245;
    problem("Only one -w, -D, -M, or -Y argument allowed",$cc);
  }
if ($mdy_arg_knt > 1 && $cc == 0)
  {
    $cc = 254;
    problem("Only one -y or -e argument allowed",$cc);
  }
if ($space_knt > 1 && $cc == 0)
  {
    $cc = 247;
    problem("Only one -s or -S argument allowed",$cc);
  }
if ($in_words_knt > 1 && $cc == 0)
  {
    $cc = 236;
    problem("Only one -i or -I argument allowed",$cc);
  }
if ($out_words_knt > 1 && $cc == 0)
  {
    $cc = 235;
    problem("Only one -o or -O argument allowed",$cc);
  }
if ($cc != 0)
  {
    usage();
    exit($cc);
  }
# if $in_words 0r $out_words then setup lc_time_array
#
if (($in_words) || ($out_words))
  {
    @lc_time_array = load_locale_lc_time();
  }
# -------------- Finally ready to start work --------------------
# ---------------------------------------------------------------
my $knt = $#ARGV + 1;
my $s_out = '';
# test for -c processing
if ($cal_mode && length($sep) > 0)
  {
    if ($knt == 1 && $sep =~ /^\S+$/)
      {
        my @tmp_array0 = split($sep,$ARGV[0]);
        if ($#tmp_array0 == 2)
          {
            if ($in_words)
              {
                my $tmp_cc = 0;
                my $tmp_val = '';
                my $sel_mode = ($in_words == WORDS_ABREV) ? MONTH_ABREV :
                  MONTH_FULL;
                my $tmp_ndx = ($mdy_mode == MDY_DMY || $mdy_mode == MDY_YMD)
                  ? 1 : 0; 
                ($tmp_cc,$tmp_val) = find_lc_time($sel_mode,TRUE,
                                                  $tmp_array0[$tmp_ndx]);
                if ($tmp_cc == 0)
                  {
                    $tmp_array0[$tmp_ndx] = $tmp_val;
                  }
                else
                  {
                    problem("Could not convert month name",$cc);
                    exit(237);
                  }
              }
            @ARGV = @tmp_array0;
            $knt = 3;
          }
        else
          {
            $cc = 238;
            problem(sprintf("-c argument expected 3 parts; got %d",
                            $#tmp_array0 + 1),$cc);
            exit($cc);
          } 
      } 
  } 
SWITCH_0:
  {
    if ($knt == 0)
      {
        my ($mo,$day,$yr) = ($utc_arg) ? GM_Today() : Today();  
        my $jdate = Cal_Jdate($mo,$day,$yr) + $day_offset; 
        $jdate = Skip_Days($jdate,$day_offset,\@wdays,$hol,$utc_arg);
        if ($wkday || $dmy_mode != DMY_NONE)
          {
            if ($wkday) 
              {
                $s_out = sprintf("%d",Wkday($jdate));
                if ($out_words)
                  {
                    my $tmp_cc = 0;
                    my $tmp_val = '';
                    my $sel_mode = ($out_words == WORDS_ABREV) ? DAY_ABREV :
                      DAY_FULL;
                    ($tmp_cc,$tmp_val) = find_lc_time($sel_mode,FALSE,
                                                      Wkday($jdate) + 1);
                    
                    if ($tmp_cc == 0)
                      {
                        $s_out = $tmp_val;
                      }     
                  }
              }
            else
              {
                ($mo,$day,$yr) = Jdate_Cal($jdate);
                SWITCH_01:
                  {
                    if ($dmy_mode == DMY_MONTH)
                      {
                        $s_out = sprintf("%02d",$mo % (MAX_MONTH + 1));
                        if ($out_words)
                          {
                            my $tmp_cc = 0;
                            my $tmp_val = '';
                            my $sel_mode = ($out_words == WORDS_ABREV) ?
                              MONTH_ABREV : MONTH_FULL;
                            ($tmp_cc,$tmp_val) = find_lc_time($sel_mode,FALSE,
                                                              $mo);
                            if ($tmp_cc == 0)
                              {
                                $s_out = $tmp_val;
                              }     
                          }
                        last SWITCH_01;
                      };  
                    if ($dmy_mode == DMY_YEAR)
                      {
                        $s_out = sprintf("%04d",$yr);
                        last SWITCH_01;
                      };
                    $s_out = sprintf("%02d",$day % (MAX_DAY + 1));
                  }; # SWITCH_01
              }
          }
        else
          {
            $s_out = sprintf("%d",$jdate);
          }
        last SWITCH_0;
      } # 0 args
    if ($knt == 1)
      {
        my $jdate = 0;
        if ($ARGV[0] =~ /^\d+$/)
          {
            $jdate = $ARGV[0] + $day_offset;
            $jdate = Skip_Days($jdate,$day_offset,\@wdays,$hol,FALSE);
          }
        else
          {
            $cc = 244;
            problem(sprintf("Invalid numeriic argument '%s'",$ARGV[0]),$cc);
          }
        if ($cc == 0)
          {
            if ($wkday || $dmy_mode != DMY_NONE)
              {
                if ($wkday) 
                  {
                    $s_out = sprintf("%d",Wkday($jdate));
                    if ($out_words)
                      {
                        my $tmp_cc = 0;
                        my $tmp_val = '';
                        my $sel_mode = ($out_words == WORDS_ABREV) ?
                          DAY_ABREV : DAY_FULL;
                        ($tmp_cc,$tmp_val) = find_lc_time($sel_mode,FALSE,
                                                          Wkday($jdate) + 1);
                        if ($tmp_cc == 0)
                          {
                            $s_out = $tmp_val;
                          }     
                      }
                  }
                else
                  {
                    my ($mo,$day,$yr) = Jdate_Cal($jdate);
                    SWITCH_11:
                      {
                        if ($dmy_mode == DMY_MONTH)
                          {
                            $s_out = sprintf("%02d",$mo % (MAX_MONTH + 1));
                            if ($out_words)
                              {
                                my $tmp_cc = 0;
                                my $tmp_val = '';
                                my $sel_mode = ($out_words == WORDS_ABREV) ?
                                  MONTH_ABREV : MONTH_FULL;
                                ($tmp_cc,$tmp_val) = find_lc_time($sel_mode,
                                                                  FALSE,
                                                                  $mo);
                                if ($tmp_cc == 0)
                                  {
                                    $s_out = $tmp_val;
                                  }     
                              }
                            last SWITCH_11;
                          };  
                        if ($dmy_mode == DMY_YEAR)
                          {
                            $s_out = sprintf("%04d",$yr);
                            last SWITCH_11;
                          };
                        $s_out = sprintf("%02d",$day % (MAX_DAY + 1));
                      }; # SWITCH_11
                  }
              }
            else
              {
                my ($mo,$day,$yr) = Jdate_Cal($jdate);
                my $s_mo = sprintf("%02d",$mo % (MAX_MONTH + 1));
                if ($out_words)
                  {
                    my $tmp_cc = 0;
                    my $tmp_val = '';
                    my $sel_mode = ($out_words == WORDS_ABREV) ?
                      MONTH_ABREV : MONTH_FULL;
                    ($tmp_cc,$tmp_val) = find_lc_time($sel_mode,FALSE,$mo);
                    if ($tmp_cc == 0)
                      {
                        $s_mo = $tmp_val;
                      }
                  }
                SWITCH_12:
                   {
                     if ($mdy_mode == MDY_YMD)
                       {
                         $s_out = sprintf("%04d%s%s%s%02d",$yr,$sep,
                                          $s_mo,$sep,
                                          $day % (MAX_DAY + 1));
                         last SWITCH_12;
                       }
                     if ($mdy_mode == MDY_DMY)
                       {
                         $s_out = sprintf("%02d%s%s%s%04d",
                                          $day % (MAX_DAY + 1),$sep,
                                          $s_mo,$sep,$yr);
                         last SWITCH_12;
                       }
                     $s_out = sprintf("%s%s%02d%s%04d",
                                      $s_mo,$sep,$day % (MAX_DAY + 1),
                                      $sep,$yr);
                   }; # SWITCH_12
              }
          }
        last SWITCH_0;
      } # 1 arg
    if ($knt == 3)
      {
        my $i = 0;
        my $mo_ndx = ($mdy_mode == MDY_DMY || $mdy_mode == MDY_YMD)
          ? 1 : 0;
        while (($i < $knt) && ($cc == 0))
          {
            if (($ARGV[$i] =~ /^-*\d+$/) ||
                (($i == $mo_ndx) && ($in_words)))
              {
                ++$i;
              }
            else
              {
                $cc = 243; 
                problem(sprintf("Arg %d '%s' invalid numeric value",
                                $i + 1,$ARGV[$i]),
                        $cc);
              }
          } # while
        if ($cc == 0)
          {
            my $tmp_cc = -1;
            my $tmp_val = '';
            my ($day,$yr);
            if ($in_words && !($cal_mode))
              {
                my $sel_mode = ($in_words == WORDS_ABREV) ? MONTH_ABREV :
                  MONTH_FULL;
                ($tmp_cc,$tmp_val) = find_lc_time($sel_mode,TRUE,
                                                  $ARGV[$mo_ndx]);
              }
            my $mo = ($tmp_cc == 0) ? $tmp_val + 0 : $ARGV[$mo_ndx] + 0; 
            SWITCH_31:
              {
                if ($mdy_mode == MDY_YMD)
                  {
                    $yr = $ARGV[0] + 0;
                    $day = $ARGV[2] + 0;
                    last SWITCH_31;
                  }
                if ($mdy_mode == MDY_DMY)
                  {
                    $day = $ARGV[0] + 0;
                    $yr = $ARGV[2] + 0;
                    last SWITCH_31;
                  }
                $day = $ARGV[1] + 0;
                $yr = $ARGV[2] + 0;
              }; # SWITCH_31
            if ($mo < 1 || $mo > MAX_MONTH)
              {
                $cc = 242;
                problem(sprintf("Month %d is out of range 1-%d",
                                $mo,MAX_MONTH),$cc);
              }
            if (($day < 1 || $day > MAX_DAY) && $cc == 0)
              {
                $cc = 241;
                problem(sprintf("Day %d is out of range 1-%d",
                                $day,MAX_DAY),$cc);
              }
            if ($cc == 0)
              {
                my $jdate = Cal_Jdate($mo,$day,$yr) + $day_offset;
                $jdate = Skip_Days($jdate,$day_offset,\@wdays,$hol,FALSE);
                if ($wkday || $dmy_mode != DMY_NONE)
                  {
                    if ($wkday) 
                      {
                        $s_out = sprintf("%d",Wkday($jdate));
                        if ($out_words)
                          {
                            $tmp_cc = 0;
                            $tmp_val = '';
                            my $sel_mode = ($out_words == WORDS_ABREV) ?
                              DAY_ABREV : DAY_FULL;
                            ($tmp_cc,$tmp_val) =
                               find_lc_time($sel_mode,FALSE,
                                            Wkday($jdate) + 1);
                            if ($tmp_cc == 0)
                              {
                                $s_out = $tmp_val;
                              }     
                          }
                      }
                    else
                      {
                        ($mo,$day,$yr) = Jdate_Cal($jdate);
                        SWITCH_32:
                          {
                            if ($dmy_mode == DMY_MONTH)
                              {
                                $s_out = sprintf("%02d",$mo % (MAX_MONTH + 1));
                                if ($out_words)
                                  {
                                    my $tmp_cc = 0;
                                    my $tmp_val = '';
                                    my $sel_mode = ($out_words == WORDS_ABREV)
                                      ? MONTH_ABREV : MONTH_FULL;
                                    ($tmp_cc,$tmp_val) =
                                       find_lc_time($sel_mode,FALSE,$mo);
                                    if ($tmp_cc == 0)
                                      {
                                        $s_out = $tmp_val;
                                      }     
                                  }
                                last SWITCH_32;
                              };  
                            if ($dmy_mode == DMY_YEAR)
                              {
                                $s_out = sprintf("%04d",$yr);
                                last SWITCH_32;
                              };
                            $s_out = sprintf("%02d",$day % (MAX_DAY + 1));
                          };
                      }
                  }
                else
                  {
                    $s_out = sprintf("%d",$jdate);
                  }
              }
          }                        
        last SWITCH_0;
      } # 3 args
#
# Invalid number of arguments; fell through case
    $cc = 240;
    problem(sprintf("Invalid number of arguments %d",$knt),$cc);
  }; # SWITCH_0
if ($cc == 0)
  {
    printf("%s\n",$s_out);
  }
exit($cc);
