home *** CD-ROM | disk | FTP | other *** search
/ C Programming Starter Kit 2.0 / SamsPublishing-CProgrammingStarterKit-v2.0-Win31.iso / bc45 / classsrc.pak / DATE.CPP < prev    next >
C/C++ Source or Header  |  1997-07-23  |  10KB  |  340 lines

  1. /*------------------------------------------------------------------------*/
  2. /*                                                                        */
  3. /*  DATE.CPP                                                              */
  4. /*                                                                        */
  5. /*  Copyright (c) 1993, 1994 Borland International                        */
  6. /*  All Rights Reserved                                                   */
  7. /*                                                                        */
  8. /*------------------------------------------------------------------------*/
  9.  
  10. #if !defined( __STDIO_H )
  11. #include <stdio.h>
  12. #endif
  13.  
  14. #if !defined( __TIME_H )
  15. #include <time.h>
  16. #endif
  17.  
  18. #if !defined( __STRING_H )
  19. #include <string.h>
  20. #endif
  21.  
  22. #if !defined( __CTYPE_H )
  23. #include <ctype.h>
  24. #endif
  25.  
  26. #if !defined( __CSTRING_H )
  27. #include <cstring.h>
  28. #endif
  29.  
  30. #if !defined( __CHECKS_H )
  31. #include <checks.h>
  32. #endif
  33.  
  34. #if !defined( CLASSLIB_DATE_H )
  35. #include <classlib/date.h>
  36. #endif
  37.  
  38. /****************************************************************
  39.  *                                                              *
  40.  *                      static constants                        *
  41.  *                                                              *
  42.  ****************************************************************/
  43.  
  44. static const unsigned char DaysInMonth[12] = 
  45.     { 31,28,31,30,31,30,31,31,30,31,30,31 };
  46. static const DayTy FirstDayOfEachMonth[12] =
  47.     { 1,32,60,91,121,152,182,213,244,274,305,335 };
  48. static const char *MonthNames[12] = 
  49.     { "January","February","March","April","May","June",
  50.       "July","August","September","October","November","December" };
  51. static const char *UCMonthNames[12] =
  52.     { "JANUARY","FEBRUARY","MARCH","APRIL","MAY","JUNE",
  53.       "JULY","AUGUST","SEPTEMBER","OCTOBER","NOVEMBER","DECEMBER" };
  54. static const char *WeekDayNames[7] =
  55.     { "Monday","Tuesday","Wednesday",
  56.       "Thursday","Friday","Saturday","Sunday" };
  57. static const char *UCWeekDayNames[7] =
  58.     { "MONDAY","TUESDAY","WEDNESDAY",
  59.       "THURSDAY","FRIDAY","SATURDAY","SUNDAY" };
  60.  
  61. static int _BIDSNEARFUNC
  62. FindMatch( const char *str, const char**candidates, int icand );
  63.  
  64. /***************************************************************************/
  65.  
  66. //      constructors
  67.  
  68. /***************************************************************************/
  69.  
  70. // Construct a TDate for today's date.
  71. TDate::TDate()
  72. {
  73.     long clk = time(0);
  74.     struct tm _FAR *now = localtime(&clk);
  75.     Julnum = Jday(now->tm_mon+1, now->tm_mday, now->tm_year+1900);
  76. }
  77.  
  78. /*
  79.  * Construct a TDate with a given day of the year and a given year.  The
  80.  * base date for this computation is Dec. 31 of the previous year.  If
  81.  * year == 0, Construct a TDate with Jan. 1, 1901 as the "day zero".
  82.  * i.e., TDate(-1) = Dec. 31, 1900 and TDate(1) = Jan. 2, 1901.
  83.  */
  84.  
  85. TDate::TDate(DayTy day, YearTy year)
  86. {
  87.     if( year )
  88.         Julnum = Jday( 12, 31, year-1 ) + (JulTy)day;
  89.     else
  90.         Julnum = jul1901                + (JulTy)day;
  91. }
  92.  
  93. //   Construct a TDate for the given day, monthName, and year.
  94. TDate::TDate( DayTy day, const char _BIDSFAR *monthName, YearTy year )
  95. {
  96.     Julnum = Jday( IndexOfMonth(monthName), day, year );
  97. }
  98.  
  99. //   Construct a TDate for the given day, month, and year.
  100. TDate::TDate( DayTy day, MonthTy month, YearTy year )
  101. {
  102.     Julnum = Jday( month, day, year );
  103. }
  104.  
  105. /***************************************************************************/
  106.  
  107. //                      static member functions
  108.  
  109. /***************************************************************************/
  110.  
  111. // Returns a string name for the weekday number.
  112. // Monday == 1, ... , Sunday == 7
  113. // Return 0 for weekday number out of range
  114. const char _BIDSFAR *TDate::DayName( DayTy weekDayNumber )
  115. {
  116.     return AssertWeekDayNumber(weekDayNumber) ? WeekDayNames[weekDayNumber-1] : 0;
  117. }
  118.  
  119. // Return the number, 1-7, of the day of the week named nameOfDay.
  120. // Return 0 if name doesn't match.
  121. DayTy TDate::DayOfWeek( const char _BIDSFAR *nameOfDay )
  122. {
  123.     return (DayTy)(FindMatch( nameOfDay, UCWeekDayNames, 7 )+1);
  124. }
  125.  
  126. // Is a day (1-31) within a given month?
  127. int TDate::DayWithinMonth( MonthTy month, DayTy day, YearTy year )
  128. {
  129.     if( day <= 0 || !AssertIndexOfMonth(month) ) 
  130.         return 0;
  131.     unsigned d = DaysInMonth[month-1];
  132.     if( LeapYear(year) && month == 2 )
  133.         d++;
  134.     return day <= d;
  135. }
  136.  
  137. // How many days are in the given YearTy year?
  138. DayTy TDate::DaysInYear( YearTy year )
  139. {
  140.     return LeapYear(year) ? 366 : 365;
  141. }
  142.  
  143. // Returns the number, 1-12, of the month named nameOfMonth.
  144. // Return 0 for no match.
  145. MonthTy TDate::IndexOfMonth( const char _BIDSFAR *nameOfMonth )
  146. {
  147.     return (MonthTy)(FindMatch( nameOfMonth, UCMonthNames, 12 )+1);
  148. }
  149.  
  150. /*
  151.  * Convert Gregorian calendar date to the corresponding Julian day
  152.  * number j.  Algorithm 199 from Communications of the ACM, Volume 6, No.
  153.  * 8, (Aug. 1963), p. 444.  Gregorian calendar started on Sep. 14, 1752.
  154.  * This function not valid before that.
  155.  * Returns 0 if the date is invalid.
  156.  */
  157.  
  158. JulTy TDate::Jday( MonthTy m, DayTy d, YearTy y )
  159. {
  160.     unsigned long c, ya;
  161.     if( y <= 99 )
  162.         y += 1900;
  163.     if( !DayWithinMonth(m, d, y) ) 
  164.         return (JulTy)0;
  165.  
  166.     if( m > 2 )          
  167.         m -= 3;
  168.     else 
  169.         {
  170.         m += 9;
  171.         y--;
  172.         } 
  173.  
  174.     c = y / 100;
  175.     ya = y - 100*c;
  176.     return ((146097L*c)>>2) + ((1461*ya)>>2) + (153*m + 2)/5 + d + 1721119L;
  177.  
  178. // Algorithm from K & R, "The C Programming Language", 1st ed.
  179. int TDate::LeapYear( YearTy year )
  180. {
  181.     return (year&3) == 0 && year%100 != 0 || year % 400 == 0;
  182. }
  183.  
  184. // Returns a string name for the month number.
  185. // Return 0 if invalid month number.
  186. const char _BIDSFAR *TDate::MonthName( MonthTy monthNumber )
  187. {
  188.     return AssertIndexOfMonth(monthNumber) ? MonthNames[monthNumber-1] : 0;
  189. }
  190.  
  191. // Return index of case-insensitive match; -1 if no match.
  192. static int _BIDSNEARFUNC FindMatch( const char *str, const char**candidates, int icand )
  193. {
  194.     unsigned len = strlen(str);
  195.  
  196.     while(icand--)
  197.         {
  198.         if( strnicmp(str, candidates[icand], len) == 0)
  199.             break;
  200.         }
  201.     return icand;
  202. }
  203.  
  204. /****************************************************************
  205.  *                                                              *
  206.  *                      Member functions                        *
  207.  *                                                              *
  208.  ****************************************************************/
  209.  
  210. // Compare function:
  211. int TDate::CompareTo( const TDate _BIDSFAR &d ) const
  212. {
  213.     if( Julnum < d.Julnum )
  214.         return -1;
  215.     else if( Julnum > d.Julnum )
  216.         return 1;
  217.     else
  218.         return 0;
  219. }
  220.  
  221. DayTy TDate::Day() const
  222. {
  223.     return DayTy(Julnum - Jday( 12, 31, Year()-1 ));
  224. }
  225.  
  226. // Returns the day of the month of this TDate.
  227. DayTy TDate::DayOfMonth() const
  228. {
  229.     MonthTy m; DayTy d; YearTy y;
  230.     Mdy( m, d, y );
  231.     return d;
  232. }
  233.  
  234. // Return the number of the first day of a given month
  235. // Return 0 if "month" is outside of the range 1 through 12, inclusive.
  236. DayTy TDate::FirstDayOfMonth( MonthTy month ) const
  237. {
  238.     if ( !AssertIndexOfMonth(month) )
  239.         return 0;
  240.     unsigned firstDay = FirstDayOfEachMonth[month-1];
  241.     if (month > 2 && Leap())
  242.         firstDay++;
  243.     return firstDay;
  244. }
  245.  
  246. unsigned TDate::Hash() const
  247. {
  248.     return (unsigned)Julnum;
  249. }
  250.  
  251. /*
  252.  * Convert a Julian day number to its corresponding Gregorian calendar
  253.  * date.  Algorithm 199 from Communications of the ACM, Volume 6, No. 8,
  254.  * (Aug. 1963), p. 444.  Gregorian calendar started on Sep. 14, 1752.
  255.  * This function not valid before that.  
  256.  */
  257.  
  258. void _BIDSNEARFUNC TDate::Mdy( MonthTy _BIDSFAR & m, DayTy _BIDSFAR & D, YearTy _BIDSFAR & y ) const
  259. {
  260.     unsigned long d;
  261.     JulTy j = Julnum - 1721119L;
  262.     y = (YearTy) (((j<<2) - 1) / 146097L);
  263.     j = (j<<2) - 1 - 146097L*y;
  264.     d = (j>>2);
  265.     j = ((d<<2) + 3) / 1461;
  266.     d = (d<<2) + 3 - 1461*j;
  267.     d = (d + 4)>>2;
  268.     m = (MonthTy)(5*d - 3)/153;
  269.     d = 5*d - 3 - 153*m;
  270.     D = (DayTy)((d + 5)/5);
  271.     y = (YearTy)(100*y + j);
  272.  
  273.     if( m < 10 )
  274.         m += 3;
  275.     else 
  276.         {
  277.         m -= 9;
  278.         y++;
  279.         } 
  280.  
  281. TDate TDate::Max( const TDate _BIDSFAR & dt ) const
  282. {
  283.     return dt.Julnum > Julnum ? dt : *this;
  284. }
  285.  
  286. TDate TDate::Min( const TDate _BIDSFAR & dt ) const 
  287. {
  288.     return dt.Julnum < Julnum ? dt : *this;
  289. }
  290.  
  291. // Returns the month of this TDate.
  292. MonthTy TDate::Month() const
  293. {
  294.     MonthTy m; DayTy d; YearTy y;
  295.     Mdy(m, d, y);
  296.     return m;
  297. }
  298.  
  299. TDate TDate::Previous( const char _BIDSFAR *dayName) const
  300. {
  301.     return Previous( DayOfWeek(dayName) );
  302. }
  303.  
  304. TDate TDate::Previous( DayTy desiredDayOfWeek ) const
  305. {
  306.     //    Renumber the desired and current day of week to start at 0 (Monday)
  307.     //    and end at 6 (Sunday).
  308.  
  309.     desiredDayOfWeek--;
  310.     DayTy thisDayOfWeek = WeekDay() - 1;
  311.     JulTy j = Julnum;
  312.  
  313.     //    Have to determine how many days difference from current day back to
  314.     //    desired, if any.  Special calculation under the 'if' statement to
  315.     //    effect the wraparound counting from Monday (0) back to Sunday (6).
  316.  
  317.     if( desiredDayOfWeek > thisDayOfWeek )
  318.         thisDayOfWeek += 7 - desiredDayOfWeek;
  319.     else
  320.         thisDayOfWeek -= desiredDayOfWeek;
  321.     j -= thisDayOfWeek; // Adjust j to set it at the desired day of week.
  322.     return  TDate(j);
  323. }
  324.  
  325. DayTy TDate::WeekDay() const
  326. {
  327.     return DayTy(((((Julnum+1)%7)+6)%7)+1);
  328. }
  329.  
  330. // Returns the year of this TDate.
  331. YearTy TDate::Year() const
  332. {
  333.     MonthTy m; DayTy d; YearTy y;
  334.     Mdy(m, d, y);
  335.     return y;
  336. }
  337.  
  338.