/*
   Functions for date calculation and conversion.  Inspired by DATE.C from
   John A. Hodgson, and a need for date calculation routines in an upcoming
   project.  These routines should be error-free until the beginning of the
   22nd century.  Please see the text above each routine for notes and comments
   on usage.  I've spent much time developing and testing these routines.

*/


typedef struct JULDATE {
	short  year;
	short  days;
	};

short days_before[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
	/*  # of days preceding each month  */

/*
    The following routine accepts one argument, a pointer to a JULDATE
    structure.  It returns the current date in Julian date form.
*/

void Get_Date(date)
struct JULDATE *date;
{
	long datestmp[3];
	int junk, year_span, years_left, remainder, leapdays;

	junk = DateStamp(&datestmp[0]);		/*  get the date  */
	datestmp[0] += 1;			/*  add 1 for Jan. 1, 1978  */
	year_span = datestmp[0] / 366;		/*  approx. year  */
	date->days = datestmp[0] % 366;		/*  # of days left over  */
	leapdays = year_span / 4;		/*  approx. # of leap days  */
	date->year = 1978 + year_span;		/*  current year  */
	years_left = year_span % 4;		/*  years since last leap yr  */
	remainder = date->year % 4;	/*  # of years since last leap  */
	if (remainder <= years_left && remainder > 0)
		leapdays += 1;		/*  recent year was a leap year  */
	date->days += year_span - leapdays;	/*  correct for leap days  */
	if (remainder > 0 && date->days > 365)
		{
		date->year += 1;
		date->days -= 365;
		}
	else if (remainder == 0 && date->days > 366)
		{
		date->year += 1;
		date->days -= 366;
		}
}

/*
    This function accepts two arguments:  a string pointer and a pointer to a
    JULDATE structure.  The routine will convert the Julian date to Gregorian
    (mm-dd-yyyy) format.  The string pointer must point to a space at least
    11 characters long.
*/

void JultoGreg(greg_date, date)
char *greg_date;
struct JULDATE *date;
{
	int length, year, month, day;
	short julday;
	char string[6];

	year = date->year;
	julday = date->days;
	if ( !(year % 4) && julday > 60)  julday -= 1;
		/*  subtract 1 if we are in a leap year & past Feb.  */
	for (month = 0; (days_before[month] < julday) && (month < 12); month++);
		/*  find month  */
	day = julday - days_before[month-1];	/*  get current day  */
	length = stci_d(greg_date, month, 11);	/*  month into greg_date  */
	strcat(greg_date, "-");
	length = stci_d(string, day, 6);
	strcat(greg_date, string);		/*  add day to greg_date  */
	strcat(greg_date, "-");
	length = stci_d(string, year, 6);
	strcat(greg_date, string);		/*  add year to greg_date  */	
}

/*
    The following function accepts two arguments:  A pointer to a JULDATE
    structure and a pointer to a string.  It evaluates the string to determine
    if it is a valid date, then converts it to Julian form and returns the
    value in the JULDATE structure.  If the date is invalid, the year element
    of the JULDATE structure will be 0.  It is not 100% accurate in its date-
    checking, as it only checks to make sure that the date is not greater than
    31, without checking what the maximum value should be for that month.  The
    Julian date returned *will* be valid, however, if not correct.

    NOTE:  The Gregorian date passed to this routine can be in mm-dd-yy OR
           mm-dd-yyyy form.  The century will automatically be added if the
	   form is the former.  (no pun intended)
*/

void GregtoJul(date, greg_date)
struct JULDATE *date;
char *greg_date;
{
	extern char *stpblk();		/*  standard string functions that  */
					/*  strips leading spaces	    */
	int month, day, year;
	short count;
	char *stptr;

	date->year = 0;				/*  will be 0 if date invalid */
	stptr = stpblk(greg_date);		/*  advance past any blanks  */
	count = stcd_i(stptr, &month);		/*  extract month #  */
	stptr += count;
	if ( (stptr[0] == '-' || stptr[0] == '/') && month >= 1 && month <= 12)
		{
		stptr += 1;			/*  skip delimiter  */
		count = stcd_i(stptr, &day);
		stptr += count;
		if ((stptr[0] == '-' || stptr[0] == '/') && day >=1 && day <=31)
			{
			stptr += 1;		/*  skip delimiter  */
			count = stcd_i(stptr, &year);
			if ( year > 0 )
				{
				if ( year < 100 )
					year += 1900;	/*  only got 2 digits */
				date->year = year;
				date->days = days_before[month - 1];
				date->days += day;
				if (!(year % 4) && month > 2)	/*  > Feb  */
					date->days += 1;	/*  leap year */
				}
			}
		}
}

/*
    The following function accepts two arguments, both pointers to JULDATE
    structures.  The routine calculates the number of days between the two
    dates and returns the result as an integer.  If the first date is earlier
    than the second, the value returned will be positive.  If the second date
    is earlier than the first, the value returned will be negative.

    NOTE TO 8/16 BIT USERS:  int on the Amiga (and presumably any 16/32 bit
    machine) declares a 32-bit integer.  I use int here for clarity, but if
    the default size of int on your machine is 16-bit, you will have to
    declare days1 & days2 as long.
*/

int Date_Span(date1, date2)
struct JULDATE *date1, *date2;
{
	int days1, days2;

	days1 = (date1->year - 1) * 365 + (date1->year - 1) / 4 + date1->days;
	days2 = (date2->year - 1) * 365 + (date2->year - 1) / 4 + date2->days;
	return (days2 - days1);
}

/*
    The following function accepts a pointer to a JULDATE structure and 
    returns an integer that is the current day of the month.  It also assigns
    to num the number of days in the current month, and to mn the month #.
*/

int Datestat(date, num, mn)
struct JULDATE *date;
int *num, *mn;
{
	int month;
	short julday;

	julday = date->days;
	if (date->year % 4 == 0 && julday > 60) julday -= 1;
	for (month = 0; (days_before[month] < julday) && (month < 12); month++);
		/*  find month  */
	if (month == 12)
		*num = 31;
	else
		{
		*num = days_before[month] - days_before[month - 1];
		if (date->year % 4 == 0 && month == 2)
			*num += 1;
		}
	*mn = month;
	return(julday - days_before[month-1]);
}
