1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2010 - 2015, Göteborg Bit Factory.
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included
13 // in all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 // SOFTWARE.
22 //
23 // http://www.opensource.org/licenses/mit-license.php
24 //
25 ////////////////////////////////////////////////////////////////////////////////
26 
27 #include <cmake.h>
28 #include <iomanip>
29 #include <sstream>
30 #include <time.h>
31 #include <assert.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <ctype.h>
35 #include <Nibbler.h>
36 #include <Date.h>
37 #include <text.h>
38 #include <util.h>
39 
40 static const char* relatives[] =
41 {
42   "sunday",
43   "monday",
44   "tuesday",
45   "wednesday",
46   "thursday",
47   "friday",
48   "saturday",
49   "today",
50   "tomorrow",
51   "yesterday",
52   "eow",
53   "eoww",
54   "eocw",
55   "eocm",
56   "eom",
57   "eoq",
58   "eoy",
59   "sow",
60   "soww",
61   "socw",
62   "socm",
63   "som",
64   "soq",
65   "soy",
66   "goodfriday",
67   "easter",
68   "eastermonday",
69   "ascension",
70   "pentecost",
71   "midsommar",
72   "midsommarafton",
73   "now",
74   "later",
75   "someday",
76 };
77 
78 #define NUM_RELATIVES   (sizeof (relatives)   / sizeof (relatives[0]))
79 
80 static const std::string weekStart = "monday";
81 
82 ////////////////////////////////////////////////////////////////////////////////
83 // Defaults to "now".
Date()84 Date::Date ()
85 {
86   _t = time (NULL);
87 }
88 
89 ////////////////////////////////////////////////////////////////////////////////
Date(const time_t t)90 Date::Date (const time_t t)
91 {
92   _t = t;
93 }
94 
95 ////////////////////////////////////////////////////////////////////////////////
Date(const int m,const int d,const int y)96 Date::Date (const int m, const int d, const int y)
97 {
98   // Error if not valid.
99   struct tm t = {0};
100   t.tm_isdst = -1;   // Requests that mktime determine summer time effect.
101   t.tm_mday  = d;
102   t.tm_mon   = m - 1;
103   t.tm_year  = y - 1900;
104 
105   _t = mktime (&t);
106 }
107 
108 ////////////////////////////////////////////////////////////////////////////////
Date(const int m,const int d,const int y,const int hr,const int mi,const int se)109 Date::Date (const int m,  const int d,  const int y,
110             const int hr, const int mi, const int se)
111 {
112   // Error if not valid.
113   struct tm t = {0};
114   t.tm_isdst = -1;   // Requests that mktime determine summer time effect.
115   t.tm_mday  = d;
116   t.tm_mon   = m - 1;
117   t.tm_year  = y - 1900;
118   t.tm_hour  = hr;
119   t.tm_min   = mi;
120   t.tm_sec   = se;
121 
122   _t = mktime (&t);
123 }
124 
125 ////////////////////////////////////////////////////////////////////////////////
Date(const std::string & input,const std::string & format)126 Date::Date (const std::string& input, const std::string& format /* = "m/d/Y" */)
127 {
128   // Before parsing according to "format", perhaps this is a relative date?
129   if (isRelativeDate (input))
130     return;
131 
132   // Parse a formatted date.
133   Nibbler n (input);
134   if (n.getDate (format, _t) && n.depleted ())
135     return;
136 
137   // Parse an ISO date.
138   if (n.getDateISO (_t) && n.depleted ())
139     return;
140 
141   // Perhaps it is an epoch date, in string form?
142   if (isEpoch (input))
143     return;
144 
145   throw ::format ("ERROR: '{1}' is not a valid date in the '{2}' format.",
146                   input, format);
147 }
148 
149 ////////////////////////////////////////////////////////////////////////////////
Date(const Date & rhs)150 Date::Date (const Date& rhs)
151 {
152   _t = rhs._t;
153 }
154 
155 ////////////////////////////////////////////////////////////////////////////////
~Date()156 Date::~Date ()
157 {
158 }
159 
160 ////////////////////////////////////////////////////////////////////////////////
toEpoch()161 time_t Date::toEpoch ()
162 {
163   return _t;
164 }
165 
166 ////////////////////////////////////////////////////////////////////////////////
toEpochString()167 std::string Date::toEpochString ()
168 {
169   std::stringstream epoch;
170   epoch << _t;
171   return epoch.str ();
172 }
173 
174 ////////////////////////////////////////////////////////////////////////////////
175 // 19980119T070000Z =  YYYYMMDDThhmmssZ
toISO()176 std::string Date::toISO ()
177 {
178   struct tm* t = gmtime (&_t);
179 
180   std::stringstream iso;
181   iso << std::setw (4) << std::setfill ('0') << t->tm_year + 1900
182       << std::setw (2) << std::setfill ('0') << t->tm_mon + 1
183       << std::setw (2) << std::setfill ('0') << t->tm_mday
184       << "T"
185       << std::setw (2) << std::setfill ('0') << t->tm_hour
186       << std::setw (2) << std::setfill ('0') << t->tm_min
187       << std::setw (2) << std::setfill ('0') << t->tm_sec
188       << "Z";
189 
190   return iso.str ();
191 }
192 
193 ////////////////////////////////////////////////////////////////////////////////
toJulian()194 double Date::toJulian ()
195 {
196   return (_t / 86400.0) + 2440587.5;
197 }
198 
199 ////////////////////////////////////////////////////////////////////////////////
toEpoch(time_t & epoch)200 void Date::toEpoch (time_t& epoch)
201 {
202   epoch = _t;
203 }
204 
205 ////////////////////////////////////////////////////////////////////////////////
toMDY(int & m,int & d,int & y)206 void Date::toMDY (int& m, int& d, int& y)
207 {
208   struct tm* t = localtime (&_t);
209 
210   m = t->tm_mon + 1;
211   d = t->tm_mday;
212   y = t->tm_year + 1900;
213 }
214 
215 ////////////////////////////////////////////////////////////////////////////////
toString(const std::string & format) const216 const std::string Date::toString (
217   const std::string& format /*= "m/d/Y" */) const
218 {
219   // Making this local copy seems to fix a bug.  Remove the local copy and
220   // you'll see segmentation faults and all kinds of gibberish.
221   std::string localFormat = format;
222 
223   char buffer[12];
224   std::string formatted;
225   for (unsigned int i = 0; i < localFormat.length (); ++i)
226   {
227     int c = localFormat[i];
228     switch (c)
229     {
230     case 'm': sprintf (buffer, "%d",   this->month ());                        break;
231     case 'M': sprintf (buffer, "%02d", this->month ());                        break;
232     case 'd': sprintf (buffer, "%d",   this->day ());                          break;
233     case 'D': sprintf (buffer, "%02d", this->day ());                          break;
234     case 'y': sprintf (buffer, "%02d", this->year () % 100);                   break;
235     case 'Y': sprintf (buffer, "%d",   this->year ());                         break;
236     case 'a': sprintf (buffer, "%.3s", Date::dayName (dayOfWeek ()).c_str ()); break;
237     case 'A': sprintf (buffer, "%.10s", Date::dayName (dayOfWeek ()).c_str ()); break;
238     case 'b': sprintf (buffer, "%.3s", Date::monthName (month ()).c_str ());   break;
239     case 'B': sprintf (buffer, "%.10s", Date::monthName (month ()).c_str ());   break;
240     case 'v': sprintf (buffer, "%d",    Date::weekOfYear (Date::dayOfWeek (weekStart))); break;
241     case 'V': sprintf (buffer, "%02d",  Date::weekOfYear (Date::dayOfWeek (weekStart))); break;
242     case 'h': sprintf (buffer, "%d",   this->hour ());                         break;
243     case 'H': sprintf (buffer, "%02d", this->hour ());                         break;
244     case 'n': sprintf (buffer, "%d",   this->minute ());                       break;
245     case 'N': sprintf (buffer, "%02d", this->minute ());                       break;
246     case 's': sprintf (buffer, "%d",   this->second ());                       break;
247     case 'S': sprintf (buffer, "%02d", this->second ());                       break;
248     case 'j': sprintf (buffer, "%d",   this->dayOfYear ());                    break;
249     case 'J': sprintf (buffer, "%03d", this->dayOfYear ());                    break;
250     default:  sprintf (buffer, "%c",   c);                                     break;
251     }
252 
253     formatted += buffer;
254   }
255 
256   return formatted;
257 }
258 
259 ////////////////////////////////////////////////////////////////////////////////
startOfDay() const260 Date Date::startOfDay () const
261 {
262   return Date (month (), day (), year (), 0, 0, 0);
263 }
264 
265 ////////////////////////////////////////////////////////////////////////////////
startOfWeek() const266 Date Date::startOfWeek () const
267 {
268   Date sow (_t);
269   sow -= (dayOfWeek () * 86400);
270   return Date (sow.month (), sow.day (), sow.year (), 0, 0, 0);
271 }
272 
273 ////////////////////////////////////////////////////////////////////////////////
startOfMonth() const274 Date Date::startOfMonth () const
275 {
276   return Date (month (), 1, year (), 0, 0, 0);
277 }
278 
279 ////////////////////////////////////////////////////////////////////////////////
startOfYear() const280 Date Date::startOfYear () const
281 {
282   return Date (1, 1, year (), 0, 0, 0);
283 }
284 
285 ////////////////////////////////////////////////////////////////////////////////
valid(const std::string & input,const std::string & format)286 bool Date::valid (const std::string& input, const std::string& format)
287 {
288   try
289   {
290     Date test (input, format);
291   }
292 
293   catch (...)
294   {
295     return false;
296   }
297 
298   return true;
299 }
300 
301 ////////////////////////////////////////////////////////////////////////////////
valid(const int m,const int d,const int y,const int hr,const int mi,const int se)302 bool Date::valid (const int m, const int d, const int y, const int hr,
303                   const int mi, const int se)
304 {
305   if (hr < 0 || hr > 23)
306     return false;
307 
308   if (mi < 0 || mi > 59)
309     return false;
310 
311   if (se < 0 || se > 59)
312     return false;
313 
314   return Date::valid (m, d, y);
315 }
316 
317 ////////////////////////////////////////////////////////////////////////////////
valid(const int m,const int d,const int y)318 bool Date::valid (const int m, const int d, const int y)
319 {
320   // Check that the year is valid.
321   if (y < 0)
322     return false;
323 
324   // Check that the month is valid.
325   if (m < 1 || m > 12)
326     return false;
327 
328   // Finally check that the days fall within the acceptable range for this
329   // month, and whether or not this is a leap year.
330   if (d < 1 || d > Date::daysInMonth (m, y))
331     return false;
332 
333   return true;
334 }
335 
336 ////////////////////////////////////////////////////////////////////////////////
337 // Julian
valid(const int d,const int y)338 bool Date::valid (const int d, const int y)
339 {
340   // Check that the year is valid.
341   if (y < 0)
342     return false;
343 
344   if (d < 1 || d > Date::daysInYear (y))
345     return false;
346 
347   return true;
348 }
349 
350 ////////////////////////////////////////////////////////////////////////////////
leapYear(int year)351 bool Date::leapYear (int year)
352 {
353   bool ly = false;
354 
355   // (year % 4 == 0) && (year % 100 !=0)  OR
356   // (year % 400 == 0)
357   // are leapyears
358 
359   if (((!(year % 4)) && (year % 100)) || (!(year % 400))) ly = true;
360 
361   return ly;
362 }
363 
364 ////////////////////////////////////////////////////////////////////////////////
daysInMonth(int month,int year)365 int Date::daysInMonth (int month, int year)
366 {
367   static int days[2][12] =
368   {
369     {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
370     {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
371   };
372 
373   return days[Date::leapYear (year) ? 1 : 0][month - 1];
374 }
375 
376 ////////////////////////////////////////////////////////////////////////////////
daysInYear(int year)377 int Date::daysInYear (int year)
378 {
379   return Date::leapYear (year) ? 366 : 365;
380 }
381 
382 ////////////////////////////////////////////////////////////////////////////////
monthName(int month)383 std::string Date::monthName (int month)
384 {
385   static const char* months[12] =
386   {
387     "january",
388     "february",
389     "march",
390     "april",
391     "may",
392     "june",
393     "july",
394     "august",
395     "september",
396     "october",
397     "november",
398     "december",
399   };
400 
401   assert (month > 0);
402   assert (month <= 12);
403   return ucFirst (months[month - 1]);
404 }
405 
406 ////////////////////////////////////////////////////////////////////////////////
dayName(int dow,std::string & name)407 void Date::dayName (int dow, std::string& name)
408 {
409   static const char* days[7] =
410   {
411     "sunday",
412     "monday",
413     "tuesday",
414     "wednesday",
415     "thursday",
416     "friday",
417     "saturday",
418   };
419 
420   name = ucFirst (days[dow]);
421 }
422 
423 ////////////////////////////////////////////////////////////////////////////////
dayName(int dow)424 std::string Date::dayName (int dow)
425 {
426   static const char* days[7] =
427   {
428     // TODO This should be global.
429     "sunday",
430     "monday",
431     "tuesday",
432     "wednesday",
433     "thursday",
434     "friday",
435     "saturday",
436   };
437 
438   return ucFirst (days[dow]);
439 }
440 
441 ////////////////////////////////////////////////////////////////////////////////
weekOfYear(int weekStart) const442 int Date::weekOfYear (int weekStart) const
443 {
444   struct tm* t = localtime (&_t);
445   char   weekStr[3];
446 
447   if (weekStart == 0)
448     strftime(weekStr, sizeof(weekStr), "%U", t);
449   else if (weekStart == 1)
450     strftime(weekStr, sizeof(weekStr), "%V", t);
451   else
452     throw std::string ("ERROR: The 'weekstart' configuration variable may only contain 'Sunday' or 'Monday'.");
453 
454   int weekNumber = atoi (weekStr);
455 
456   if (weekStart == 0)
457     weekNumber += 1;
458 
459   return weekNumber;
460 }
461 
462 ////////////////////////////////////////////////////////////////////////////////
dayOfWeek() const463 int Date::dayOfWeek () const
464 {
465   struct tm* t = localtime (&_t);
466   return t->tm_wday;
467 }
468 
469 ////////////////////////////////////////////////////////////////////////////////
dayOfWeek(const std::string & input)470 int Date::dayOfWeek (const std::string& input)
471 {
472   std::string in = lowerCase (input);
473 
474   if (in == "sunday"    || in == "sun") return 0;
475   if (in == "monday"    || in == "mon") return 1;
476   if (in == "tuesday"   || in == "tue") return 2;
477   if (in == "wednesday" || in == "wed") return 3;
478   if (in == "thursday"  || in == "thu") return 4;
479   if (in == "friday"    || in == "fri") return 5;
480   if (in == "saturday"  || in == "sat") return 6;
481 
482   return -1;
483 }
484 
485 ////////////////////////////////////////////////////////////////////////////////
dayOfYear() const486 int Date::dayOfYear () const
487 {
488   struct tm* t = localtime (&_t);
489   return t->tm_yday + 1;
490 }
491 
492 ////////////////////////////////////////////////////////////////////////////////
monthOfYear(const std::string & input)493 int Date::monthOfYear (const std::string& input)
494 {
495   std::string in = lowerCase (input);
496 
497   if (in == "january"   || in == "jan")  return  1;
498   if (in == "february"  || in == "feb")  return  2;
499   if (in == "march"     || in == "mar")  return  3;
500   if (in == "april"     || in == "apr")  return  4;
501   if (in == "may"                     )  return  5;
502   if (in == "june"      || in == "jun")  return  6;
503   if (in == "july"      || in == "jul")  return  7;
504   if (in == "august"    || in == "aug")  return  8;
505   if (in == "september" || in == "sep")  return  9;
506   if (in == "october"   || in == "oct")  return 10;
507   if (in == "november"  || in == "nov")  return 11;
508   if (in == "december"  || in == "dec")  return 12;
509 
510   return -1;
511 }
512 
513 ////////////////////////////////////////////////////////////////////////////////
length(const std::string & format)514 int Date::length (const std::string& format)
515 {
516   int total = 0;
517 
518   std::string::const_iterator i;
519   for (i = format.begin (); i != format.end (); ++i)
520   {
521     switch (*i)
522     {
523     case 'm':
524     case 'M':
525     case 'd':
526     case 'D':
527     case 'y':
528     case 'v':
529     case 'V':
530     case 'h':
531     case 'H':
532     case 'n':
533     case 'N':
534     case 's':
535     case 'S': total += 2; break;
536     case 'b':
537     case 'j':
538     case 'J':
539     case 'a': total += 3; break;
540     case 'Y': total += 4; break;
541     case 'A':
542     case 'B': total += 10; break;
543 
544     // TODO This should be a calculated character width, not necessarily 1.
545     default:  total += 1; break;
546     }
547   }
548 
549   return total;
550 }
551 
552 ////////////////////////////////////////////////////////////////////////////////
easter(int year)553 time_t Date::easter (int year)
554 {
555   int Y = year;
556   int a = Y % 19;
557   int b = Y / 100;
558   int c = Y % 100;
559   int d = b / 4;
560   int e = b % 4;
561   int f = (b + 8) / 25;
562   int g = (b - f + 1) / 3;
563   int h = (19 * a + b - d - g + 15) % 30;
564   int i = c / 4;
565   int k = c % 4;
566   int L = (32 + 2 * e + 2 * i - h - k) % 7;
567   int m = (a + 11 * h + 22 * L) / 451;
568   int month = (h + L - 7 * m + 114) / 31;
569   int day = ((h + L - 7 * m + 114) % 31) + 1;
570   struct tm t = {0};
571   t.tm_isdst = -1;   // Requests that mktime determine summer time effect.
572   t.tm_mday  = day;
573   t.tm_mon   = month - 1;
574   t.tm_year  = year - 1900;
575   return mktime (&t);
576 }
577 
578 ////////////////////////////////////////////////////////////////////////////////
month() const579 int Date::month () const
580 {
581   struct tm* t = localtime (&_t);
582   return t->tm_mon + 1;
583 }
584 
585 ////////////////////////////////////////////////////////////////////////////////
week() const586 int Date::week () const
587 {
588   return Date::weekOfYear (Date::dayOfWeek (weekStart));
589 }
590 
591 ////////////////////////////////////////////////////////////////////////////////
day() const592 int Date::day () const
593 {
594   struct tm* t = localtime (&_t);
595   return t->tm_mday;
596 }
597 
598 ////////////////////////////////////////////////////////////////////////////////
year() const599 int Date::year () const
600 {
601   struct tm* t = localtime (&_t);
602   return t->tm_year + 1900;
603 }
604 
605 ////////////////////////////////////////////////////////////////////////////////
hour() const606 int Date::hour () const
607 {
608   struct tm* t = localtime (&_t);
609   return t->tm_hour;
610 }
611 
612 ////////////////////////////////////////////////////////////////////////////////
minute() const613 int Date::minute () const
614 {
615   struct tm* t = localtime (&_t);
616   return t->tm_min;
617 }
618 
619 ////////////////////////////////////////////////////////////////////////////////
second() const620 int Date::second () const
621 {
622   struct tm* t = localtime (&_t);
623   return t->tm_sec;
624 }
625 
626 ////////////////////////////////////////////////////////////////////////////////
operator ==(const Date & rhs) const627 bool Date::operator== (const Date& rhs) const
628 {
629   return rhs._t == _t;
630 }
631 
632 ////////////////////////////////////////////////////////////////////////////////
operator !=(const Date & rhs) const633 bool Date::operator!= (const Date& rhs) const
634 {
635   return rhs._t != _t;
636 }
637 
638 ////////////////////////////////////////////////////////////////////////////////
operator <(const Date & rhs) const639 bool Date::operator<  (const Date& rhs) const
640 {
641   return _t < rhs._t;
642 }
643 
644 ////////////////////////////////////////////////////////////////////////////////
operator >(const Date & rhs) const645 bool Date::operator>  (const Date& rhs) const
646 {
647   return _t > rhs._t;
648 }
649 
650 ////////////////////////////////////////////////////////////////////////////////
operator <=(const Date & rhs) const651 bool Date::operator<= (const Date& rhs) const
652 {
653   return _t <= rhs._t;
654 }
655 
656 ////////////////////////////////////////////////////////////////////////////////
operator >=(const Date & rhs) const657 bool Date::operator>= (const Date& rhs) const
658 {
659   return _t >= rhs._t;
660 }
661 
662 ////////////////////////////////////////////////////////////////////////////////
sameHour(const Date & rhs) const663 bool Date::sameHour (const Date& rhs) const
664 {
665   if (this->year ()  == rhs.year ()  &&
666       this->month () == rhs.month () &&
667       this->day ()   == rhs.day ()   &&
668       this->hour ()  == rhs.hour ())
669     return true;
670 
671   return false;
672 }
673 
674 ////////////////////////////////////////////////////////////////////////////////
sameDay(const Date & rhs) const675 bool Date::sameDay (const Date& rhs) const
676 {
677   if (this->year ()  == rhs.year ()  &&
678       this->month () == rhs.month () &&
679       this->day ()   == rhs.day ())
680     return true;
681 
682   return false;
683 }
684 
685 ////////////////////////////////////////////////////////////////////////////////
sameWeek(const Date & rhs) const686 bool Date::sameWeek (const Date& rhs) const
687 {
688   if (this->year ()  == rhs.year () &&
689       this->week () == rhs.week ())
690     return true;
691 
692   return false;
693 }
694 
695 ////////////////////////////////////////////////////////////////////////////////
sameMonth(const Date & rhs) const696 bool Date::sameMonth (const Date& rhs) const
697 {
698   if (this->year ()  == rhs.year () &&
699       this->month () == rhs.month ())
700     return true;
701 
702   return false;
703 }
704 
705 ////////////////////////////////////////////////////////////////////////////////
sameYear(const Date & rhs) const706 bool Date::sameYear (const Date& rhs) const
707 {
708   if (this->year () == rhs.year ())
709     return true;
710 
711   return false;
712 }
713 
714 ////////////////////////////////////////////////////////////////////////////////
operator -(const int delta)715 Date Date::operator- (const int delta)
716 {
717   return Date (_t - delta);
718 }
719 
720 ////////////////////////////////////////////////////////////////////////////////
operator +(const int delta)721 Date Date::operator+ (const int delta)
722 {
723   return Date (_t + delta);
724 }
725 
726 ////////////////////////////////////////////////////////////////////////////////
operator +=(const int delta)727 Date& Date::operator+= (const int delta)
728 {
729   _t += (time_t) delta;
730   return *this;
731 }
732 
733 ////////////////////////////////////////////////////////////////////////////////
operator -=(const int delta)734 Date& Date::operator-= (const int delta)
735 {
736   _t -= (time_t) delta;
737   return *this;
738 }
739 
740 ////////////////////////////////////////////////////////////////////////////////
operator -(const Date & rhs)741 time_t Date::operator- (const Date& rhs)
742 {
743   return _t - rhs._t;
744 }
745 
746 ////////////////////////////////////////////////////////////////////////////////
747 // Prefix decrement by one day.
operator --()748 void Date::operator-- ()
749 {
750   Date yesterday = startOfDay () - 1;
751   yesterday = Date (yesterday.month (),
752                     yesterday.day (),
753                     yesterday.year (),
754                     hour (),
755                     minute (),
756                     second ());
757   _t = yesterday._t;
758 }
759 
760 ////////////////////////////////////////////////////////////////////////////////
761 // Postfix decrement by one day.
operator --(int)762 void Date::operator-- (int)
763 {
764   Date yesterday = startOfDay () - 1;
765   yesterday = Date (yesterday.month (),
766                     yesterday.day (),
767                     yesterday.year (),
768                     hour (),
769                     minute (),
770                     second ());
771   _t = yesterday._t;
772 }
773 
774 ////////////////////////////////////////////////////////////////////////////////
775 // Prefix increment by one day.
operator ++()776 void Date::operator++ ()
777 {
778   Date tomorrow = (startOfDay () + 90001).startOfDay ();
779   tomorrow = Date (tomorrow.month (),
780                    tomorrow.day (),
781                    tomorrow.year (),
782                    hour (),
783                    minute (),
784                    second ());
785   _t = tomorrow._t;
786 }
787 
788 ////////////////////////////////////////////////////////////////////////////////
789 // Postfix increment by one day.
operator ++(int)790 void Date::operator++ (int)
791 {
792   Date tomorrow = (startOfDay () + 90001).startOfDay ();
793   tomorrow = Date (tomorrow.month (),
794                    tomorrow.day (),
795                    tomorrow.year (),
796                    hour (),
797                    minute (),
798                    second ());
799   _t = tomorrow._t;
800 }
801 
802 ////////////////////////////////////////////////////////////////////////////////
isEpoch(const std::string & input)803 bool Date::isEpoch (const std::string& input)
804 {
805   if (digitsOnly (input) &&
806       input.length () <= 10 )
807   {
808     _t = (time_t) atoi (input.c_str ());
809     return true;
810   }
811 
812   return false;
813 }
814 
815 ////////////////////////////////////////////////////////////////////////////////
816 // If the input string looks like a relative date, determine that date, set _t
817 // and return true.
818 //
819 // What is a relative date?  All of the following should be recognizable, and
820 // converted to an absolute date:
821 //   wednesday
822 //   fri
823 //   23rd
824 //   today
825 //   tomorrow
826 //   yesterday
827 //   eow         (end of week)
828 //   eom         (end of month)
829 //   eoy         (end of year)
830 //   now
isRelativeDate(const std::string & input)831 bool Date::isRelativeDate (const std::string& input)
832 {
833   std::string in (lowerCase (input));
834   Date today;
835 
836   std::vector <std::string> supported;
837   for (unsigned int i = 0; i < NUM_RELATIVES; ++i)
838     supported.push_back (relatives[i]);
839 
840   // Hard-coded 3, despite rc.abbreviation.minimum.
841   std::vector <std::string> matches;
842   if (autoComplete (in, supported, matches, 3) == 1)
843   {
844     std::string found = matches[0];
845 
846     // If day name.
847     int dow;
848     if ((dow = Date::dayOfWeek (found)) != -1 ||
849         found == "eow"  ||
850         found == "eoww" ||
851         found == "eocw" ||
852         found == "sow"  ||
853         found == "soww" ||
854         found == "socw")
855     {
856       if (found == "eow" || found == "eoww")
857         dow = 5;
858 
859       if (found == "eocw")
860         dow = (Date::dayOfWeek (weekStart) + 6) % 7;
861 
862       if (found == "sow" || found == "soww")
863         dow = 1;
864 
865       if (found == "socw")
866         dow = Date::dayOfWeek (weekStart);
867 
868       if (today.dayOfWeek () >= dow)
869         today += (dow - today.dayOfWeek () + 7) * 86400;
870       else
871         today += (dow - today.dayOfWeek ()) * 86400;
872 
873       int m, d, y;
874       today.toMDY (m, d, y);
875       Date then (m, d, y);
876 
877       _t = then._t;
878       return true;
879     }
880     else if (found == "today")
881     {
882       Date then (today.month (),
883                  today.day (),
884                  today.year ());
885       _t = then._t;
886       return true;
887     }
888     else if (found == "tomorrow")
889     {
890       Date then (today.month (),
891                  today.day (),
892                  today.year ());
893       _t = then._t + 86400;
894       return true;
895     }
896     else if (found == "yesterday")
897     {
898       Date then (today.month (),
899                  today.day (),
900                  today.year ());
901       _t = then._t - 86400;
902       return true;
903     }
904     else if (found == "eom" || found == "eocm")
905     {
906       Date then (today.month (),
907                  daysInMonth (today.month (), today.year ()),
908                  today.year ());
909       _t = then._t;
910       return true;
911     }
912     else if (found == "eoq")
913     {
914       int eoq_month = today.month () + 2 - (today.month () - 1) % 3;
915       Date then (eoq_month,
916                  daysInMonth (eoq_month, today.year ()),
917                  today.year ());
918       _t = then._t;
919       return true;
920     }
921     else if (found == "eoy")
922     {
923       Date then (12, 31, today.year ());
924       _t = then._t;
925       return true;
926     }
927     else if (found == "socm")
928     {
929       int m = today.month ();
930       int y = today.year ();
931       Date then (m, 1, y);
932       _t = then._t;
933       return true;
934     }
935     else if (found == "som")
936     {
937       int m = today.month () + 1;
938       int y = today.year ();
939       if (m > 12)
940       {
941         m -=12;
942         y++;
943       }
944       Date then (m, 1, y);
945       _t = then._t;
946       return true;
947     }
948     else if (found == "soq")
949     {
950       int m = today.month () + 3 - (today.month () - 1) % 3;
951       int y = today.year ();
952       if (m > 12)
953       {
954         m -=12;
955         y++;
956       }
957       Date then (m , 1, y);
958       _t = then._t;
959       return true;
960     }
961     else if (found == "soy")
962     {
963       Date then (1, 1, today.year () + 1);
964       _t = then._t;
965       return true;
966     }
967     else if (found == "goodfriday")
968     {
969       Date then (Date::easter(today.year()));
970       _t = then._t - 86400*2;
971       return true;
972     }
973     else if (found == "easter")
974     {
975       Date then (Date::easter(today.year()));
976       _t = then._t;
977       return true;
978     }
979     else if (found == "eastermonday")
980     {
981       Date then (Date::easter(today.year()));
982       _t = then._t + 86400;
983       return true;
984     }
985     else if (found == "ascension")
986     {
987       Date then (Date::easter(today.year()));
988       _t = then._t + 86400*39;
989       return true;
990     }
991     else if (found == "pentecost")
992     {
993       Date then (Date::easter(today.year()));
994       _t = then._t + 86400*49;
995       return true;
996     }
997     else if (found == "midsommar")
998     {
999       for (int midsommar = 20; midsommar <= 26; midsommar++)
1000       {
1001         Date then (6, midsommar, today.year ());
1002         if (6 == then.dayOfWeek ())
1003         {
1004           _t = then._t;
1005           return true;
1006         }
1007       }
1008     }
1009     else if (found == "midsommarafton")
1010     {
1011       for (int midsommar = 19; midsommar <= 25; midsommar++)
1012       {
1013         Date then (6, midsommar, today.year ());
1014         if (5 == then.dayOfWeek ())
1015         {
1016           _t = then._t;
1017           return true;
1018         }
1019       }
1020     }
1021     else if (found == "now")
1022     {
1023       _t = time (NULL);
1024       return true;
1025     }
1026     else if (found == "later" || found == "someday")
1027     {
1028       Date then (1, 18, 2038);
1029       _t = then._t;
1030       return true;
1031     }
1032   }
1033 
1034   // Support "21st" to indicate the next date that is the 21st day.
1035   else if (in.length () <= 4 &&
1036            isdigit (in[0]))
1037   {
1038     int number;
1039     std::string ordinal;
1040 
1041     if (isdigit (in[1]))
1042     {
1043       number = atoi (in.substr (0, 2).c_str ());
1044       ordinal = lowerCase (in.substr (2));
1045     }
1046     else
1047     {
1048       number = atoi (in.substr (0, 2).c_str ());
1049       ordinal = lowerCase (in.substr (1));
1050     }
1051 
1052     // Sanity check.
1053     if (number <= 31)
1054     {
1055       if (ordinal == "st" ||
1056           ordinal == "nd" ||
1057           ordinal == "rd" ||
1058           ordinal == "th")
1059       {
1060         int m = today.month ();
1061         int d = today.day ();
1062         int y = today.year ();
1063 
1064         // If it is this month.
1065         if (d < number &&
1066             number <= Date::daysInMonth (m, y))
1067         {
1068           Date then (m, number, y);
1069           _t = then._t;
1070           return true;
1071         }
1072 
1073         do
1074         {
1075           m++;
1076 
1077           if (m > 12)
1078           {
1079             m = 1;
1080             y++;
1081           }
1082         }
1083         while (number > Date::daysInMonth (m, y));
1084 
1085         Date then (m, number, y);
1086         _t = then._t;
1087         return true;
1088       }
1089     }
1090   }
1091 
1092   return false;
1093 }
1094 
1095 ////////////////////////////////////////////////////////////////////////////////
get_relatives()1096 const std::vector <std::string> Date::get_relatives ()
1097 {
1098   std::vector <std::string> all;
1099   for (unsigned int i = 0; i < NUM_RELATIVES; ++i)
1100     if (strcmp (relatives[i], "-"))
1101       all.push_back (relatives[i]);
1102 
1103   return all;
1104 }
1105 
1106 ////////////////////////////////////////////////////////////////////////////////
1107