[comp.sources.wanted] Date manipulation routines

jimb@dhw68k.cts.com (Jim Bacon) (09/11/88)

I have seen at least two requests for date manipulation routines lately.
Here is the library that I use.  It is based heavily on the routines
found in "Common C Functions" (Que).  Because it allows Julian dates based
on any given year, it easily allows for math to be performed on dates.
Notice also that it will work with dates without leading zeroes in the
MM/DD/YY format.

I can be reached at turnkey!anacom1!jim or jim@anacom1.tcc.com

Enjoy!

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#               "End of shell archive."
# Contents:  lldates.c
# Wrapped by jim@anacom1.tcc.com on Sun Sep 11 01:15:47 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'lldates.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lldates.c'\"
else
echo shar: Extracting \"'lldates.c'\" \(4967 characters\)
sed "s/^X//" >'lldates.c' <<'END_OF_FILE'
X/*****************************************************************************
X *
X *     MODULE NAME: datelib.c
X *
X *       FUNCTIONS: long       juldate(), julcmp()
X *                  int        isleap(), zeller()
X *                  char       *julrev()
X *
X *     REVISION HISTORY:
X *     Version/--date--/-by-/reason------------------------------------------
X *     1.0     12/28/85 jb   julian date functiX#define        INT(x)  ((int)(x))
X
X#define        MO      0
X#define        DAY     1
X#define        YR      2
X
X#define        BAD_DATE        0
X#define        BAD_DAY         1
X#define        BAD_MO          2
X#define        BAD_YR          3
X#define        BAD_DIGIT       4
X#define        BAD_JUL         5
X
Xchar   *wk_day[] = { 
X       "ERROR", "Sunday", "Monday", "Tuesday", "Wednesday",
X       "Thursday", "Friday", "Saturday" };
X
X
Xint    days_mo[] = {
X       0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
X
X
Xint    _julerr;                /* global error return */
X
X/**
X *     FUNCTION NAME:  int     isleap()
X *
X *       DESCRIPTION:  determines whether a given year is leap or not.
X *
X *            INPUTS:  int     year
X *
X *           OUTPUTS:  TRUE if leap, otherwise FALSE
X */
X
Xint    isleap(year)
Xint    year;
X{
X       return( year % 4 == 0 && year % 100 != 0 || year % 400 == 0 );
X}
X
X
X/**
X *     FUNCTION NAME: long juldate()
X *
X *       DESCRIPTION:  converts a date string to number of days from a
X *                     given base year.
X *
X *            INPUTS:  char    *date           pointer to date string
X *                                 3], n;
X       long    retjul;
X
X       for ( n = 0; n < 3; n++) mdy[n] = 0;
X       _julerr = 0;
X       n = 0;
         return(BAD_DATE);
X       }
X
X       if ( mdy[YR] < 100 ) {
X               if ( mdy[YR] < base - 1900)
X                       mdy[YR] += 2000;
X               else
X                       mdy[YR] += 1900;
X       }
X
X       if ( mdy[YR] < base ) {
X               _julerr = BAD_YR;
X               return(BAD_DATE);
X       }
X
X       days_mo[2] = isleap(mdy[YR]) ? 29 : 28;
X
X       if ( mdy[DAY] < 1 || mdy[DAY] > days_mo[mdy[MO]] ) {
X               _julerr = BAD_DAY;
X               return(BAD_DATE);
X       }
X
X       retjul = mdy[DAY];
X
X       for (n = 1; n < mdy[MO]; n++)
X               retjul += days_mo[n];
X
X       for 
X *            INPUTS:  char    *str1,*str2      date strings 
X *
X *           OUTPUTS:  difference between julian dates based on common
X *                     year of 1900.
X *
X *          PROBLEMS:  both input dates must be >= 1/1/1900.
X *                     returns 0 if bad date strings are input.
X */
X
Xlong   julcmp(str1, str2)
Xchar   *str1, *str2;
X{
X       long    jul1, jul2;
X
X       _julerr = 0;
X
X       if ( !(jul1 = juldate(str1, 1900)) )
X               return(BAD_DATE);
X
X       if ( !(jul2 = juldate(str2, 1900)) )
X               return(BAD_DATE);
X
X       return(jul2 - jul1);
X}
X
X
X/**
X *     FUNCTION NAME: char     *julrev()
X *
X *       DESCRIPTION:  returns a date string from an unsigned julian integer
X *
X *            INPUTS:  long    jul             julian number
X *                     int     year            base year
X *
X *           OUTPUTS:  pointer to date string. NULL if negative jul
X */
X
Xchar   *julrev(jul, year)
Xlong   jul;
Xint    year;
X{
X       static char     date[11];
X       int     days_year, n = 1;
X
X       memset(date, '\0', sizeof(date));
X
X       days_mo[2] = 28;
X
X       if ( jul > 0 )
X               _julerr = 0;
X       else {
X               _julerr = BAD_JUL;
X               return(NULL);
X       }
X
X       do {
X               days_year = isleap(year) ? 366 : 365;
X               year++; 
X               jul -= days_year;
X       } while ( jul > 0 );
X
X       year--; 
X       jul += days_year;
X
X       if ( days_year == 366 )
X               days_mo[2] = 29;
X
X       do
X               jul -= days_mo[n++];
X       while ( jul > 0 );
X
X       --n; 
X       jul += d input date.
X *
X *            INPUTS:  char    *date           mm/dd/yy or mm/dd/yyyy
X *
X *           OUTPUTS:  int     n               sun = 1, sat = 7, 0 if error
X */
X
Xint    zeller(date)
Xchar   *date;
X{
X
X       char    c;
X       int     mdy[3], month, year, century, offset, n = 0;
X
X       _julerr = 0;
X
X       mdy[DAY] = mdy[MO] = mdy[YR] = 0;
X
X       while ( c = *date++) {
X               if ( c == '-' || c == '/' ) {
X                       n++; 
X                       continue;
X               }
X               if ( !isdigit(c) ) {
X                       _julerr = BAD_DIGIT;
X                       return(BAD_DATE);
X               }
X               mdy[n] = 10 * mdy[n] + (c - '0');
X       }
X
X       mdy[YR] = (mdy[YR] < 100 ? mdy[YR] + 1900 : mdy[YR]);
X
X       if ( mdy[MO] > 2 ) {
X               month = mdy[MO] - 2;
X               year = mdy[YR];
X       } else
X        {
X               month = mdy[MO] + 10;
X               year = mdy[YR] - 1;
X       }
X
X       century = year / 100;
tes.c'`; then
    echo shar: \"'lldates.c'\" unpacked with wrong size!
fi
# end of 'lldates.c'
fi
echo shar: End of shell archive.
exit 0

jimb@dhw68k.cts.com (Jim Bacon) (09/20/88)

Here is a re-post of the routines I posted earlier.  Several of you have
informed me of a grunged post, this should be better.

 
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#               "End of shell archive."
# Contents:  lldates.c
# Wrapped by jim@anacom1.tcc.com on Sun Sep 11 01:15:47 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'lldates.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lldates.c'\"
else
echo shar: Extracting \"'lldates.c'\" \(4967 characters\)
sed "s/^X//" >'lldates.c' <<'END_OF_FILE'
X/*****************************************************************************
X *
X *     MODULE NAME: datelib.c
X *
X *       FUNCTIONS: long       juldate(), julcmp()
X *                  int        isleap(), zeller()
X *                  char       *julrev()
X *
X *     REVISION HISTORY:
X *     Version/--date--/-by-/reason------------------------------------------
X *     1.0     12/28/85 jb   julian date function library
X *     1.1     9/10/88  jgb  cleaned up for UNIX
X *
X *     DESCRIPTION: routines for date manipulations
X *
X ****************************************************************************/
X
X#include       <stdio.h>
X#include       <ctype.h>
X#include       <memory.h>
X
X#define        INT(x)  ((int)(x))
X
X#define        MO      0
X#define        DAY     1
X#define        YR      2
X
X#define        BAD_DATE        0
X#define        BAD_DAY         1
X#define        BAD_MO          2
X#define        BAD_YR          3
X#define        BAD_DIGIT       4
X#define        BAD_JUL         5
X
Xchar   *wk_day[] = { 
X       "ERROR", "Sunday", "Monday", "Tuesday", "Wednesday",
X       "Thursday", "Friday", "Saturday" };
X
X
Xint    days_mo[] = {
X       0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
X
X
Xint    _julerr;                /* global error return */
X
X/**
X *     FUNCTION NAME:  int     isleap()
X *
X *       DESCRIPTION:  determines whether a given year is leap or not.
X *
X *            INPUTS:  int     year
X *
X *           OUTPUTS:  TRUE if leap, otherwise FALSE
X */
X
Xint    isleap(year)
Xint    year;
X{
X       return( year % 4 == 0 && year % 100 != 0 || year % 400 == 0 );
X}
X
X
X/**
X *     FUNCTION NAME: long juldate()
X *
X *       DESCRIPTION:  converts a date string to number of days from a
X *                     given base year.
X *
X *            INPUTS:  char    *date           pointer to date string
X *                                             in mm/dd/yy or yyyy format
X *                     int     base            base year, >= 1900
X *
X *           OUTPUTS:  number of days from 1/1/base or 0 if error
X */
X
Xlong   juldate(date, base)
Xchar   *date;
Xint    base;
X{
X       char    c;
X       int     mdy[3], n;
X       long    retjul;
X
X       for ( n = 0; n < 3; n++) mdy[n] = 0;
X       _julerr = 0;
X       n = 0;
X       days_mo[2] = 28;        /* fix possible prior feb 29 */
X
X       while ( c = *date++) {
X               if ( c == '-' || c == '/' ) {
X                       n++;
X                       continue;
X               }
X               if ( !isdigit(c) ) {
X                       _julerr = BAD_DIGIT;
X                       return(BAD_DATE);
X               }
X               mdy[n] = 10 * mdy[n] + (c - '0');
X       }
X
X       if ( mdy[MO] < 1 || mdy[MO] > 12 ) {
X               _julerr = BAD_MO;
X               return(BAD_DATE);
X       }
X
X       if ( mdy[YR] < 100 ) {
X               if ( mdy[YR] < base - 1900)
X                       mdy[YR] += 2000;
X               else
X                       mdy[YR] += 1900;
X       }
X
X       if ( mdy[YR] < base ) {
X               _julerr = BAD_YR;
X               return(BAD_DATE);
X       }
X
X       days_mo[2] = isleap(mdy[YR]) ? 29 : 28;
X
X       if ( mdy[DAY] < 1 || mdy[DAY] > days_mo[mdy[MO]] ) {
X               _julerr = BAD_DAY;
X               return(BAD_DATE);
X       }
X
X       retjul = mdy[DAY];
X
X       for (n = 1; n < mdy[MO]; n++)
X               retjul += days_mo[n];
X
X       for (n = base; n < mdy[YR]; n++)
X               retjul += ( isleap(n) ? 366 : 365 );
X
X       return(retjul);
X}
X
X
X/**
X *     FUNCTION NAME:  long    julcmp()
X *
X *       DESCRIPTION:  returns the numbers of days between 2 dates.
X *                     negative if str1 > str2.
X *
X *            INPUTS:  char    *str1,*str2      date strings 
X *
X *           OUTPUTS:  difference between julian dates based on common
X *                     year of 1900.
X *
X *          PROBLEMS:  both input dates must be >= 1/1/1900.
X *                     returns 0 if bad date strings are input.
X */
X
Xlong   julcmp(str1, str2)
Xchar   *str1, *str2;
X{
X       long    jul1, jul2;
X
X       _julerr = 0;
X
X       if ( !(jul1 = juldate(str1, 1900)) )
X               return(BAD_DATE);
X
X       if ( !(jul2 = juldate(str2, 1900)) )
X               return(BAD_DATE);
X
X       return(jul2 - jul1);
X}
X
X
X/**
X *     FUNCTION NAME: char     *julrev()
X *
X *       DESCRIPTION:  returns a date string from an unsigned julian integer
X *
X *            INPUTS:  long    jul             julian number
X *                     int     year            base year
X *
X *           OUTPUTS:  pointer to date string. NULL if negative jul
X */
X
Xchar   *julrev(jul, year)
Xlong   jul;
Xint    year;
X{
X       static char     date[11];
X       int     days_year, n = 1;
X
X       memset(date, '\0', sizeof(date));
X
X       days_mo[2] = 28;
X
X       if ( jul > 0 )
X               _julerr = 0;
X       else {
X               _julerr = BAD_JUL;
X               return(NULL);
X       }
X
X       do {
X               days_year = isleap(year) ? 366 : 365;
X               year++; 
X               jul -= days_year;
X       } while ( jul > 0 );
X
X       year--; 
X       jul += days_year;
X
X       if ( days_year == 366 )
X               days_mo[2] = 29;
X
X       do
X               jul -= days_mo[n++];
X       while ( jul > 0 );
X
X       --n; 
X       jul += days_mo[n];
X
X       year = (year > 1999 ? year : year - 1900);
X
X       sprintf(date, "%02d/%02d/%02d", n, (int)jul, year);
X
X
X       return(date);
X}
X
X
X/**
X *     FUNCTION NAME:  int     *zeller()
X *
X *       DESCRIPTION:  produces zeller congruence give an input date.
X *
X *            INPUTS:  char    *date           mm/dd/yy or mm/dd/yyyy
X *
X *           OUTPUTS:  int     n               sun = 1, sat = 7, 0 if error
X */
X
Xint    zeller(date)
Xchar   *date;
X{
X
X       char    c;
X       int     mdy[3], month, year, century, offset, n = 0;
X
X       _julerr = 0;
X
X       mdy[DAY] = mdy[MO] = mdy[YR] = 0;
X
X       while ( c = *date++) {
X               if ( c == '-' || c == '/' ) {
X                       n++; 
X                       continue;
X               }
X               if ( !isdigit(c) ) {
X                       _julerr = BAD_DIGIT;
X                       return(BAD_DATE);
X               }
X               mdy[n] = 10 * mdy[n] + (c - '0');
X       }
X
X       mdy[YR] = (mdy[YR] < 100 ? mdy[YR] + 1900 : mdy[YR]);
X
X       if ( mdy[MO] > 2 ) {
X               month = mdy[MO] - 2;
X               year = mdy[YR];
X       } else
X        {
X               month = mdy[MO] + 10;
X               year = mdy[YR] - 1;
X       }
X
X       century = year / 100;
X       offset = year % 100;
X
X       n = INT((13 * month - 1) / 5) + mdy[DAY] + offset + INT(offset / 4)
X        + INT(century / 4) - 2 * century + 77;
X       n = n - 7 * INT(n / 7);
X
X       return(n + 1);
X}
END_OF_FILE
if test 4967 -ne `wc -c <'lldates.c'`; then
    echo shar: \"'lldates.c'\" unpacked with wrong size!
fi
# end of 'lldates.c'
fi
echo shar: End of shell archive.
exit 0