/* This function module provides three functions for manipulating dates:
/*   LegalDays -- adjusts dates to reflect actual number of days per month
/*   FromDosDate -- converts information provided via AmigaDos DateStamp to
/*     MM, DD, YYYY, Hour, Minute, Second information
/*   ToDosDate -- converts MM, DD, YYYY, Hour, Minute and Second data to
/*     AmigaDos DateStamp format */

/* First we need to get various structure and DEFINE info... */
#include <libraries/dos.h>
#include <lattice/stdio.h>

/* Right now, AmigaDos' day zero is midnight, 1 January 1978 */
#define STARTYEAR 1978

/* This keeps the compiler happy; DateStamp() is an AmigaDos function */
void DateStamp();

/* This function takes three arguments and returns a 'legal' day number,
/* defined as follows:
/*   - the supplied day number if it is legitimate (e.g., March 31, or 
/*     Feb 29 in a leap year)
/*   - the last legitimate day of the specified month (e.g., Feb 28 if Feb 29
/*     is supplied and Year is not a leap year 
/*   - 1 if the supplied day number is less than or equal to zero
/* January is month number 1. */

int LegalDays(Month,Day,Year)
  int Month,Day,Year;
  {
   /* First make sure we have a positive date */
   Day=Day<=0?1:Day;
   /* What we do depends on what month we're in */
   switch(Month)
     {
      /* February...*/
      case 2:
      /* Leap years.  Years divisible by 400, e.g., 2000, are not leap years */
        if(Year%4==0 && Year%400!=0)
          {
           if(Day>29)
             Day=29;
          }
      /* Normal years */
        else
          {
           if(Day>28)
             Day=28;
          }
        break;
      /* Months with 30 days */
      case 4:
      case 6:
      case 9:
      case 11:
        if(Day>30)
          Day=30;
        break;
      /* Months with 31 days, which is everything else */
      default:
        if(Day>31)
          Day=31;
        break;
     }
   return(Day);
  }

/* This function 'returns' pointers to integers specifying month, day, year,
/* hour, minute and second as known by AmigaDos.  'Returns' is in quotes
/* because we are doing call by reference (I think) here to get around
/* C's limitation on only returning one value from a function.*/

void FromDosDate(DosPtr,MPtr,DPtr,YPtr,HPtr,MinPtr,SPtr)
  struct DateStamp *DosPtr;
  int *MPtr,*DPtr,*YPtr,*HPtr,*MinPtr,*SPtr;
  {
   int NumLeap,NumReg;
   
   /* Number of leap years between 1978 and current date.  Dos returns the
   /* number of days between 1/1/1978 and current date.  What we do here is
   /* calculate the number of 1461 day periods (4 years, including the leap)
   /* since 1/1/1978.  We need to adjust for the fact that the first leap
   /* after 1/1/1978 was only two years later, in 1980.*/
   
   NumLeap=(DosPtr->ds_Days+((STARTYEAR-1)%4)*365)/1461;

   /* The number of regular years is the number of days left after we subtract
   /* 366 days for each leap year divided by 365 days */

   NumReg=(DosPtr->ds_Days-366*NumLeap)/365;

   /* If we've calculated a year beyond 2000 we need to adjust for the fact
   /* that the year 2000 is not a leap year.*/
   *YPtr=STARTYEAR+NumLeap+NumReg;
   if(*YPtr>2000)
     {
      --NumLeap;
      NumReg=(DosPtr->ds_Days-366*NumLeap)/365;
      *YPtr=STARTYEAR+NumLeap+NumReg;
     }
     
   /* Now we'll subtract out the number of days in each month of the year  
   /* until we go nonpositive, at which point we'll have reached the correct
   /* month.  To start, we need to reduce the day count by the number of days
   /* in the years between 1/1/1978 and current time and reset the month 
   /* counter.*/

   *DPtr=DosPtr->ds_Days-366*NumLeap-365*NumReg+1;
   *MPtr=0;
   while(*DPtr>0)
     {
      *DPtr-=LegalDays(*MPtr,99,*YPtr);
      ++*MPtr;
     }
     
   /* Actually, we've skipped to the end of the correct month, so add back
   /* its number of days.*/

   *DPtr+=LegalDays(*MPtr-1,99,*YPtr);

   /* Now do seconds, minutes and hours. */
   *SPtr=DosPtr->ds_Tick/TICKS_PER_SECOND;
   *HPtr=DosPtr->ds_Minute/60;
   *MinPtr=DosPtr->ds_Minute-*HPtr*60;
  }
  
/* This function 'returns' a pointer to a DateStamp structure which is
/* initialized according to user-supplied date and time information.  The
/* returned pointer could have been returned by value, rather than by
/* reference, but I wanted to keep the parameter list identical to 
/* FromDosDate().*/

void ToDosDate(DosPtr,MPtr,DPtr,YPtr,HPtr,MinPtr,SPtr)
  struct DateStamp *DosPtr;
  int *MPtr,*DPtr,*YPtr,*HPtr,*MinPtr,*SPtr;
  {
   int NumLeap,Month=0;

   /* The number of leap years since 1/1/1978 is modulo 4 the number of years
   /* between the current date and 1978.  However, we must adjust for the fact
   /* that the first leap year after 1978 was only two years later in 1980.*/
  
   NumLeap=(*YPtr-STARTYEAR+(STARTYEAR-1)%4)/4;

   /* Once again, we adjust for 2000 not being a leap year */

   if(*YPtr>2000)
     --NumLeap;

   /* The number of days since 1978 is, to begin with, 366 for every leap
   /* year since then and 365 for every other year since then. */

   DosPtr->ds_Days=NumLeap*366+(*YPtr-STARTYEAR-NumLeap)*365;

   /* Now we add in the days for each month up to the month before the
   /* current one.*/

   while(Month<(*MPtr-1))
     {
      DosPtr->ds_Days+=LegalDays(Month,99,*YPtr);
      ++Month;
     }

   /* Now add the day number within the current month.  We must subtract 1
   /* because AmigaDos treats 1/1/1978 as 0 days away from its time zero.*/
   DosPtr->ds_Days+=*DPtr-1;

   /* Convert seconds to ticks */
   DosPtr->ds_Tick=*SPtr*TICKS_PER_SECOND;
   /* Convert hours and minutes to minutes since midnight */
   DosPtr->ds_Minute=*HPtr*60+*MinPtr;
  }
