A dateplus AWK program is also available.
static char SccsId[] = "@(#)dateplus.c 2.3 03/19/10";
/*--------------------------------------------------------------------*/
/* dateplus.c */
/*--------------------------------------------------------------------*/
/* */
/* Copyright (c) 1995-2010 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. */
/* */
/* ------------------------------------------------------------------ */
/* Program documentation and notes located in the */
/* show_documentation() function. */
/*--------------------------------------------------------------------*/
/*====================================================================*/
/* D E C L A R A T I O N S */
/*====================================================================*/
#include <stdio.h>
#include <stdlib.h> /* abs, atoi, exit */
#include <string.h> /* strlen, strncpy */
#include <ctype.h> /* isdigit */
#include <time.h> /* time routines */
#define H24SSS 86400 /* Number of seconds in a day */
#define FALSE 0
#define TRUE 1
#define STRNCPY(to, from, n) {strncpy(to, from, n);to[n] = 0x0;}
/*====================================================================*/
/* P R O T O T Y P E S */
/*====================================================================*/
long calc_basedate(int yyyy, int mm, int dd);
int leap_year(int yyyy);
void show_documentation(char *progname);
int this_years_days(int yyyy);
void usage(char *progname);
void validate_mm_dd(int mm, int dd);
/*====================================================================*/
/* G L O B A L V A R I A B L E S */
/*====================================================================*/
int yyyy;
int ddddd;
const char *months[] = /* Month names */
{
"January", "February", "March" ,
"April" , "May" , "June" ,
"July" , "August" , "September",
"October", "November", "December"
};
/*====================================================================*/
/* M A I N */
/*====================================================================*/
main(int argc, char *argv[])
{
int julian_days[] = {0,31,59,90,120,151,181,212,243,273,304,334,365};
int julian_leap[] = {0,31,60,91,121,152,182,213,244,274,305,335,366};
int *accum_days = &julian_days[0]; /* julian_days address to ptr */
/*--------------------------------------------------------------------*/
/* Variables for weekday calculations */
/* */
const char *weekdays_abbr[]
= {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
const char *weekdays_long[]
= {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
int weekday_wanted = FALSE; /* Initially false. */
int weekday_abbr_wanted = FALSE; /* '' '' */
int weekday_long_wanted = FALSE; /* '' '' */
/* */
/*--------------------------------------------------------------------*/
int i, n; /* Counters */
int mm;
int dd;
int since_1970 = 0; /* Option flag */
int Since_yyyymmdd = 0; /* '' '' */
int since_yyyymmdd = 0; /* '' '' */
int basedate = 0;
long ddddd = 0;
long ddddd_yyyy = 0;
int iterations = 1;
int yyyymmdd_to_jjj = 0;
int yyyyjjj_to_yyyymmdd = 0;
int adj_days = 0;
int abs_adj = 0;
int valid_opt = FALSE; /* Pessimistic by nature I am. */
char yyyymmdd[9];
char four_char[5];
char two_char[3];
char opt = 0x0; /* Command line option */
char *p_argv = argv[0]; /* Instantiate pointer to argv[0] */
char progname[64] = {0x0};
/*---------------------------------------------------------------*/
/* Begin by placing today's date into yyyymmdd string variable. */
/*---------------------------------------------------------------*/
struct tm *time_now;
time_t secs_now;
time(&secs_now); /* Convert calendar time to local */
time_now = localtime(&secs_now);
strftime(yyyymmdd,9,"%Y""%m""%d",time_now);
/*-------------------------------------------------*/
/* Parse today's date and calc its ddddd value. */
/*-------------------------------------------------*/
STRNCPY(four_char, yyyymmdd, 4); yyyy = atoi(four_char);
STRNCPY(two_char, (&yyyymmdd[0]+4), 2); mm = atoi(two_char );
STRNCPY(two_char, (&yyyymmdd[0]+6), 2); dd = atoi(two_char );
ddddd = calc_basedate(yyyy, mm, dd);
/*---------------------------------------------------------*/
/* Lowercase argv[0] and assign the basename to progname. */
/*---------------------------------------------------------*/
for (n=i=0; *(p_argv+n); *(p_argv+n)=tolower(*(p_argv+n)), n++)
if ((*(p_argv+n) == '/') || (*(p_argv+n) == '\\'))
i = n + 1;
strcpy(progname,argv[0]+i); /* Portion that follows last / (or \). */
/*-----------------------------------------------------------------*/
/* If user supplies no args (argc == 1) then show usage and exit. */
/* Else, see if they gave us options. If so, process accordingly. */
/*-----------------------------------------------------------------*/
if (argc == 1) /* This zero-based numbering drives me nuts! */
{
usage(progname);
exit(7);
}
/*-------------------------------------*/
/* If we have iterations, validate'em. */
/*-------------------------------------*/
if (argc > 3)
{
p_argv = argv[3]; /* Instantiate pointer to argv[3] */
for (n=0; *(p_argv+n); n++)
{
if (isdigit(*(p_argv+n)))
continue; /* A-OK */
fprintf(stderr,"\nIterations (%s) invalid: must be integer.\n",
argv[3]);
fprintf(stderr,"%s terminated.\a\n", progname);
exit(8);
}
iterations = atoi(argv[3]);
}
/*------------------------------------------------------------*/
/* If the first argument's leading character is a hyphen */
/* (indicating potential option), then see is it is, indeed */
/* an option, or simply a negative adjustment value. */
/*------------------------------------------------------------*/
if ((opt = *argv[1]) == '-')
{
switch (opt = *(argv[1]+1))
{
case 'h': /* User wants short help */
usage(progname);
exit(9);
break;
case 'H': /* User wants detailed help */
show_documentation(progname);
exit(10);
break;
case 'y': /* User wants yesterday's date */
if (argc > 2)
{
fprintf(stderr,"No other arguments allowed with -y "
"(yesterday) option.\n",argv[1]);
fprintf(stderr,"%s terminated.\a\n", progname);
exit(11);
}
secs_now -= H24SSS;
time_now = localtime(&secs_now);
strftime(yyyymmdd, 9, "%Y""%m""%d", time_now);
printf("%s\n", yyyymmdd);
exit(0);
break;
case 't': /* User wants tomorrow's date */
if (argc > 2)
{
fprintf(stderr,"No other arguments allowed with -t "
"(tomorrow) option.\n");
fprintf(stderr,"%s terminated.\a\n", progname);
exit(12);
}
secs_now += H24SSS;
time_now = localtime(&secs_now);
strftime(yyyymmdd, 9, "%Y""%m""%d", time_now);
printf("%s\n", yyyymmdd);
exit(0);
break;
case 'S': /* Days since date (unsigned) */
since_1970 = 0;
Since_yyyymmdd = 1; /* Capitalized variable name */
basedate = 0;
valid_opt = TRUE;
break;
case 's': /* Days since date */
since_1970 = 0;
since_yyyymmdd = 1;
basedate = 0;
valid_opt = TRUE;
break;
case 'j': /* Gregorian to Julian */
if (argc > 2)
{
if (strlen(argv[2]) != 8)
{
fprintf(stderr,
"Date (%s) must be in the form: yyyymmdd\n",
argv[2]);
fprintf(stderr,"%s terminated.\a\n", progname);
exit(13);
}
strcpy(yyyymmdd,argv[2]);
}
yyyymmdd_to_jjj = TRUE;
iterations = 1; /* Not used with Julian */
adj_days = 0;
valid_opt = TRUE;
break;
case 'J': /* Julian to Gregorian with yyyy */
if (argc < 3 || strlen(argv[2]) != 7)
{
fprintf(stderr,
"Julian-to-Gregorian conversion requires "
"yyyyJJJ julian date.\n");
fprintf(stderr,"%s terminated.\a\n", progname);
exit(14);
}
strcpy(yyyymmdd,argv[2]);
STRNCPY( four_char, (&yyyymmdd[0]+4), 3);
ddddd = atoi(four_char);
STRNCPY( four_char, yyyymmdd, 4);
yyyy = atoi(four_char);
if (leap_year(yyyy))
accum_days = &julian_leap[0];
/*-----------------------------------------------------*/
/* Rip through accum_days until we find one that is */
/* too big. Then use the preceding one as the correct */
/* month number and decrement ddddd by the month's */
/* julian days. */
/*-----------------------------------------------------*/
for (mm=1; mm<=12; mm++)
{
if ((ddddd - accum_days[mm]) <= 0)
{
ddddd -= accum_days[mm-1];
break;
}
}
printf("%04d%02d%02d\n", yyyy,mm,ddddd);
exit(0);
break;
case 'w': /* Weekday option (returns weekday abbreviation) */
weekday_wanted = TRUE; /* Was false, now true */
weekday_abbr_wanted = TRUE; /* Was false, now true */
iterations = 1; /* Not used with Weekday */
adj_days = 0;
valid_opt = TRUE;
break;
case 'W': /* Weekday option (returns weekday) */
weekday_wanted = TRUE; /* Was false, now true */
weekday_long_wanted = TRUE; /* Was false, now true */
iterations = 1; /* Not used with Weekday */
adj_days = 0;
valid_opt = TRUE;
break;
case 'b': /* Basedate from Jan. 1, 0001 */
since_1970 = 0;
since_yyyymmdd = 0;
basedate = 1;
valid_opt = TRUE;
break;
case 'u': /* Basedate from Jan. 1, 1970 */
since_1970 = 1;
since_yyyymmdd = 0;
basedate = 0;
valid_opt = TRUE;
break;
default:
if (isdigit(opt))
{
valid_opt = TRUE;
/*--------------------------------------------------*/
/* Ensure that adjustment days are, indeed, numeric */
/* numeric (before the atoi). */
/*--------------------------------------------------*/
p_argv = argv[1]; /* Pointer to argv[1] */
for (n=0; *(p_argv+n); n++)
{
if (isdigit(*(p_argv+n)))
continue; /* A-OK */
if (n==0 && (*(p_argv+n)=='-' || *(p_argv+n)=='+'))
continue; /* A-OK */
fprintf(stderr,
"\nAdj days (%s) NOT [+-]numeric.\n", argv[0]);
fprintf(stderr,"%s terminated.\a\n", progname);
exit(15);
}
/*--------------------------------------------------*/
/* Now, convert 1st arg (now argv[0]) to adj days. */
/*--------------------------------------------------*/
adj_days = atoi(argv[1]);
}
break;
} /* switch (opt = *(argv[1]+1)) */
} /* if ((opt = *argv[1]) == '-') */
else if (isdigit(opt) || opt == '+')
{
p_argv = argv[1]; /* Instantiate pointer to argv[1] */
for (n=0; *(p_argv+n); n++)
{
if (isdigit(*(p_argv+n)))
continue; /* A-OK */
if (n==0 && *(p_argv+n)=='+')
continue; /* A-OK */
fprintf(stderr,"\nAdjust-days (%s) NOT numeric!\n", argv[1]);
fprintf(stderr,"%s terminated.\a\n", progname);
exit(16);
}
adj_days = atoi(argv[1]);
valid_opt = TRUE;
} /* else if (isdigit(opt) || opt == '+') */
/*---------------------------------------*/
/* Anything else is an invalid argument. */
/*---------------------------------------*/
if (valid_opt != TRUE)
{
printf("%s: Invalid argument '%s'!\a\n\n", argv[0], argv[1]);
usage(progname);
exit(17);
}
/*-----------------------------------------------------------------*/
/* If we have a yyyymmdd date argument, then do a basic validation */
/* (must be 8 digits). If it looks OK, assign it to yyyymmdd. */
/*-----------------------------------------------------------------*/
if (argc > 2)
{
if (strlen(argv[2]) != 8)
{
fprintf(stderr,
"Date (%s) must be in the form: yyyymmdd!\n",
argv[2]);
fprintf(stderr,"%s terminated.\a\n", argv[0]);
exit(18);
}
/*------------------------------------------------------------*/
/* Using a pointer to 1st character in argv[2], scan the arg */
/* (character-by-character) confirming that each is a digit. */
/* If any nonnumeric value, then fuss at the user and exit. */
/*------------------------------------------------------------*/
p_argv = argv[2]; /* Pointer to argv[2] */
for (n=0; *(p_argv+n); n++)
{
if (isdigit(*(p_argv+n)))
continue; /* A-OK */
fprintf(stderr,"\nDate (%s) NOT numeric!\n", argv[0]);
fprintf(stderr,"%s terminated.\a\n", argv[0]);
exit(19);
}
strcpy(yyyymmdd,argv[2]); /* OK, assign argv[2] to yyyymmdd */
} /* if (argc > 2) */
/*----------------------------------------------------------------*/
/* Are we returning days since a date or base dates? */
/*----------------------------------------------------------------*/
if (since_yyyymmdd || since_1970 || argc > 2)
{
STRNCPY(four_char, yyyymmdd, 4); yyyy = atoi(four_char);
STRNCPY(two_char, (&yyyymmdd[0]+4), 2); mm = atoi(two_char );
STRNCPY(two_char, (&yyyymmdd[0]+6), 2); dd = atoi(two_char );
validate_mm_dd(mm, dd);
}
if (since_yyyymmdd || Since_yyyymmdd) /* -S|s options */
{
ddddd_yyyy = calc_basedate(yyyy, mm, dd);
ddddd -= ddddd_yyyy;
printf("%d\n", ((Since_yyyymmdd) ? labs(ddddd) : ddddd));
exit(0);
}
else if (since_1970) /* -u option */
{
ddddd = calc_basedate(yyyy, mm, dd);
if (yyyy >= 1970)
{
ddddd_yyyy = calc_basedate(1970, 01, 01);
ddddd -= ddddd_yyyy;
printf("%d\n", ddddd);
exit(0);
}
else
{
fprintf(stderr, "This is a trick question, right?\n"
"The -u option with a date older than Jan. 1, 1970,\n"
"and you expect me to give you the basedate! :-))\n"
"%s terminated.\n", progname);
exit(20);
}
} /*--------------------------------------------*/
else if (basedate) /* Calculate basedate for the date specified. */
{ /*--------------------------------------------*/
ddddd = calc_basedate(yyyy, mm, dd);
printf("%d\n", ddddd);
exit(0);
}
/*-------------------------------------------------------------*/
/* No option passed, only adj-days and (optionally) yyyymmdd. */
/*-------------------------------------------------------------*/
if (argc == 1 && adj_days != 0)
{
secs_now += (adj_days * H24SSS);
time_now = localtime(&secs_now);
strftime(yyyymmdd, 9, "%Y""%m""%d", time_now);
printf("%s\n", yyyymmdd);
exit(0);
}
/*---------------------------------------------------------------*/
/* Arriving here means it was none of the above (single option */
/* or argument), so begin by parsing yyyymmdd into yyyy, mm, and */
/* ddddd, then assigning them to integers (dd's value is the */
/* seed to ddddd, hence the assignment to ddddd, not dd). */
/*---------------------------------------------------------------*/
STRNCPY(four_char, yyyymmdd, 4) ; yyyy = atoi(four_char);
STRNCPY(two_char, (&yyyymmdd[0]+4), 2); mm = atoi(two_char );
STRNCPY(two_char, (&yyyymmdd[0]+6), 2); ddddd = atoi(two_char );
validate_mm_dd(mm, ddddd);
/*---------------------------------------------------------------*/
/* Validation OK. Add the previous month YTD days to our ddddd. */
/*---------------------------------------------------------------*/
for (n=1; n <= iterations; n++)
{
/*------------------------------------------------------------*/
/* If leap year, assign address of julian_leap to accum_days. */
/*------------------------------------------------------------*/
if (leap_year(yyyy))
accum_days = &julian_leap[0];
/*------------------------------------------------------------*/
ddddd += accum_days[mm-1]; /* January-preceding month's days */
/*------------------------------------------------------------*/
/*------------------------------------------------------------*/
/* If adjustment day(s) is negative, back up one year at a */
/* time -- adding that year's days to ddddd -- until we have */
/* enough accumulated days to handle a date that far back. */
/*------------------------------------------------------------*/
if (adj_days < 0)
{
abs_adj = labs(adj_days);
while (ddddd <= abs_adj)
ddddd += (leap_year(--yyyy)) ? 366 : 365;
}
/*-----------------------------------*/
/* Now, add in the adjustment day(s) */
/*-----------------------------------*/
ddddd += adj_days;
/*----------------------------------------------------------*/
/* If user wants weekday, print it, and exit (a zero-based */
/* number: 0=Sunday, 6=Saturday). */
/*----------------------------------------------------------*/
if (weekday_wanted == 1)
{
/*----------------------------------------------------------*/
/* In the Day of the week formula below, the divisions are */
/* integer divisions, in which remainders are discarded */
/* */
/* 14 - month */
/* a = ---------- */
/* 12 */
/* */
/* y = year - a */
/* */
/* m = month + 12a - 2 */
/* */
/* y 31m */
/* Jd = (5+ day + y + --- + ---) mod 7 # Julian */
/* 4 12 */
/* */
/* y y y 31m */
/* Gd = (day + y + --- - --- + --- + ---) mod 7 # Gregorian */
/* 4 100 400 12 */
/*----------------------------------------------------------*/
while (--yyyy > 0)
ddddd += (leap_year(yyyy)) ? 366 : 365;
n = (ddddd % 7);
if (weekday_abbr_wanted == 1)
printf("%s\n", weekdays_abbr[n]);
else
printf("%s\n", weekdays_long[n]);
exit(n); /* n = day number [0-6] */
}
/*------------------------------------------------------------*/
/* By now we have the number of days (ddddd) from a Jan. 01 */
/* date far enough back to handle any negative adjustment(s). */
/* Beginning there (and providing ddddd has a year's worth), */
/* we subtract a year's worth of days from ddddd. (We're */
/* careful to any leap day.) We do this as long as ddddd is */
/* greater than the current year's days. Through this process */
/* we calculate the new year and the number of days we have */
/* for that year. From there it is a simple matter to reduce */
/* the days to month and day values. */
/*------------------------------------------------------------*/
while (ddddd > this_years_days(yyyy))
{
ddddd -= this_years_days(yyyy);
yyyy++;
}
/*------------------------------------------------------------*/
/* If the days left in our accumulator (ddddd) are > 59, and */
/* the year proves to be a leap year, then use a YTD monthly */
/* accumulator that includes a leap day. */
/*------------------------------------------------------------*/
if ((ddddd > 59) && leap_year(yyyy))
accum_days = &julian_leap[0];
else
accum_days = &julian_days[0];
if (yyyymmdd_to_jjj)
{
printf("%03d\n", ddddd);
}
else
{
/*-----------------------------------------------------------*/
/* Rip through accum_days until we find one that is too big. */
/* Then use the preceding one as the correct month number */
/* and decrement ddddd by the month's julian days. */
/*-----------------------------------------------------------*/
for (mm=1; mm<=12; mm++)
{
if ((ddddd - accum_days[mm]) <= 0)
{
ddddd -= accum_days[mm-1];
break;
}
}
printf("%04d%02d%02d\n", yyyy,mm,ddddd);
}
}
exit(0);
/*---------------------------------------------------------------*/
/* Program never gets here, but this */ return 0; /* eliminates */
/* pesky compile message, about main not returning a value (it */
/* seems that 'exit(0)' is just not good enough). */
/*---------------------------------------------------------------*/
}
/*====================================================================*/
/* U S E R F U N C T I O N S */
/* (in alphabetical order) */
/*--------------------------------------------------------------------*/
long calc_basedate(int yyyy, int mm, int dd)
/*--------------------------------------------------------------------*/
{
long ddddd;
int i;
int leap_days;
int cc_leapdays;
int cc;
int julian_days[] = {0,31,59,90,120,151,181,212,243,273,304,334,365};
int julian_leap[] = {0,31,60,91,121,152,182,213,244,274,305,335,366};
int *accum_days = &julian_days[0];
/*-------------------------------------------------------------*/
/* We can't have a yyyy since there is no year 0000 in the CE. */
/*-------------------------------------------------------------*/
if ((yyyy - 1) > 0)
{
yyyy--;
cc = yyyy / 100;
ddddd = yyyy * 365;
leap_days = yyyy / 4;
cc_leapdays = (cc > 0) ? (cc / 4) : 0;
ddddd = ddddd + leap_days - cc + cc_leapdays;
yyyy++;
}
ddddd += dd;
/*-------------------------------------------------------*/
/* 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];
return (ddddd);
}
/*--------------------------------------------------------------------*/
int leap_year(int yyyy)
/*--------------------------------------------------------------------*/
{
/*-------------------------------------------------------------*/
/* if year is evenly divisible by 4, but yyyy is not evenly */
/* divisible by 100, OR yyyy IS evenly divisible by 400, then */
/* it's a leap year. */
/*-------------------------------------------------------------*/
return (((yyyy%4 == 0 && yyyy%100 != 0) || (yyyy%400 == 0)) ? 1 : 0);
}
/*--------------------------------------------------------------------*/
void show_documentation(char *progname)
/*--------------------------------------------------------------------*/
{
#define P printf
P("#===========================================================#\n");
P("# D O C U M E N T A T I O N #\n");
P("#===========================================================#\n");
P( "\n");
P(" Author: Bob Orlando <Bob@OrlandoKuntao.com>" "\n");
P( "\n");
P(" Date: July 2, 1995" "\n");
P( "\n");
usage(progname);
P(" Purpose: This program calculates a date (or dates) that's\n");
P(" nnnnn days (+ or -) distant from either today's \n");
P(" date or another user-supplied date. We can also\n");
P(" calculate the number of days since or until the \n");
P(" the date specified, as well as base dates since \n");
P(" either Jan. 1, 0001 or the Unix epoch date," "\n");
P(" Jan. 1, 1970" "\n");
P( "\n");
P("Description: For all practical purposes, any date the user" "\n");
P(" wishes to use as a yyyymmdd (from 01/01/0001" "\n");
P(" through 12/31/9999) is accepted. The resultant \n");
P(" date is returned via stdout in yyyymmdd form." "\n");
P(" The program is especially useful for calculating\n");
P(" yesterday's, tomorrow's, or any other desired" "\n");
P(" date. Additionally, by specifying an iteration \n");
P(" value (the last argument) the program generates \n");
P(" that number of dates, each adjusted on the" "\n");
P(" previous date (handy for calculating paydays and\n");
P(" the like). Because the program writes to stdout\n");
P(" its output is easily redirected to a file where \n");
P(" the results can be subsequently edited or munged\n");
P(" as needed. The program also comes with help" "\n");
P(" options (-h and -H for summary and detailed" "\n");
P(" outputs, respectively)." "\n");
P( "\n");
P(" Exit Codes: For all options except weekday (-w or -W)" "\n");
P(" Zero = Normal | Success" "\n");
P(" Nonzero (7+) = Abnormal | Failure" "\n");
P(" For weekday (-w or -W) option" "\n");
P(" 0-6 = Success (zero-based weekday: Sun = 0)""\n");
P(" 7+ = Failure" "\n");
P( "\n");
P(" History: The Gregorian correction to the Julian calendar \n");
P(" made in October, 1582 dropped 10 days. That" "\n");
P(" is, October 4, 1582 was followed immediately by \n");
P(" October 15. This Papal correction, although" "\n");
P(" scientifically correct, was not adopted by" "\n");
P(" non-Catholic countries until almost two" "\n");
P(" centuries later in 1752. In September, 1752" "\n");
P(" the English calendar was adjusted to Pope" "\n");
P(" Gregory's method of correction and 11 days were \n");
P(" dropped (September 14, followed September 2)." "\n");
P( "\n");
P(" While this routine easily calculates the date" "\n");
P(" that far back, it does not drop October 5-14," "\n");
P(" 1582 or September 3-13, 1752. If this routine""\n");
P(" is used to calculate dates that far back, the" "\n");
P(" previously adjusted and dropped dates will" "\n");
P(" appear as if no calendar corrections were ever""\n");
P(" made. All of this is only for technical" "\n");
P(" purists. Most real-world applications will not \n");
P(" go back that far. This program's accuracy," "\n");
P(" then, is sufficient for most purposes." "\n");
P( "\n");
P(" Note: As with any program there are doubtless, many" "\n");
P(" places where this code can be improved or" "\n");
P(" simplified. Feel free, therefore, to forward" "\n");
P(" comments/suggestions to Bob@OrlandoKuntao.com.""\n");
P(" That said, my preference is for readable, easy""\n");
P(" to follow code over highly efficient, but" "\n");
P(" difficult to follow (read obfuscated) code since\n");
P(" readable code (even if less efficient) makes" "\n");
P(" maintenance much less a chore." "\n");
P( "\n");
P(" +----------------------------------------------------------+\n");
P(" | For modifications, see 'Modified' section in the |\n");
P(" | source code immediately following this statement. |\n");
P(" +----------------------------------------------------------+\n");
P( "\n");
/*-----------------------------------------------------------------*/
/* */
/* Modified: 2010-03-19 Bob Orlando */
/* v2.3 * Bug fix: Added code to reject day '00' */
/* supplied by user. */
/* */
/* 2004-07-14 Bob Orlando */
/* v2.2 * Add -W option (same as -w, but returns */
/* long weekday name). Thanks to Hans */
/* Looyschelder of the Netherlands for */
/* both the idea and the clean code for */
/* accomplishing it. */
/* */
/* 2004-05-05 Bob Orlando */
/* v2.1 * Add -S option (same as -s, but returns */
/* unsigned integer regardless of whether */
/* the target date is future or past). */
/* */
/* Revised: 2003-05-30 Bob Orlando */
/* v2.0 * Incorporate most of basedate.c */
/* functionality (options -b, -u, and -s). */
/* */
/*-----------------------------------------------------------------*/
#undef P
return;
}
/*--------------------------------------------------------------------*/
int this_years_days(int yyyy)
/*--------------------------------------------------------------------*/
{
return ((leap_year(yyyy)) ? 366 : 365);
}
/*--------------------------------------------------------------------*/
void usage(char *progname)
/*--------------------------------------------------------------------*/
{
#define P printf
P(" Program ID: %s (03-19-2010 / v2.3)\n", progname);
P( "\n");
P(" Usage: %s -bHhJjSstuWwy\n"
" [-|+]adj_days [yyyymmdd [iterations]]\n",progname);
P( "\n");
P(" -b Basedate of today (or date specified)" "\n");
P(" since Jan. 1, 0001" "\n");
P(" -H Detailed help (Documentation)" "\n");
P(" -h Summary help (Usage)" "\n");
P(" -J Julian (yyyyJJJ) to Gregorian (yyyymmdd)""\n");
P(" -j Julian day (ddd) for today or yyyymmdd" "\n");
P(" -S Days since (or until) yyyymmdd (unsigned) \n");
P(" -s Days since (or until) yyyymmdd **" "\n");
P(" -t Tomorrow's date" "\n");
P(" -u Basedate of today (or date specified)" "\n");
P(" since Jan. 1, 1970--the Unix epoch date" "\n");
P(" (essentially 'dateplus -s 19700101')" "\n");
P(" -W Date's long weekday" "\n");
P(" -w Date's weekday" "\n");
P(" (-w or -W: see 'Exit Codes' below)" "\n");
P(" -y Yesterday's date" "\n");
P( "\n");
P(" Notes: adj_days may be positive, negative, or unsigned.\n");
P( "\n");
P(" adj_days by itself (without a yyyymmdd option" "\n");
P(" argument), yields an adjusted date that is" "\n");
P(" calculated from today's date. For example," "\n");
P(" '%s 1' yields tomorrow's date).\n", progname);
P( "\n");
P(" yyyymmdd is an optional date from which an" "\n");
P(" adjusted date is calculated." "\n");
P( "\n");
P(" iterations generate multiple dates that are" "\n");
P(" adj_days apart (i.e. paydays). This argument" "\n");
P(" is not allowed with the julian (-j) option." "\n");
P( "\n");
P(" yyyymmdd also may be a future date (useful for""\n");
P(" determining the number of days until a certain""\n");
P(" date). Days future are reported negatively." "\n");
P(" For example, run on October 27, 1997), the" "\n");
P(" ** 'dateplus -s 20000101' yielded -731, meaning" "\n");
P(" 731 days until Y2K. (For unsigned results use""\n");
P(" -S option instead.)" "\n\n");
#undef P
return;
}
/*--------------------------------------------------------------------*/
void validate_mm_dd(int mm, int dd)
/*--------------------------------------------------------------------*/
{
int mm_days;
/*-----------------------------------------------------*/
/* Validate the month and day. Start by determining */
/* the maximum possible days for the month. Then, see */
/* if the day for the month given exceeds its maximum. */
/*-----------------------------------------------------*/
switch (mm)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
mm_days=31;
break;
case 4:
case 6:
case 9:
case 11:
mm_days=30;
break;
case 2:
mm_days = (leap_year(yyyy)) ? 29 : 28;
break;
default:
fprintf(stderr,"Month %02d is invalid.\n", mm);
exit(21);
}
if (dd > mm_days)
{
fprintf(stderr,"%s, %d does not have %d days.\n",
months[mm-1], yyyy, dd);
exit(22);
}
if (dd < 1)
{
fprintf(stderr,"%d is an invalid day for any month!\n", dd);
exit(23);
}
return; /* Passed with flying colors. */
}
|