static char SccsId[] = "@(#)dateplus.c 2.2 07/14/04"; /*--------------------------------------------------------------------*/ /* dateplus.c */ /* ------------------------------------------------------------------ */ /* */ /* Copyright (c) 1995-2004 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 #include /* abs, atoi, exit */ #include /* strlen, strncpy */ #include /* isdigit */ #include /* 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 " "\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: 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 (07-14-2004 / v2.2)\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); } return; /* Passed with flying colors. */ }