home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / listings / v_08_06 / 8n06060a < prev    next >
Text File  |  1989-11-10  |  14KB  |  571 lines

  1. /*
  2. **  dates.cpp -- date object methods
  3. */
  4.  
  5.  
  6. #include    <stdio.h>
  7. #include    <stdlib.h>
  8. #include    <math.h>
  9. #include    <dos.h>
  10. #include    <string.h>
  11. #include    "dates.hpp"
  12.  
  13.  
  14. static char ShortMonths[MAXMONTH][4] = {
  15.     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  16.     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 
  17. };
  18.  
  19. static char LongMonths[MAXMONTH][10] = {
  20.     "January",  "February",  "March",
  21.     "April",    "May",       "June",
  22.     "July",     "August",    "September",
  23.     "October",  "November",  "December"
  24. };
  25.  
  26. static unsigned MonthDays[MAXMONTH] = {
  27.     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  28. };
  29.  
  30. static char ShortWeekDays[MAXWEEKDAY+1][4] = {
  31.     "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
  32. };
  33.  
  34. static char LongWeekDays[MAXWEEKDAY+1][10] = {
  35.     "Sunday", "Monday", "Tuesday", "Wednesday",
  36.     "Thursday", "Friday", "Saturday"
  37. };
  38.  
  39. static const char *CurrentDateFormat = "m-dd-yyy";
  40. static int OnHeap = 0;        /* CurrentDateFormat doesn't point to heap */
  41.  
  42.  
  43. /*
  44. **  ChangeDefaultDateFormat -- change the format string used to
  45. **  initialize the DateFormatPtr.  Return 1 on success or 0 if
  46. **  memory could not be allocated for the new string.
  47. */
  48.  
  49. int ChangeDefaultDateFormat(const char *s)
  50. {
  51. char *t;
  52.     
  53.     if ((t = strdup(s)) == NULL)
  54.     return (0);            /* return failure */
  55.     if (OnHeap)
  56.         free(CurrentDateFormat);    /* release old string */
  57.     else
  58.          OnHeap = 1;            /* now we're allocating on heap */
  59.     CurrentDateFormat = t;        /* point to new string */    
  60.     return (1);
  61. }
  62.  
  63.  
  64. /*
  65. **  IsLeap -- return non-zero if Year is a leap year, 0 otherwise.
  66. */
  67.  
  68. static int IsLeap(int Year)
  69. {
  70.   return (Year % 4 == 0) && (Year % 4000 != 0) &&
  71.             ((Year % 100 != 0) || (Year % 400 == 0));
  72. }
  73.  
  74.  
  75. /*
  76. **  DaysInMonth -- return the number of days in Month.  Year
  77. **  is passed to test for a leap year if the month is February.
  78. */
  79.  
  80. static unsigned char DaysInMonth(unsigned char Month, int Year)
  81. {
  82.     if ((Month == 2) && IsLeap(Year))
  83.         return (29);
  84.     else
  85.         return (MonthDays[Month - 1]);
  86. }
  87.  
  88.  
  89. /*
  90. **  CheckForValidDate -- check that Day, Month and Year
  91. **  represent a valid date.  Return non-zero if so, 0 otherwise.
  92. */
  93.  
  94. static int CheckForValidDate(unsigned char Day, unsigned char Month, int Year)
  95. {
  96.     if ((Year < MINYEAR) || (Year > MAXYEAR) || (Year == 0) ||
  97.     (Month < MINMONTH) || (Month > MAXMONTH))
  98.     return (0);
  99.     return (Day <= DaysInMonth(Month, Year));
  100. }
  101.  
  102.  
  103. /*
  104. **  JulianToDayOfWeek -- given a Julian date, return the
  105. **  day of the week for that date, 0 being Sunday, 6 being
  106. **  Saturday.
  107. */
  108.  
  109. static unsigned char JulianToDayOfWeek(long Jul)
  110. {
  111.     return ((Jul + 1) % 7);
  112. }
  113.  
  114.  
  115. /*
  116. **  DMYtoJulian -- after validating the date passed as a Day,
  117. **  Month and Year, convert it to a Julian date.
  118. **
  119. **  The algorithm shown here is swiped directly from "Numerical
  120. **  Recipes in C" by Press, Flannery, Teukolsky and Vettering, p. 10.
  121. */
  122.  
  123. #define    IGREG    (15+31L*(10+12L*1582))
  124.  
  125. static long DMYtoJulian(unsigned char Day, unsigned char Month, int Year)
  126. {
  127. unsigned long Ja, Jm, Jy, Jul;
  128.  
  129.     if (!CheckForValidDate(Day, Month, Year))
  130.     return (BADDATE);
  131.     if (Year < 0)
  132.     Year++;
  133.     if (Month > 2)
  134.     {
  135.     Jy = Year;
  136.     Jm = Month + 1;
  137.     }
  138.     else
  139.     {
  140.     Jy = Year - 1;
  141.     Jm = Month + 13;
  142.     }
  143.     Jul = (long) (floor(365.25*Jy) + floor(30.6001*Jm) + Day + 1720995);
  144.     if (Day + 31L*(Month + 12L*Year) >= IGREG)
  145.     {
  146.     Ja = 0.01*Jy;
  147.     Jul += 2 - Ja + (int) (0.25*Ja);
  148.     }
  149.     return (Jul);
  150. }
  151.  
  152.  
  153. /*  JulianToDMY -- convert a Julian date to the appropriate
  154. **  Day, Month and Year.
  155. **
  156. **  Also swiped from "Numerical Recipes".
  157. */
  158.  
  159. #define    GREGOR    2299161
  160.  
  161. static void JulianToDMY(long Jul, unsigned char *Day, unsigned char *Month, 
  162.     int *Year)
  163. {
  164. long Ja, JAlpha, Jb, Jc, Jd, Je;
  165.  
  166.     if ((Jul != BADDATE) && (Jul >= MINDATE) && (Jul <= MAXDATE))
  167.     {
  168.     if (Jul >= GREGOR)
  169.     {
  170.         JAlpha = ((double) (Jul - 1867216) - 0.25)/36524.25;
  171.         Ja = Jul + 1 + JAlpha - (long) (0.25*JAlpha);
  172.     }
  173.     else
  174.         Ja = Jul;
  175.     Jb = Ja + 1524;
  176.     Jc = 6680.0 + ((double) (Jb - 2439870) - 122.1)/365.25;
  177.     Jd = 365*Jc + (0.25*Jc);
  178.     Je = (Jb - Jd)/30.6001;
  179.     *Day = Jb - Jd - (int) (30.6001*Je);
  180.     *Month = Je - 1;
  181.     if (*Month > 12)
  182.         *Month -= 12;
  183.     *Year = Jc - 4715;
  184.     if (*Month > 2)
  185.         --(*Year);
  186.     if (Year <= 0)
  187.         --(*Year);
  188.     }
  189. }
  190.  
  191.  
  192. /*
  193. **  ChangeDate -- change a date to reflect the new date passed
  194. **  in the arguments.  If the requested date is not a legal date,
  195. **  return a value of 0 without making any changes.  If legal,
  196. **  return 1.  The date format string is not affected.
  197. */
  198.  
  199. int DateObject::ChangeDate(unsigned char NDay, unsigned char NMonth, int NYear)
  200. {
  201. long t;
  202.  
  203.     t = DMYtoJulian(NDay, NMonth, NYear);
  204.     if (t != BADDATE)
  205.     {
  206.         Julian = t;
  207.         Day = NDay;
  208.         Month = NMonth;
  209.         Year = NYear;
  210.         DayOfWeek = JulianToDayOfWeek(Julian);
  211.         return (1);
  212.     }
  213.     return (0);
  214. }
  215.  
  216.  
  217. /*
  218. **  DateObject -- constructor if no args given.  Just initialize
  219. **  to todays date.
  220. */
  221.  
  222. DateObject::DateObject(void)
  223. {
  224. union REGS regs;
  225.  
  226.     regs.x.ax = 0x2a00;        /* DOS get date service */
  227.     intdos(®s, ®s);
  228.     Day = regs.h.dl;
  229.     Month = regs.h.dh;
  230.     Year = regs.x.cx;
  231.     DayOfWeek = regs.h.al;
  232.     Julian = DMYtoJulian(Day, Month, Year);
  233.     DateFormatPtr = strdup(CurrentDateFormat);
  234.     if (DateFormatPtr == NULL)
  235.         Julian = BADDATE;
  236. }
  237.  
  238.  
  239. /*
  240. **  DateObject -- copy initializer constructor
  241. */
  242.  
  243. DateObject::DateObject(DateObject &OtherDate)
  244. {
  245.     Day = OtherDate.Day;
  246.     Month = OtherDate.Month;
  247.     Year = OtherDate.Year;
  248.     DayOfWeek = OtherDate.DayOfWeek;
  249.     Julian = OtherDate.Julian;
  250.     DateFormatPtr = strdup(OtherDate.DateFormatPtr);
  251.     if (DateFormatPtr == NULL)
  252.         Julian = BADDATE;
  253. }
  254.  
  255.  
  256. /*
  257. **  DateObject -- constructor when day, month and year initializers
  258. **  are provided.  The default format string is used.
  259. */
  260.  
  261. DateObject::DateObject(unsigned char InitDay, unsigned char InitMonth,
  262.     int InitYear)
  263. {
  264.     ChangeDate(InitDay, InitMonth, InitYear);
  265.     if (Julian != BADDATE)
  266.     {
  267.     DateFormatPtr = strdup(CurrentDateFormat);
  268.     if (DateFormatPtr == NULL)
  269.         Julian = BADDATE;
  270.     }
  271. }
  272.  
  273.  
  274. /*
  275. **  DateObject -- constructor used when day, month, year and a
  276. **  format string initializer are provided.
  277. */
  278.  
  279. DateObject::DateObject(unsigned char InitDay, unsigned char InitMonth,
  280.     int InitYear, const char *FormatStr)
  281. {
  282.     ChangeDate(InitDay, InitMonth, InitYear);
  283.     if (Julian != BADDATE)
  284.     {
  285.     DateFormatPtr = strdup(FormatStr);
  286.     if (DateFormatPtr == NULL)
  287.         Julian = BADDATE;
  288.     }
  289. }
  290.  
  291.  
  292. /*
  293. **  = -- assignment operator.
  294. */
  295.  
  296. DateObject DateObject::operator = (DateObject &d)
  297. {
  298.     Day = d.Day;
  299.     Month = d.Month;
  300.     Year = d.Year;
  301.     Julian = d.Julian;
  302.     DayOfWeek = d.DayOfWeek;
  303.     DateFormatPtr = strdup(d.DateFormatPtr);
  304.     if (DateFormatPtr == NULL)
  305.         Julian = BADDATE;
  306.     return (*this);
  307. }
  308.  
  309.  
  310. /*
  311. **  + -- addition operator.  This is the fundamental operator
  312. **  function.  All other arithmetic operators returning
  313. **  DateObjects call this function.  The resulting DateObject 
  314. **  copies that format string of the DateObject argument.
  315. */
  316.  
  317. DateObject operator + (DateObject &d, long x)
  318. {
  319. DateObject sum;
  320.  
  321.     sum.Julian = d.Julian + x;
  322.     if ((sum.Julian < MINDATE) || (sum.Julian > MAXDATE))
  323.         sum.Julian = BADDATE;
  324.     else
  325.     {
  326.     JulianToDMY(sum.Julian, &(sum.Day), &(sum.Month), &(sum.Year));
  327.     sum.DayOfWeek = JulianToDayOfWeek(sum.Julian);
  328.     sum.DateFormatPtr = strdup(d.DateFormatPtr);
  329.     if (sum.DateFormatPtr == NULL)
  330.         sum.Julian = BADDATE;
  331.     }
  332.     return (sum);
  333. }
  334.  
  335. DateObject operator + (long x, DateObject &d)
  336. {
  337.     return (d + x);
  338. }
  339.  
  340. DateObject DateObject::operator - (long x)
  341. {
  342.     return (*this + (-x));
  343. }
  344.  
  345. DateObject DateObject::operator ++ (void)
  346. {
  347.     return ((*this) = (*this) + 1L);
  348. }
  349.  
  350. DateObject DateObject::operator -- (void)
  351. {
  352.     return ((*this) = (*this) + (-1L));
  353. }
  354.  
  355. DateObject DateObject::operator += (long x)
  356. {
  357.     return ((*this) = (*this) + x);
  358. }
  359.  
  360. DateObject DateObject::operator -= (long x)
  361. {
  362.     return ((*this) = (*this) + (-x));
  363. }
  364.  
  365.  
  366. /*
  367. **  GetFormat -- return a dynamically allocated
  368. **  copy of a date's format string
  369. */
  370.  
  371. const char * DateObject::GetFormat()
  372. {
  373.     return (strdup(DateFormatPtr));
  374. }
  375.  
  376.  
  377. /*
  378. **  ChangeFormat -- change the date's format string
  379. */
  380.  
  381. void DateObject::ChangeFormat (const char *s)
  382. {
  383. char *t;
  384.  
  385.     if ((t = strdup(s)) != NULL)
  386.     {
  387.         free(DateFormatPtr);
  388.         DateFormatPtr = t;
  389.     }
  390. }
  391.  
  392.  
  393. /*
  394. **  DateObject destructor -- simply release the string space occupied
  395. **  by *DateFormatPtr.  We do not have to check that the pointer is
  396. **  non-NULL, since free accepts NULL pointers and does nothing.
  397. */
  398.  
  399. DateObject::~DateObject(void)
  400. {
  401.     free(DateFormatPtr);
  402. }
  403.  
  404.  
  405. /*
  406. **  CountLetters -- count the run of letters in s matching
  407. **  the first letter in s.  Advance s to point to the next
  408. **  letter that does not match the first character. Return
  409. **  the count.  This is a helper function for DateToString.
  410. */
  411.  
  412. static int CountLetters(char **s)
  413. {
  414. int n, c;
  415.  
  416.     for (n = 0, c = **s; **s == c; n++, (*s)++)
  417.         ;
  418.     return (n);
  419. }
  420.  
  421. /*
  422. **  DateToString -- convert Self to a printable string
  423. **  based on the contents of the format string, DateFormatPtr.
  424. **
  425. **  The format string is interpreted somewhat like date
  426. **  formats in Microsoft Excel.  Most characters are passed
  427. **  through unchanged into the result string.  However,
  428. **  certain special characters are replaced.  The special
  429. **  characters and their replacements are:
  430. **
  431. **  d      Replaced by a one or two digit day number, i.e. '2'.
  432. **  dd     Replaced by a two digit day number with a leading
  433. **         0 if the day is less than 10, i.e. '02'.
  434. **  ddd    Replaced by a 3 character name for the day of the
  435. **         week, i.e. 'Wed'.
  436. **  dddd   Replaced by the complete word for the day of the
  437. **         week, i.e. 'Wednesday'.
  438. **  m      Replaced by a one or two digit month number, i.e. '2'.
  439. **  mm     Replaced by a two digit month number with a leading
  440. **         0 if the month is less than October, i.e. '02'.
  441. **  mmm    Replaced by a 3 character name for the month, i.e. 'Jan'.
  442. **  mmmm   Replaced by the complete word for the month, i.e. 'January'.
  443. **  yy     Replaced by a 2 digit year Modulo 100, i.e. '89'.
  444. **  yyy    Replaced by a 4 digit year number, i.e. '1989'.  BC
  445. **         dates are preceded by a '-'.
  446. **  yyyy   Replaced by a 4 digit string representing the absolute
  447. **         value of the year number followed by ' AD' or ' BC',
  448. **         as appropriate, i.e. '1989 AD'.
  449. **  \      Skipped and places the next character into the string
  450. **         without interpration.  Allows you to put words in the
  451. **         output string, i.e. 'To\da\y is ...' will generate
  452. **         a string of the form 'Today is ...'.
  453. */
  454.  
  455. char *DateObject::DateToString(void)
  456. {
  457. int c, Len, Pos, Count;
  458. char *str,     /* a dynamically allocated work string */
  459.      *sptr,     /* a moving pointer to the next avail char in str */
  460.      *ret,     /* pointer to the trimmed string returned by the function */
  461.      *fptr;    /* a pointer into the format string */
  462.  
  463.     if (Julian == BADDATE)
  464.         return (strdup("Bad Date"));
  465.         
  466.     Len = strlen(DateFormatPtr);
  467.     Pos = 0;
  468.     sptr = str = calloc((MAXDATESTRLEN+1), sizeof(char));
  469.     fptr = DateFormatPtr;
  470.     while (*fptr)
  471.     {
  472.         switch (*fptr)
  473.         {
  474.             case 'd' :
  475.             case 'D' :
  476.             Count = CountLetters(&fptr);
  477.             if (Count >= 4)
  478.             {
  479.                 strcat(sptr, LongWeekDays[DayOfWeek]);
  480.                 while (*sptr)
  481.                     sptr++;
  482.             }
  483.             else if (Count == 3)
  484.             {
  485.                 strcat(sptr, ShortWeekDays[DayOfWeek]);
  486.                 while (*sptr)
  487.                     sptr++;
  488.             }
  489.             else
  490.             {
  491.                 if ((Count == 2) && (Day < 10))
  492.                     strcat(sptr++, "0");
  493.                 itoa(Day, sptr, 10);
  494.                 while (*sptr)
  495.                     sptr++;
  496.             }
  497.             break;
  498.             case 'm' :
  499.             case 'M' :
  500.                     Count = CountLetters(&fptr);
  501.                     if (Count >= 4)
  502.                     {
  503.                         strcat(sptr, LongMonths[Month-1]);
  504.                         while (*sptr)
  505.                             sptr++;
  506.                     }
  507.                     else if (Count == 3)
  508.                     {
  509.                         strcat(sptr, ShortMonths[Month-1]);
  510.                         while (*sptr)
  511.                             sptr++;
  512.                     }
  513.                     else
  514.                     {
  515.                 if ((Count == 2) && (Month < 10))
  516.                     strcat(sptr++, "0");
  517.                 itoa(Month, sptr, 10);
  518.                 while (*sptr)
  519.                     sptr++;
  520.                     }
  521.                     break;
  522.             case 'y' :
  523.             case 'Y' :
  524.                     Count = CountLetters(&fptr);
  525.                     if (Count >= 4)
  526.                     {
  527.                         itoa(Year, sptr, 10);
  528.                         if (Year < 0)    /* overwrite minus sign */
  529.                             strcpy(sptr, sptr + 1);
  530.                         while (*sptr)
  531.                             sptr++;
  532.                         if (Year > 0)
  533.                             strcat(sptr, " AD");
  534.                         else
  535.                             strcat(sptr, " BC");
  536.                         while (*sptr)
  537.                             sptr++;
  538.                     }
  539.                     else
  540.                     {
  541.                         if (Count == 2)
  542.                         {
  543.                             if ((Year % 100) < 10)
  544.                                 strcat(sptr++, "0");
  545.                             itoa((Year % 100), sptr, 10);
  546.                             while (*sptr)
  547.                                 sptr++;
  548.                         }
  549.                         else
  550.                         {
  551.                             itoa(Year, sptr, 10);
  552.                             while (*sptr)
  553.                                 sptr++;
  554.                         }
  555.                     }
  556.                     break;
  557.             case '\\' :
  558.                     fptr++;        /* skip over the \, and ... */
  559.             default :
  560.                     *sptr = *fptr;    /* copy the character */
  561.                     sptr++;        /* point to next empty char */
  562.                     fptr++;
  563.                     break;
  564.         }
  565.     }
  566.     ret = strdup(str);            /* make a "trimmed" copy */
  567.     free(str);                /* free our work string */
  568.     return (ret);            /* return the "duped" string */
  569. }
  570.  
  571.