home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / snip9707.zip / DATE.CPP < prev    next >
C/C++ Source or Header  |  1997-07-05  |  13KB  |  587 lines

  1. // +++Date last modified: 05-Jul-1997
  2.  
  3. /*
  4.  * This file is part of PB-Lib C/C++ Library
  5.  *
  6.  * Copyright (c) 1995, 1996 Branislav L. Slantchev
  7.  * A Product of Silicon Creations, Inc.
  8.  *
  9.  * This class is hereby donated to the SNIPPETS collection (maintained
  10.  * by Bob Stout). You are granted the right to use the code contained
  11.  * herein free of charge as long as you keep this copyright notice intact.
  12.  *
  13.  * Contact: 73023.262@compuserve.com
  14. */
  15. #include "date.hpp"
  16.  
  17. const int             zDate::ReformYear      = 1582;
  18. const ulong           zDate::ReformDayNumber = 577737L;
  19. const zDate::month    zDate::ReformMonth     = zDate::oct;
  20.         zDate::week_day zDate::BeginDSTDay     = zDate::sun;
  21.         zDate::month    zDate::BeginDSTMonth   = zDate::apr;
  22.         zDate::week_day zDate::EndDSTDay       = zDate::sun;
  23.         zDate::month    zDate::EndDSTMonth     = zDate::oct;
  24.  
  25. zDate::zDate()
  26. {
  27.       Set(jan, 1, 1);
  28. }
  29.  
  30. zDate::zDate(month aMonth, int aDay, int aYear)
  31. {
  32.       Set(aMonth, aDay, aYear);
  33. }
  34.  
  35. zDate::zDate(int dayOfYear, int year)
  36. {
  37.       m_day   = 31;
  38.       m_month = dec;
  39.       m_year  = year - 1;
  40.       m_dayno = MakeDayNumber();
  41.       FromDayNumber(m_dayno + dayOfYear);
  42. }
  43.  
  44. zDate
  45. zDate::Set(month aMonth, int aDay, int aYear)
  46. {
  47.       m_month = aMonth;
  48.       m_day   = aDay;
  49.       m_year  = aYear;
  50.       m_dayno = MakeDayNumber();
  51.  
  52.       return *this;
  53. }
  54.  
  55. zDate::zDate(const zDate &aDate)
  56. {
  57.       m_month = aDate.m_month;
  58.       m_day   = aDate.m_day;
  59.       m_year  = aDate.m_year;
  60.       m_dayno = aDate.m_dayno;
  61. }
  62.  
  63. zDate::zDate(ulong nJulian)
  64. {
  65.       FromDayNumber(nJulian);
  66. }
  67.  
  68. zDate::zDate(const struct tm *tmDate)
  69. {
  70.       m_month = (month)(tmDate->tm_mon + 1);
  71.       m_day   = tmDate->tm_mday;
  72.       m_year  = tmDate->tm_year + 1900;
  73.       m_dayno = MakeDayNumber();
  74. }
  75.  
  76. zDate
  77. zDate::Today()
  78. {
  79.       time_t     secs_now = time(0);
  80.       struct tm *time_now = localtime(&secs_now);
  81.       zDate      today(time_now);
  82.  
  83.       return today;
  84. }
  85.  
  86. Boolean
  87. zDate::operator!=(const zDate &aDate) const
  88. {
  89.       return Boolean(m_dayno != aDate.m_dayno);
  90. }
  91.  
  92. Boolean
  93. zDate::operator==(const zDate &aDate) const
  94. {
  95.       return Boolean(m_dayno == aDate.m_dayno);
  96. }
  97.  
  98. Boolean
  99. zDate::operator<(const zDate &aDate) const
  100. {
  101.       return Boolean(m_dayno < aDate.m_dayno);
  102. }
  103.  
  104. Boolean
  105. zDate::operator<=(const zDate &aDate) const
  106. {
  107.       return Boolean(m_dayno <= aDate.m_dayno);
  108. }
  109.  
  110. Boolean
  111. zDate::operator>(const zDate &aDate) const
  112. {
  113.       return Boolean(m_dayno > aDate.m_dayno);
  114. }
  115.  
  116. Boolean
  117. zDate::operator>=(const zDate &aDate) const
  118. {
  119.       return Boolean(m_dayno >= aDate.m_dayno);
  120. }
  121.  
  122. zDate&
  123. zDate::operator=(const zDate &aDate)
  124. {
  125.       m_day   = aDate.m_day;
  126.       m_month = aDate.m_month;
  127.       m_year  = aDate.m_year;
  128.       m_dayno = aDate.m_dayno;
  129.  
  130.       return *this;
  131. }
  132.  
  133. int
  134. zDate::DaysInMonth(month aMonth, int aYear)
  135. {
  136.       static const int days[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
  137.  
  138.       if( aYear == ReformYear && aMonth == ReformMonth ) return 21;
  139.       return days[aMonth] + (feb == aMonth && IsLeapYear(aYear));
  140. }
  141.  
  142. int
  143. zDate::DayOfYear() const
  144. {
  145.       zDate first(jan, 1, m_year);
  146.  
  147.       return 1 + (int)(m_dayno - first.m_dayno);
  148. }
  149.  
  150. int
  151. zDate::DaysInYear(int year)
  152. {
  153.       int days = 365 + IsLeapYear(year);
  154.       // 10 days cancelled by the reform of pope Gregor XIII
  155.       if( year == ReformYear ) return days - 10;
  156.       else return days;
  157. }
  158.  
  159. Boolean
  160. zDate::IsLeapYear(int year)
  161. {
  162.       if( year % 4 ) return False;  // if not divisible by 4, not leap
  163.       if( year < ReformYear ) return True; // before this year, all were leap
  164.       if( year % 100 ) return True; // by 4, but not by 100 is leap
  165.       if( year % 400 ) return False;      // not by 100 and not by 400 not leap
  166.       return True;
  167. }
  168.  
  169. Boolean
  170. zDate::IsValid(month aMonth, int aDay, int aYear)
  171. {
  172.       return Boolean(
  173.                aYear  > 0
  174.             && aMonth >= jan && aMonth <= dec
  175.             && aDay   >  0   && aDay   <= DaysInMonth(aMonth, aYear)
  176.       );
  177. }
  178.  
  179. int
  180. zDate::Age(const zDate &birthday) const
  181. {
  182.       int age = m_year - birthday.m_year - 1;
  183.  
  184.       if( m_month > birthday.m_month ) age++;
  185.       else if( m_month == birthday.m_month && m_day >= birthday.m_day ) age++;
  186.  
  187.       return age;
  188. }
  189.  
  190. zDate
  191. zDate::operator+(int nDays) const
  192. {
  193.       return zDate(m_dayno + (long)nDays);
  194. }
  195.  
  196. zDate
  197. zDate::operator+(long nDays) const
  198. {
  199.       return zDate(m_dayno + nDays);
  200. }
  201.  
  202. zDate
  203. zDate::operator-(int nDays) const
  204. {
  205.       return zDate(m_dayno - (long)nDays);
  206. }
  207.  
  208. zDate
  209. zDate::operator-(long nDays) const
  210. {
  211.       return zDate(m_dayno - nDays);
  212. }
  213.  
  214. long
  215. zDate::operator-(const zDate &aDate) const
  216. {
  217.       return (long)(m_dayno - aDate.m_dayno);
  218. }
  219.  
  220. zDate&
  221. zDate::operator+=(int nDays)
  222. {
  223.       FromDayNumber(m_dayno + (long)nDays);
  224.       return *this;
  225. }
  226.  
  227. zDate&
  228. zDate::operator+=(long nDays)
  229. {
  230.       FromDayNumber(m_dayno + nDays);
  231.       return *this;
  232. }
  233.  
  234. zDate
  235. zDate::operator++()
  236. {
  237.       FromDayNumber(m_dayno + 1L);
  238.       return *this;
  239. }
  240.  
  241. zDate
  242. zDate::operator++(int)
  243. {
  244.       zDate date(*this);
  245.  
  246.       FromDayNumber(m_dayno + 1L);
  247.       return date;
  248. }
  249.  
  250. zDate
  251. zDate::operator--()
  252. {
  253.       FromDayNumber(m_dayno - 1L);
  254.       return *this;
  255. }
  256.  
  257. /* postfix */
  258. zDate
  259. zDate::operator--(int)
  260. {
  261.       zDate date(*this);
  262.  
  263.       FromDayNumber(m_dayno - 1L);
  264.       return date;
  265. }
  266.  
  267. zDate&
  268. zDate::operator-=(int nDays)
  269. {
  270.       FromDayNumber(m_dayno - (long)nDays);
  271.       return *this;
  272. }
  273.  
  274. zDate&
  275. zDate::operator-=(long nDays)
  276. {
  277.       FromDayNumber(m_dayno - nDays);
  278.       return *this;
  279. }
  280.  
  281. int
  282. zDate::WeekOfYear() const
  283. {
  284.       zDate first(jan, 1, m_year);
  285.  
  286.       return 1 + int((m_dayno - first.m_dayno + 1) / 7);
  287. }
  288.  
  289. int
  290. zDate::WeekOfMonth() const
  291. {
  292.       int abs_mday = m_day + zDate(m_month, 1, m_year).DayOfWeek() - 1;
  293.  
  294.       return 1 + ((abs_mday - DayOfWeek()) / 7);
  295. }
  296.  
  297. int
  298. zDate::WeeksInYear(int year)
  299. {
  300.       return zDate(dec, 31, year).WeekOfYear();
  301. }
  302.  
  303. zDate
  304. zDate::AddWeeks(int nWeeks) const
  305. {
  306.       zDate date(*this);
  307.  
  308.       date += (long)nWeeks * 7L;
  309.       return date;
  310. }
  311.  
  312. zDate
  313. zDate::AddYears(int nYears) const
  314. {
  315.       zDate date(*this);
  316.       int   delta = nYears > 0 ? -1 : 1;
  317.       int   year = m_year;
  318.       long  days = 0;
  319.  
  320.       while( nYears ){
  321.             nYears += delta;
  322.             year   -= delta;
  323.             days   += DaysInYear(year);
  324.       }
  325.       date += (-delta * days);
  326.       return date;
  327. }
  328.  
  329. zDate::operator long() const
  330. {
  331.       return DayNumber();
  332. }
  333.  
  334. /*
  335.  * arguably useful routine, you can access the date object as an array,
  336.  * with [0] == day (1..31), [1] == month (1..12), [2] == year - 1900
  337. */
  338. char
  339. zDate::operator[](int index) const
  340. {
  341.       switch( index ){
  342.             case 0 : return m_day;
  343.             case 1 : return m_month;
  344.             case 2 : return m_year - 1900;
  345.             default: return -1;
  346.       }
  347. }
  348.  
  349. int
  350. zDate::DaysInMonth() const
  351. {
  352.       return DaysInMonth(m_month, m_year);
  353. }
  354.  
  355. int
  356. zDate::DaysInYear() const
  357. {
  358.       return DaysInYear(m_year);
  359. }
  360.  
  361. int
  362. zDate::WeeksInYear() const
  363. {
  364.       return WeeksInYear(m_year);
  365. }
  366.  
  367. Boolean
  368. zDate::IsValid() const
  369. {
  370.       return IsValid(m_month, m_day, m_year);
  371. }
  372.  
  373. Boolean
  374. zDate::IsLeapYear() const
  375. {
  376.       return IsLeapYear(m_year);
  377. }
  378.  
  379. ulong
  380. zDate::MakeDayNumber() const
  381. {
  382.       long days;
  383.       long year  = (long)m_year - 1L;
  384.  
  385.       // get all days plus all leap years, minus non-leap years
  386.       days = year * 365L + year / 4L - year / 100L + year / 400L;
  387.       // the years before 1582 were all leap if divisible by 4
  388.       if( year > ReformYear ) days += 12;
  389.       else{
  390.             days += year / 100L;
  391.             days -= year / 400L;
  392.       }
  393.       // get the days for the month up to the current one
  394.       for( int i = jan; i < m_month; ++i )
  395.             days += DaysInMonth((month)i, m_year);
  396.       // now add the current days of the month
  397.       days += m_day;
  398.       // now adjust for the 10 missing days (Oct 4 - Oct 15, 1582)
  399.       if( days > ReformDayNumber ) days -= 10L;
  400.       // we have the current day number now
  401.       return days;
  402. }
  403.  
  404. zDate::week_day
  405. zDate::DayOfWeek() const
  406. {
  407.       const week_day wdays[7] = {sun,mon,tue,wed,thu,fri,sat};
  408.  
  409.       return wdays[(int)(((m_dayno % 7) + 5) % 7)];
  410. }
  411.  
  412. void
  413. zDate::FromDayNumber(ulong dayno)
  414. {
  415.       m_dayno = dayno;
  416.  
  417.       if( dayno > ReformDayNumber ) dayno += 10L;
  418.  
  419.       m_year = (int)(dayno / 365);
  420.       m_day = (int)(dayno % 365L);
  421.  
  422.       if( m_year < 1700 ) m_day -= (m_year / 4);
  423.       else{
  424.             m_day -= (m_year / 4);
  425.             m_day += (m_year / 100);
  426.             m_day -= (m_year / 400);
  427.             m_day -= 12;
  428.       }
  429.  
  430.       while( m_day <= 0 ){
  431.             m_day += (365 + IsLeapYear(m_year));
  432.             m_year--;
  433.       }
  434.  
  435.       // m_year is the number of elapsed years, add 1 to get current
  436.       m_year += 1;
  437.  
  438.       // figure out the month and current day too
  439.       for( m_month = jan; m_month <= dec; m_month = (month)(m_month + 1) ){
  440.             int days = DaysInMonth(m_month, m_year);
  441.  
  442.             if( m_day <= days ) break;
  443.             else m_day -= days;
  444.       }
  445. }
  446.  
  447. Boolean
  448. zDate::IsDST() const
  449. {
  450.       return IsDST(*this);
  451. }
  452.  
  453. /*
  454.  * DST (Daylight Savings Time) starts at 2:00a on the first Sunday of
  455.  * April and ends at 2:00a on the last Sunday of October (US rules)
  456. */
  457.  
  458. zDate
  459. zDate::BeginDST(int year)
  460. {
  461.       zDate date(BeginDSTMonth, 1, year);
  462.  
  463.       while( BeginDSTDay != date.DayOfWeek() ) date++;
  464.       return date;
  465. }
  466.  
  467. zDate
  468. zDate::EndDST(int year)
  469. {
  470.       zDate date(EndDSTMonth, 31, year);
  471.  
  472.       while( EndDSTDay != date.DayOfWeek() ) date--;
  473.       return date;
  474. }
  475.  
  476. zDate
  477. zDate::BeginDST() const
  478. {
  479.       return BeginDST(m_year);
  480. }
  481.  
  482. zDate
  483. zDate::EndDST() const
  484. {
  485.       return EndDST(m_year);
  486. }
  487.  
  488. Boolean
  489. zDate::IsDST(const zDate &date)
  490. {
  491.       return Boolean( date >= date.BeginDST() && date <= date.EndDST() );
  492. }
  493.  
  494. zDate::moon_phase
  495. zDate::MoonPhase() const
  496. {
  497.       return MoonPhase(*this);
  498. }
  499.  
  500. zDate::moon_phase
  501. zDate::MoonPhase(const zDate &date)
  502. {
  503.       ulong phase = date.m_dayno;
  504.  
  505.       phase *= 9693L;
  506.       phase /= 35780L;
  507.       phase -= 4L;
  508.       phase %= 8L;
  509.  
  510.       return (moon_phase)phase;
  511. }
  512.  
  513. zDate
  514. zDate::Easter() const
  515. {
  516.       return Easter(m_year);
  517. }
  518.  
  519. /*
  520.  * i won't pretend i know exactly how this algorithm works.
  521.  * i used the one specified by the US Naval Observatory
  522. */
  523. zDate
  524. zDate::Easter(int year)
  525. {
  526.       int c, n, k, i, j, l, m, d;
  527.  
  528.       c = year / 100;
  529.       n = year - 19 * (year / 19);
  530.       k = (c - 17) / 25;
  531.       i = c - c / 4 - (c - k) / 3 + 19 * n + 15;
  532.       i = i - 30 * (i / 30);
  533.       i = i - (i / 28) * (1 - (i / 28) * (29 / (i + 1)) * ((21 - n) / 11));
  534.       j = year + year / 4 + i + 2 - c + c / 4;
  535.       j = j - 7 * (j / 7);
  536.       l = i - j;
  537.       m = 3 + (l + 40) / 44;
  538.       d = l + 28 - 31 * (m / 4);
  539.  
  540.       return zDate((month)m, d, year);
  541. }
  542.  
  543. /*
  544.  * this is a peculiar function - when months are added (or subtracted),
  545.  * what really happens is that the month number is modified (with the
  546.  * appropriate year adjustments too). if the resulting month/day combination
  547.  * is invalid (i.e. Apr 31), the days will spill into the next month (in
  548.  * the case with the example, the new date will be May 1). If we are
  549.  * subtracting months and we end up with an invalid date, the difference
  550.  * will be subtracted from the days (the month stays the same): this means
  551.  * that March 31, 1996 (leap year) minus 1 month = Feb 27, 1996
  552. */
  553. zDate
  554. zDate::AddMonths(int nMonths) const
  555. {
  556.       int mon  = m_month + nMonths;
  557.       int year = m_year;
  558.       int day  = m_day;
  559.       int mdays;
  560.  
  561.       while( mon < 1 ){
  562.             mon += 12;
  563.             year--;
  564.       }
  565.  
  566.       while( mon > 12 ){
  567.             mon -= 12;
  568.             year++;
  569.       }
  570.  
  571.       mdays = DaysInMonth((month)mon, year);
  572.  
  573.       if( day > mdays ){
  574.             if( nMonths < 0 ) day = mdays - (day - mdays);
  575.             else{
  576.                   day -= mdays;
  577.                   mon++;
  578.                   if( mon > 12 ){
  579.                         year++;
  580.                         mon = 1;
  581.                   }
  582.             }
  583.       }
  584.  
  585.       return zDate((month)mon, day, year);
  586. }
  587.