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