1 ////////////////////////////////////////////////////////////////////////////////
3 // Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
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:
12 // The above copyright notice and this permission notice shall be included
13 // in all copies or substantial portions of the Software.
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
23 // http://www.opensource.org/licenses/mit-license.php
25 ////////////////////////////////////////////////////////////////////////////////
40 static std::vector <std::string> dayNames {
49 static std::vector <std::string> monthNames {
63 int Datetime::weekstart = 1; // Monday, per ISO-8601.
64 int Datetime::minimumMatchLength = 3;
65 bool Datetime::isoEnabled = true;
66 bool Datetime::standaloneDateEnabled = true;
67 bool Datetime::standaloneTimeEnabled = true;
69 ////////////////////////////////////////////////////////////////////////////////
73 _date = time (nullptr);
76 ////////////////////////////////////////////////////////////////////////////////
77 Datetime::Datetime (const std::string& input, const std::string& format)
80 std::string::size_type start = 0;
81 if (! parse (input, start, format))
82 throw ::format ("'{1}' is not a valid date in the '{2}' format.", input, format);
85 ////////////////////////////////////////////////////////////////////////////////
86 Datetime::Datetime (const time_t t)
92 ////////////////////////////////////////////////////////////////////////////////
93 Datetime::Datetime (const int y, const int m, const int d)
95 // Protect against arguments being passed in the wrong order.
96 assert (y >= 1969 && y < 2100);
97 assert (m >= 1 && m <= 12);
98 assert (d >= 1 && d <= 31);
102 // Error if not valid.
104 t.tm_isdst = -1; // Requests that mktime determine summer time effect.
107 t.tm_year = y - 1900;
112 ////////////////////////////////////////////////////////////////////////////////
113 Datetime::Datetime (const int y, const int m, const int d,
114 const int hr, const int mi, const int se)
116 // Protect against arguments being passed in the wrong order.
117 assert (y >= 1969 && y < 2100);
118 assert (m >= 1 && m <= 12);
119 assert (d >= 1 && d <= 31);
120 assert (hr >= 0 && hr <= 24);
121 assert (mi >= 0 && mi < 60);
122 assert (se >= 0 && se < 60);
126 // Error if not valid.
128 t.tm_isdst = -1; // Requests that mktime determine summer time effect.
131 t.tm_year = y - 1900;
139 ////////////////////////////////////////////////////////////////////////////////
140 bool Datetime::parse (
141 const std::string& input,
142 std::string::size_type& start,
143 const std::string& format)
148 pig.skipN (static_cast <int> (i));
150 auto checkpoint = pig.cursor ();
152 // Parse epoch first, as it's the most common scenario.
153 if (parse_epoch (pig))
155 // ::validate and ::resolve are not needed in this case.
156 start = pig.cursor ();
160 if (parse_formatted (pig, format))
162 // Check the values and determine time_t.
165 start = pig.cursor ();
171 // Allow parse_date_time and parse_date_time_ext regardless of
172 // Datetime::isoEnabled setting, because these formats are relied upon by
173 // the 'import' command, JSON parser and hook system.
174 if (parse_date_time_ext (pig) || // Strictest first.
175 parse_date_time (pig) ||
176 (Datetime::isoEnabled &&
177 ( parse_date_ext (pig) ||
178 (Datetime::standaloneDateEnabled && parse_date (pig)) ||
179 parse_time_utc_ext (pig) ||
180 parse_time_utc (pig) ||
181 parse_time_off_ext (pig) ||
182 parse_time_off (pig) ||
183 parse_time_ext (pig) ||
184 (Datetime::standaloneTimeEnabled && parse_time (pig)) // Time last, as it is the most permissive.
189 // Check the values and determine time_t.
192 start = pig.cursor ();
198 pig.restoreTo (checkpoint);
200 if (parse_named (pig))
202 // ::validate and ::resolve are not needed in this case.
203 start = pig.cursor ();
210 ////////////////////////////////////////////////////////////////////////////////
211 void Datetime::clear ()
225 ////////////////////////////////////////////////////////////////////////////////
226 bool Datetime::parse_formatted (Pig& pig, const std::string& format)
228 // Short-circuit on missing format.
232 auto checkpoint = pig.cursor ();
234 int month {-1}; // So we can check later.
241 // For parsing, unused.
245 for (unsigned int f = 0; f < format.length (); ++f)
250 if (pig.getDigit (month))
253 pig.getDigit (month);
256 if (pig.getDigit (month))
261 pig.restoreTo (checkpoint);
267 if (! pig.getDigit2 (month))
269 pig.restoreTo (checkpoint);
275 if (pig.getDigit (day))
280 if (day == 1 || day == 2 || day == 3)
283 if (pig.getDigit (day))
289 pig.restoreTo (checkpoint);
295 if (! pig.getDigit2 (day))
297 pig.restoreTo (checkpoint);
303 if (! pig.getDigit2 (year))
305 pig.restoreTo (checkpoint);
312 if (! pig.getDigit4 (year))
314 pig.restoreTo (checkpoint);
320 if (pig.getDigit (hour))
325 if (hour == 1 || hour == 2)
328 if (pig.getDigit (hour))
334 pig.restoreTo (checkpoint);
340 if (! pig.getDigit2 (hour))
342 pig.restoreTo (checkpoint);
348 if (pig.getDigit (minute))
351 pig.getDigit (minute);
356 if (pig.getDigit (minute))
362 pig.restoreTo (checkpoint);
368 if (! pig.getDigit2 (minute))
370 pig.restoreTo (checkpoint);
376 if (pig.getDigit (second))
379 pig.getDigit (second);
384 if (pig.getDigit (second))
390 pig.restoreTo (checkpoint);
396 if (! pig.getDigit2 (second))
398 pig.restoreTo (checkpoint);
404 if (pig.getDigit (week))
412 if (pig.getDigit (week))
418 pig.restoreTo (checkpoint);
424 if (! pig.getDigit2 (week))
426 pig.restoreTo (checkpoint);
432 wday = Datetime::dayOfWeek (pig.str ().substr (0, 3));
435 pig.restoreTo (checkpoint);
445 if (pig.getUntil (format[f + 1], dayName))
447 wday = Datetime::dayOfWeek (dayName);
450 pig.restoreTo (checkpoint);
458 month = Datetime::monthOfYear (pig.str ().substr (0, 3));
461 pig.restoreTo (checkpoint);
470 std::string monthName;
471 if (pig.getUntil (format[f + 1], monthName))
473 month = Datetime::monthOfYear (monthName);
476 pig.restoreTo (checkpoint);
484 if (! pig.skip (format[f]))
486 pig.restoreTo (checkpoint);
493 // It is possible that the format='Y-M-D', and the input is Y-M-DTH:N:SZ, and
494 // this should not be considered a match.
495 if (! pig.eos () && ! unicodeWhitespace (pig.peek ()))
497 pig.restoreTo (checkpoint);
501 // Missing values are filled in from the current date.
508 month = now.month ();
517 minute = now.minute ();
519 second = now.second ();
526 // Any remaining undefined values are assigned defaults.
527 if (month == -1) month = 1;
528 if (day == -1) day = 1;
529 if (hour == -1) hour = 0;
530 if (minute == -1) minute = 0;
531 if (second == -1) second = 0;
536 _seconds = (hour * 3600) + (minute * 60) + second;
541 ////////////////////////////////////////////////////////////////////////////////
542 // Note how these are all single words.
544 // Examples and descriptions, assuming now == 2017-03-05T12:34:56.
547 // ------------------- ------------------
548 // now 2017-03-05T12:34:56 Unaffected
549 // yesterday 2017-03-04T00:00:00 Unaffected
550 // today 2017-03-05T00:00:00 Unaffected
551 // tomorrow 2017-03-06T00:00:00 Unaffected
552 // <ordinal> 12th 2017-03-12T00:00:00
553 // <day> monday 2017-03-06T00:00:00
554 // <month> april 2017-04-01T00:00:00
555 // later 2038-01-18T00:00:00 Unaffected
556 // someday 2038-01-18T00:00:00 Unaffected
557 // sopd 2017-03-04T00:00:00 Unaffected
558 // sod 2017-03-05T00:00:00 Unaffected
559 // sond 2017-03-06T00:00:00 Unaffected
560 // eopd 2017-03-05T00:00:00 Unaffected
561 // eod 2017-03-06T00:00:00 Unaffected
562 // eond 2017-03-07T00:00:00 Unaffected
563 // sopw 2017-02-26T00:00:00 Unaffected
564 // sow 2017-03-05T00:00:00 Unaffected
565 // sonw 2017-03-12T00:00:00 Unaffected
566 // eopw 2017-03-05T00:00:00 Unaffected
567 // eow 2017-03-12T00:00:00 Unaffected
568 // eonw 2017-03-19T00:00:00 Unaffected
569 // sopww 2017-02-27T00:00:00 Unaffected
570 // soww 2017-03-06T00:00:00
571 // sonww 2017-03-06T00:00:00 Unaffected
572 // eopww 2017-03-03T00:00:00 Unaffected
573 // eoww 2017-03-10T00:00:00
574 // eonww 2017-03-17T00:00:00 Unaffected
575 // sopm 2017-02-01T00:00:00 Unaffected
576 // som 2017-03-01T00:00:00 Unaffected
577 // sonm 2017-04-01T00:00:00 Unaffected
578 // eopm 2017-03-01T00:00:00 Unaffected
579 // eom 2017-04-01T00:00:00 Unaffected
580 // eonm 2017-05-01T00:00:00 Unaffected
581 // sopq 2017-10-01T00:00:00 Unaffected
582 // soq 2017-01-01T00:00:00 Unaffected
583 // sonq 2017-04-01T00:00:00 Unaffected
584 // eopq 2017-01-01T00:00:00 Unaffected
585 // eoq 2017-04-01T00:00:00 Unaffected
586 // eonq 2017-07-01T00:00:00 Unaffected
587 // sopy 2016-01-01T00:00:00 Unaffected
588 // soy 2017-01-01T00:00:00 Unaffected
589 // sony 2018-01-01T00:00:00 Unaffected
590 // eopy 2017-01-01T00:00:00 Unaffected
591 // eoy 2018-01-01T00:00:00 Unaffected
592 // eony 2019-01-01T00:00:00 Unaffected
593 // easter 2017-04-16T00:00:00
594 // eastermonday 2017-04-16T00:00:00
595 // ascension 2017-05-25T00:00:00
596 // pentecost 2017-06-04T00:00:00
597 // goodfriday 2017-04-14T00:00:00
598 // midsommar 2017-06-24T00:00:00 midnight, 1st Saturday after 20th June
599 // midsommarafton 2017-06-23T00:00:00 midnight, 1st Friday after 19th June
600 // juhannus 2017-06-23T00:00:00 midnight, 1st Friday after 19th June
602 bool Datetime::parse_named (Pig& pig)
604 auto checkpoint = pig.cursor ();
606 // Experimental handling of date phrases, such as "first monday in march".
607 // Note that this requires that phrases are deliminted by EOS or WS.
609 std::vector <std::string> tokens;
610 while (pig.getUntilWS (token))
612 tokens.push_back (token);
618 // This grpoup contains "1st monday ..." which must be processed before
619 // initializeOrdinal below.
620 if (initializeNthDayInMonth (tokens))
626 // Restoration necessary because of the tokenization.
627 pig.restoreTo (checkpoint);
629 if (initializeNow (pig) ||
630 initializeYesterday (pig) ||
631 initializeToday (pig) ||
632 initializeTomorrow (pig) ||
633 initializeOrdinal (pig) ||
634 initializeDayName (pig) ||
635 initializeMonthName (pig) ||
636 initializeLater (pig) ||
637 initializeSopd (pig) ||
638 initializeSod (pig) ||
639 initializeSond (pig) ||
640 initializeEopd (pig) ||
641 initializeEod (pig) ||
642 initializeEond (pig) ||
643 initializeSopw (pig) ||
644 initializeSow (pig) ||
645 initializeSonw (pig) ||
646 initializeEopw (pig) ||
647 initializeEow (pig) ||
648 initializeEonw (pig) ||
649 initializeSopww (pig) || // Must appear after sopw
650 initializeSonww (pig) || // Must appear after sonw
651 initializeSoww (pig) || // Must appear after sow
652 initializeEopww (pig) || // Must appear after eopw
653 initializeEonww (pig) || // Must appear after eonw
654 initializeEoww (pig) || // Must appear after eow
655 initializeSopm (pig) ||
656 initializeSom (pig) ||
657 initializeSonm (pig) ||
658 initializeEopm (pig) ||
659 initializeEom (pig) ||
660 initializeEonm (pig) ||
661 initializeSopq (pig) ||
662 initializeSoq (pig) ||
663 initializeSonq (pig) ||
664 initializeEopq (pig) ||
665 initializeEoq (pig) ||
666 initializeEonq (pig) ||
667 initializeSopy (pig) ||
668 initializeSoy (pig) ||
669 initializeSony (pig) ||
670 initializeEopy (pig) ||
671 initializeEoy (pig) ||
672 initializeEony (pig) ||
673 initializeEaster (pig) ||
674 initializeMidsommar (pig) ||
675 initializeMidsommarafton (pig) ||
676 initializeInformalTime (pig))
681 pig.restoreTo (checkpoint);
685 ////////////////////////////////////////////////////////////////////////////////
686 // Valid epoch values are unsigned integers after 1980-01-01T00:00:00Z. This
687 // restriction means that '12' will not be identified as an epoch date.
688 bool Datetime::parse_epoch (Pig& pig)
690 auto checkpoint = pig.cursor ();
693 if (pig.getDigits (epoch) &&
694 ! unicodeLatinAlpha (pig.peek ()) &&
697 _date = static_cast <time_t> (epoch);
698 //std::cout << "# parse_epoch
\e[33msucceed
\e[0m " << pig.dump () << "\n";
702 //std::cout << "# parse_epoch fail\n";
703 pig.restoreTo (checkpoint);
707 ////////////////////////////////////////////////////////////////////////////////
708 // date_ext 'T' time_utc_ext 'Z'
709 // date_ext 'T' time_off_ext
710 // date_ext 'T' time_ext
711 bool Datetime::parse_date_time_ext (Pig& pig)
713 auto checkpoint = pig.cursor ();
715 if (parse_date_ext (pig) &&
717 (parse_time_utc_ext (pig) ||
718 parse_time_off_ext (pig) ||
719 parse_time_ext (pig)))
724 pig.restoreTo (checkpoint);
728 ////////////////////////////////////////////////////////////////////////////////
734 bool Datetime::parse_date_ext (Pig& pig)
736 auto checkpoint = pig.cursor ();
739 if (parse_year (pig, year) &&
742 auto checkpointYear = pig.cursor ();
748 if (pig.skip ('W') &&
749 parse_week (pig, _week))
751 if (pig.skip ('-') &&
752 pig.getDigit (_weekday))
754 // What is happening here - must be something to do?
757 if (! unicodeLatinDigit (pig.peek ()))
764 pig.restoreTo (checkpointYear);
766 if (parse_month (pig, month) &&
768 parse_day (pig, day) &&
769 ! unicodeLatinDigit (pig.peek ()))
777 pig.restoreTo (checkpointYear);
779 if (parse_julian (pig, julian) &&
780 ! unicodeLatinDigit (pig.peek ()))
787 pig.restoreTo (checkpointYear);
789 if (parse_month (pig, month) &&
790 pig.peek () != '-' &&
791 ! unicodeLatinDigit (pig.peek ()))
800 pig.restoreTo (checkpoint);
804 ////////////////////////////////////////////////////////////////////////////////
806 bool Datetime::parse_off_ext (Pig& pig)
808 auto checkpoint = pig.cursor ();
810 int sign = pig.peek ();
811 if (sign == '+' || sign == '-')
818 if (parse_off_hour (pig, hour))
822 if (! parse_off_minute (pig, minute))
824 pig.restoreTo (checkpoint);
829 _offset = (hour * 3600) + (minute * 60);
833 if (! unicodeLatinDigit (pig.peek ()))
838 pig.restoreTo (checkpoint);
842 ////////////////////////////////////////////////////////////////////////////////
844 bool Datetime::parse_time_ext (Pig& pig, bool terminated)
846 auto checkpoint = pig.cursor ();
850 if (parse_hour (pig, hour) &&
852 parse_minute (pig, minute))
857 if (parse_second (pig, second) &&
858 ! unicodeLatinDigit (pig.peek ()) &&
859 (! terminated || (pig.peek () != '-' && pig.peek () != '+')))
861 _seconds = (hour * 3600) + (minute * 60) + second;
865 pig.restoreTo (checkpoint);
869 auto following = pig.peek ();
870 if (! unicodeLatinDigit (following) &&
871 (! terminated || (following != '+' && following != '-')) &&
877 _seconds = (hour * 3600) + (minute * 60);
882 pig.restoreTo (checkpoint);
886 ////////////////////////////////////////////////////////////////////////////////
888 bool Datetime::parse_time_utc_ext (Pig& pig)
890 auto checkpoint = pig.cursor ();
892 if (parse_time_ext (pig, false) &&
895 if (! unicodeLatinDigit (pig.peek ()))
902 pig.restoreTo (checkpoint);
906 ////////////////////////////////////////////////////////////////////////////////
908 bool Datetime::parse_time_off_ext (Pig& pig)
910 auto checkpoint = pig.cursor ();
912 if (parse_time_ext (pig, false) &&
918 pig.restoreTo (checkpoint);
922 ////////////////////////////////////////////////////////////////////////////////
925 bool Datetime::parse_date_time (Pig& pig)
927 auto checkpoint = pig.cursor ();
929 if (parse_date (pig) &&
931 (parse_time_utc (pig) ||
932 parse_time_off (pig) ||
938 pig.restoreTo (checkpoint);
942 ////////////////////////////////////////////////////////////////////////////////
947 bool Datetime::parse_date (Pig& pig)
949 auto checkpoint = pig.cursor ();
957 if (parse_year (pig, year))
959 auto checkpointYear = pig.cursor ();
961 if (pig.skip ('W') &&
962 parse_week (pig, week))
964 if (pig.getDigit (weekday))
967 if (! unicodeLatinDigit (pig.peek ()))
975 pig.restoreTo (checkpointYear);
977 if (parse_julian (pig, julian) &&
978 ! unicodeLatinDigit (pig.peek ()))
985 pig.restoreTo (checkpointYear);
987 if (parse_month (pig, month))
989 if (parse_day (pig, day))
991 if (! unicodeLatinDigit (pig.peek ()))
1001 if (! unicodeLatinDigit (pig.peek ()))
1012 pig.restoreTo (checkpoint);
1016 ////////////////////////////////////////////////////////////////////////////////
1018 bool Datetime::parse_time_utc (Pig& pig)
1020 auto checkpoint = pig.cursor ();
1022 if (parse_time (pig, false) &&
1026 if (! unicodeLatinDigit (pig.peek ()))
1030 pig.restoreTo (checkpoint);
1034 ////////////////////////////////////////////////////////////////////////////////
1036 bool Datetime::parse_time_off (Pig& pig)
1038 auto checkpoint = pig.cursor ();
1040 if (parse_time (pig, false) &&
1043 auto terminator = pig.peek ();
1044 if (terminator != '-' && ! unicodeLatinDigit (terminator))
1050 pig.restoreTo (checkpoint);
1054 ////////////////////////////////////////////////////////////////////////////////
1057 bool Datetime::parse_time (Pig& pig, bool terminated)
1059 auto checkpoint = pig.cursor ();
1063 if (parse_hour (pig, hour) &&
1064 parse_minute (pig, minute))
1067 parse_second (pig, second);
1069 auto terminator = pig.peek ();
1071 (! unicodeLatinDigit (terminator) && terminator != '-' && terminator != '+'))
1073 _seconds = (hour * 3600) + (minute * 60) + second;
1078 pig.restoreTo (checkpoint);
1082 ////////////////////////////////////////////////////////////////////////////////
1085 bool Datetime::parse_off (Pig& pig)
1087 auto checkpoint = pig.cursor ();
1089 int sign = pig.peek ();
1090 if (sign == '+' || sign == '-')
1095 if (parse_off_hour (pig, hour))
1098 parse_off_minute (pig, minute);
1100 if (! unicodeLatinDigit (pig.peek ()))
1102 _offset = (hour * 3600) + (minute * 60);
1104 _offset = - _offset;
1111 pig.restoreTo (checkpoint);
1115 ////////////////////////////////////////////////////////////////////////////////
1116 bool Datetime::parse_year (Pig& pig, int& value)
1118 auto checkpoint = pig.cursor ();
1121 if (pig.getDigit4 (year) &&
1128 pig.restoreTo (checkpoint);
1132 ////////////////////////////////////////////////////////////////////////////////
1133 bool Datetime::parse_month (Pig& pig, int& value)
1135 auto checkpoint = pig.cursor ();
1138 if (pig.getDigit2 (month) &&
1146 pig.restoreTo (checkpoint);
1150 ////////////////////////////////////////////////////////////////////////////////
1151 bool Datetime::parse_week (Pig& pig, int& value)
1153 auto checkpoint = pig.cursor ();
1156 if (pig.getDigit2 (week) &&
1164 pig.restoreTo (checkpoint);
1168 ////////////////////////////////////////////////////////////////////////////////
1169 bool Datetime::parse_julian (Pig& pig, int& value)
1171 auto checkpoint = pig.cursor ();
1174 if (pig.getDigit3 (julian) &&
1182 pig.restoreTo (checkpoint);
1186 ////////////////////////////////////////////////////////////////////////////////
1187 bool Datetime::parse_day (Pig& pig, int& value)
1189 auto checkpoint = pig.cursor ();
1192 if (pig.getDigit2 (day) &&
1200 pig.restoreTo (checkpoint);
1204 ////////////////////////////////////////////////////////////////////////////////
1205 bool Datetime::parse_weekday (Pig& pig, int& value)
1207 auto checkpoint = pig.cursor ();
1210 if (pig.getDigit (weekday) &&
1218 pig.restoreTo (checkpoint);
1222 ////////////////////////////////////////////////////////////////////////////////
1223 bool Datetime::parse_hour (Pig& pig, int& value)
1225 auto checkpoint = pig.cursor ();
1228 if (pig.getDigit2 (hour) &&
1236 pig.restoreTo (checkpoint);
1240 ////////////////////////////////////////////////////////////////////////////////
1241 bool Datetime::parse_minute (Pig& pig, int& value)
1243 auto checkpoint = pig.cursor ();
1246 if (pig.getDigit2 (minute) &&
1254 pig.restoreTo (checkpoint);
1258 ////////////////////////////////////////////////////////////////////////////////
1259 bool Datetime::parse_second (Pig& pig, int& value)
1261 auto checkpoint = pig.cursor ();
1264 if (pig.getDigit2 (second) &&
1272 pig.restoreTo (checkpoint);
1276 ////////////////////////////////////////////////////////////////////////////////
1277 bool Datetime::parse_off_hour (Pig& pig, int& value)
1279 auto checkpoint = pig.cursor ();
1282 if (pig.getDigit2 (hour) &&
1290 pig.restoreTo (checkpoint);
1294 ////////////////////////////////////////////////////////////////////////////////
1295 bool Datetime::parse_off_minute (Pig& pig, int& value)
1297 auto checkpoint = pig.cursor ();
1300 if (pig.getDigit2 (minute) &&
1308 pig.restoreTo (checkpoint);
1312 ////////////////////////////////////////////////////////////////////////////////
1313 // now [ !<alpha> && !<digit> ]
1314 bool Datetime::initializeNow (Pig& pig)
1316 auto checkpoint = pig.cursor ();
1318 if (pig.skipLiteral ("now"))
1320 auto following = pig.peek ();
1321 if (! unicodeLatinAlpha (following) &&
1322 ! unicodeLatinDigit (following))
1324 _date = time (nullptr);
1329 pig.restoreTo (checkpoint);
1333 ////////////////////////////////////////////////////////////////////////////////
1334 // yesterday/abbrev [ !<alpha> && !<digit> ]
1335 bool Datetime::initializeYesterday (Pig& pig)
1337 auto checkpoint = pig.cursor ();
1340 if (pig.skipPartial ("yesterday", token) &&
1341 token.length () >= static_cast <std::string::size_type> (Datetime::minimumMatchLength))
1343 auto following = pig.peek ();
1344 if (! unicodeLatinAlpha (following) &&
1345 ! unicodeLatinDigit (following))
1347 time_t now = time (nullptr);
1348 struct tm* t = localtime (&now);
1350 t->tm_hour = t->tm_min = t->tm_sec = 0;
1358 pig.restoreTo (checkpoint);
1362 ////////////////////////////////////////////////////////////////////////////////
1363 // today/abbrev [ !<alpha> && !<digit> ]
1364 bool Datetime::initializeToday (Pig& pig)
1366 auto checkpoint = pig.cursor ();
1369 if (pig.skipPartial ("today", token) &&
1370 token.length () >= static_cast <std::string::size_type> (Datetime::minimumMatchLength))
1372 auto following = pig.peek ();
1373 if (! unicodeLatinAlpha (following) &&
1374 ! unicodeLatinDigit (following))
1376 time_t now = time (nullptr);
1377 struct tm* t = localtime (&now);
1379 t->tm_hour = t->tm_min = t->tm_sec = 0;
1387 pig.restoreTo (checkpoint);
1391 ////////////////////////////////////////////////////////////////////////////////
1392 // tomcorrow/abbrev [ !<alpha> && !<digit> ]
1393 bool Datetime::initializeTomorrow (Pig& pig)
1395 auto checkpoint = pig.cursor ();
1398 if (pig.skipPartial ("tomorrow", token) &&
1399 token.length () >= static_cast <std::string::size_type> (Datetime::minimumMatchLength))
1401 auto following = pig.peek ();
1402 if (! unicodeLatinAlpha (following) &&
1403 ! unicodeLatinDigit (following))
1405 time_t now = time (nullptr);
1406 struct tm* t = localtime (&now);
1409 t->tm_hour = t->tm_min = t->tm_sec = 0;
1416 pig.restoreTo (checkpoint);
1420 ////////////////////////////////////////////////////////////////////////////////
1421 // <digit>+ [ "st" | "nd" | "rd" | "th" ] [ !<alpha> && !<digit> ]
1422 bool Datetime::initializeOrdinal (Pig& pig)
1424 auto checkpoint = pig.cursor ();
1427 if (pig.getDigits (number) &&
1433 if (pig.getCharacter (character1) &&
1434 pig.getCharacter (character2) &&
1435 ! unicodeLatinAlpha (pig.peek ()) &&
1436 ! unicodeLatinDigit (pig.peek ()))
1438 int remainder1 = number % 10;
1439 int remainder2 = number % 100;
1440 if ((remainder2 != 11 && remainder1 == 1 && character1 == 's' && character2 == 't') ||
1441 (remainder2 != 12 && remainder1 == 2 && character1 == 'n' && character2 == 'd') ||
1442 (remainder2 != 13 && remainder1 == 3 && character1 == 'r' && character2 == 'd') ||
1443 ((remainder2 == 11 ||
1447 remainder1 > 3) && character1 == 't' && character2 == 'h'))
1449 time_t now = time (nullptr);
1450 struct tm* t = localtime (&now);
1452 int y = t->tm_year + 1900;
1453 int m = t->tm_mon + 1;
1456 // If it is this month.
1458 number <= daysInMonth (y, m))
1460 t->tm_hour = t->tm_min = t->tm_sec = 0;
1462 t->tm_mday = number;
1463 t->tm_year = y - 1900;
1475 t->tm_hour = t->tm_min = t->tm_sec = 0;
1477 t->tm_mday = number;
1478 t->tm_year = y - 1900;
1488 pig.restoreTo (checkpoint);
1492 ////////////////////////////////////////////////////////////////////////////////
1493 // sunday/abbrev [ !<alpha> && !<digit> && !: && != ]
1494 bool Datetime::initializeDayName (Pig& pig)
1496 auto checkpoint = pig.cursor ();
1499 for (int day = 0; day <= 7; ++day) // Deliberate <= so that 'sunday' is either 0 or 7.
1501 if (pig.skipPartial (dayNames[day % 7], token) &&
1502 token.length () >= static_cast <std::string::size_type> (Datetime::minimumMatchLength))
1504 auto following = pig.peek ();
1505 if (! unicodeLatinAlpha (following) &&
1506 ! unicodeLatinDigit (following) &&
1510 time_t now = time (nullptr);
1511 struct tm* t = localtime (&now);
1513 if (t->tm_wday >= day)
1514 t->tm_mday += day - t->tm_wday + 7;
1516 t->tm_mday += day - t->tm_wday;
1518 t->tm_hour = t->tm_min = t->tm_sec = 0;
1525 pig.restoreTo (checkpoint);
1531 ////////////////////////////////////////////////////////////////////////////////
1532 // january/abbrev [ !<alpha> && !<digit> && !: && != ]
1533 bool Datetime::initializeMonthName (Pig& pig)
1535 auto checkpoint = pig.cursor ();
1538 for (int month = 0; month < 12; ++month)
1540 if (pig.skipPartial (monthNames[month], token) &&
1541 token.length () >= static_cast <std::string::size_type> (Datetime::minimumMatchLength))
1543 auto following = pig.peek ();
1544 if (! unicodeLatinAlpha (following) &&
1545 ! unicodeLatinDigit (following) &&
1549 time_t now = time (nullptr);
1550 struct tm* t = localtime (&now);
1552 if (t->tm_mon >= month)
1557 t->tm_hour = t->tm_min = t->tm_sec = 0;
1564 pig.restoreTo (checkpoint);
1570 ////////////////////////////////////////////////////////////////////////////////
1571 // later/abbrev [ !<alpha> && !<digit> ]
1572 // someday/abbrev [ !<alpha> && !<digit> ]
1573 bool Datetime::initializeLater (Pig& pig)
1575 auto checkpoint = pig.cursor ();
1578 if ((pig.skipPartial ("later", token) &&
1579 token.length () >= static_cast <std::string::size_type> (Datetime::minimumMatchLength))
1583 (pig.skipPartial ("someday", token) &&
1584 token.length () >= static_cast <std::string::size_type> (std::max (Datetime::minimumMatchLength, 4))))
1586 auto following = pig.peek ();
1587 if (! unicodeLatinAlpha (following) &&
1588 ! unicodeLatinDigit (following))
1590 time_t now = time (nullptr);
1591 struct tm* t = localtime (&now);
1593 t->tm_hour = t->tm_min = t->tm_sec = 0;
1603 pig.restoreTo (checkpoint);
1607 ////////////////////////////////////////////////////////////////////////////////
1608 // sopd [ !<alpha> && !<digit> ]
1609 bool Datetime::initializeSopd (Pig& pig)
1611 auto checkpoint = pig.cursor ();
1613 if (pig.skipLiteral ("sopd"))
1615 auto following = pig.peek ();
1616 if (! unicodeLatinAlpha (following) &&
1617 ! unicodeLatinDigit (following))
1619 time_t now = time (nullptr);
1620 struct tm* t = localtime (&now);
1622 t->tm_hour = t->tm_min = t->tm_sec = 0;
1630 pig.restoreTo (checkpoint);
1634 ////////////////////////////////////////////////////////////////////////////////
1635 // sod [ !<alpha> && !<digit> ]
1636 bool Datetime::initializeSod (Pig& pig)
1638 auto checkpoint = pig.cursor ();
1640 if (pig.skipLiteral ("sod"))
1642 auto following = pig.peek ();
1643 if (! unicodeLatinAlpha (following) &&
1644 ! unicodeLatinDigit (following))
1646 time_t now = time (nullptr);
1647 struct tm* t = localtime (&now);
1649 t->tm_hour = t->tm_min = t->tm_sec = 0;
1656 pig.restoreTo (checkpoint);
1660 ////////////////////////////////////////////////////////////////////////////////
1661 // sond [ !<alpha> && !<digit> ]
1662 bool Datetime::initializeSond (Pig& pig)
1664 auto checkpoint = pig.cursor ();
1666 if (pig.skipLiteral ("sond"))
1668 auto following = pig.peek ();
1669 if (! unicodeLatinAlpha (following) &&
1670 ! unicodeLatinDigit (following))
1672 time_t now = time (nullptr);
1673 struct tm* t = localtime (&now);
1676 t->tm_hour = t->tm_min = t->tm_sec = 0;
1683 pig.restoreTo (checkpoint);
1687 ////////////////////////////////////////////////////////////////////////////////
1688 // eopd [ !<alpha> && !<digit> ]
1689 bool Datetime::initializeEopd (Pig& pig)
1691 auto checkpoint = pig.cursor ();
1693 if (pig.skipLiteral ("eopd"))
1695 auto following = pig.peek ();
1696 if (! unicodeLatinAlpha (following) &&
1697 ! unicodeLatinDigit (following))
1699 time_t now = time (nullptr);
1700 struct tm* t = localtime (&now);
1702 t->tm_hour = t->tm_min = t->tm_sec = 0;
1709 pig.restoreTo (checkpoint);
1713 ////////////////////////////////////////////////////////////////////////////////
1714 // eod [ !<alpha> && !<digit> ]
1715 bool Datetime::initializeEod (Pig& pig)
1717 auto checkpoint = pig.cursor ();
1719 if (pig.skipLiteral ("eod"))
1721 auto following = pig.peek ();
1722 if (! unicodeLatinAlpha (following) &&
1723 ! unicodeLatinDigit (following))
1725 time_t now = time (nullptr);
1726 struct tm* t = localtime (&now);
1729 t->tm_hour = t->tm_min = t->tm_sec = 0;
1736 pig.restoreTo (checkpoint);
1740 ////////////////////////////////////////////////////////////////////////////////
1741 // eond [ !<alpha> && !<digit> ]
1742 bool Datetime::initializeEond (Pig& pig)
1744 auto checkpoint = pig.cursor ();
1746 if (pig.skipLiteral ("eond"))
1748 auto following = pig.peek ();
1749 if (! unicodeLatinAlpha (following) &&
1750 ! unicodeLatinDigit (following))
1752 time_t now = time (nullptr);
1753 struct tm* t = localtime (&now);
1756 t->tm_hour = t->tm_min = t->tm_sec = 0;
1763 pig.restoreTo (checkpoint);
1767 ////////////////////////////////////////////////////////////////////////////////
1768 // sopw [ !<alpha> && !<digit> ]
1769 bool Datetime::initializeSopw (Pig& pig)
1771 auto checkpoint = pig.cursor ();
1773 if (pig.skipLiteral ("sopw"))
1775 auto following = pig.peek ();
1776 if (! unicodeLatinAlpha (following) &&
1777 ! unicodeLatinDigit (following))
1779 time_t now = time (nullptr);
1780 struct tm* t = localtime (&now);
1781 t->tm_hour = t->tm_min = t->tm_sec = 0;
1783 int extra = (t->tm_wday + 6) % 7;
1784 t->tm_mday -= extra;
1793 pig.restoreTo (checkpoint);
1797 ////////////////////////////////////////////////////////////////////////////////
1798 // sow [ !<alpha> && !<digit> ]
1799 bool Datetime::initializeSow (Pig& pig)
1801 auto checkpoint = pig.cursor ();
1803 if (pig.skipLiteral ("sow"))
1805 auto following = pig.peek ();
1806 if (! unicodeLatinAlpha (following) &&
1807 ! unicodeLatinDigit (following))
1809 time_t now = time (nullptr);
1810 struct tm* t = localtime (&now);
1811 t->tm_hour = t->tm_min = t->tm_sec = 0;
1813 int extra = (t->tm_wday + 6) % 7;
1814 t->tm_mday -= extra;
1822 pig.restoreTo (checkpoint);
1826 ////////////////////////////////////////////////////////////////////////////////
1827 // sonw [ !<alpha> && !<digit> ]
1828 bool Datetime::initializeSonw (Pig& pig)
1830 auto checkpoint = pig.cursor ();
1832 if (pig.skipLiteral ("sonw"))
1834 auto following = pig.peek ();
1835 if (! unicodeLatinAlpha (following) &&
1836 ! unicodeLatinDigit (following))
1838 time_t now = time (nullptr);
1839 struct tm* t = localtime (&now);
1840 t->tm_hour = t->tm_min = t->tm_sec = 0;
1842 int extra = (t->tm_wday + 6) % 7;
1843 t->tm_mday -= extra;
1852 pig.restoreTo (checkpoint);
1856 ////////////////////////////////////////////////////////////////////////////////
1857 // eopw [ !<alpha> && !<digit> ]
1858 bool Datetime::initializeEopw (Pig& pig)
1860 auto checkpoint = pig.cursor ();
1862 if (pig.skipLiteral ("eopw"))
1864 auto following = pig.peek ();
1865 if (! unicodeLatinAlpha (following) &&
1866 ! unicodeLatinDigit (following))
1868 time_t now = time (nullptr);
1869 struct tm* t = localtime (&now);
1870 t->tm_hour = t->tm_min = t->tm_sec = 0;
1872 int extra = (t->tm_wday + 6) % 7;
1873 t->tm_mday -= extra;
1881 pig.restoreTo (checkpoint);
1885 ////////////////////////////////////////////////////////////////////////////////
1886 // eow [ !<alpha> && !<digit> ]
1887 bool Datetime::initializeEow (Pig& pig)
1889 auto checkpoint = pig.cursor ();
1891 if (pig.skipLiteral ("eow"))
1893 auto following = pig.peek ();
1894 if (! unicodeLatinAlpha (following) &&
1895 ! unicodeLatinDigit (following))
1897 time_t now = time (nullptr);
1898 struct tm* t = localtime (&now);
1899 t->tm_hour = t->tm_min = t->tm_sec = 0;
1901 int extra = (t->tm_wday + 6) % 7;
1902 t->tm_mday -= extra;
1911 pig.restoreTo (checkpoint);
1915 ////////////////////////////////////////////////////////////////////////////////
1916 // eonw [ !<alpha> && !<digit> ]
1917 bool Datetime::initializeEonw (Pig& pig)
1919 auto checkpoint = pig.cursor ();
1921 if (pig.skipLiteral ("eonw"))
1923 auto following = pig.peek ();
1924 if (! unicodeLatinAlpha (following) &&
1925 ! unicodeLatinDigit (following))
1927 time_t now = time (nullptr);
1928 struct tm* t = localtime (&now);
1930 t->tm_mday += 15 - t->tm_wday;
1931 t->tm_hour = t->tm_min = t->tm_sec = 0;
1938 pig.restoreTo (checkpoint);
1942 ////////////////////////////////////////////////////////////////////////////////
1943 // sopww [ !<alpha> && !<digit> ]
1944 bool Datetime::initializeSopww (Pig& pig)
1946 auto checkpoint = pig.cursor ();
1948 if (pig.skipLiteral ("sopww"))
1950 auto following = pig.peek ();
1951 if (! unicodeLatinAlpha (following) &&
1952 ! unicodeLatinDigit (following))
1954 time_t now = time (nullptr);
1955 struct tm* t = localtime (&now);
1957 t->tm_mday += -6 - t->tm_wday;
1958 t->tm_hour = t->tm_min = t->tm_sec = 0;
1965 pig.restoreTo (checkpoint);
1969 ////////////////////////////////////////////////////////////////////////////////
1970 // soww [ !<alpha> && !<digit> ]
1971 bool Datetime::initializeSoww (Pig& pig)
1973 auto checkpoint = pig.cursor ();
1975 if (pig.skipLiteral ("soww"))
1977 auto following = pig.peek ();
1978 if (! unicodeLatinAlpha (following) &&
1979 ! unicodeLatinDigit (following))
1981 time_t now = time (nullptr);
1982 struct tm* t = localtime (&now);
1984 t->tm_mday += 8 - t->tm_wday;
1985 t->tm_hour = t->tm_min = t->tm_sec = 0;
1992 pig.restoreTo (checkpoint);
1996 ////////////////////////////////////////////////////////////////////////////////
1997 // sonww [ !<alpha> && !<digit> ]
1998 bool Datetime::initializeSonww (Pig& pig)
2000 auto checkpoint = pig.cursor ();
2002 if (pig.skipLiteral ("sonww"))
2004 auto following = pig.peek ();
2005 if (! unicodeLatinAlpha (following) &&
2006 ! unicodeLatinDigit (following))
2008 time_t now = time (nullptr);
2009 struct tm* t = localtime (&now);
2011 t->tm_mday += 8 - t->tm_wday;
2012 t->tm_hour = t->tm_min = t->tm_sec = 0;
2019 pig.restoreTo (checkpoint);
2023 ////////////////////////////////////////////////////////////////////////////////
2024 // eopww [ !<alpha> && !<digit> ]
2025 bool Datetime::initializeEopww (Pig& pig)
2027 auto checkpoint = pig.cursor ();
2029 if (pig.skipLiteral ("eopww"))
2031 auto following = pig.peek ();
2032 if (! unicodeLatinAlpha (following) &&
2033 ! unicodeLatinDigit (following))
2035 time_t now = time (nullptr);
2036 struct tm* t = localtime (&now);
2038 t->tm_mday -= (t->tm_wday + 1) % 7;
2039 t->tm_hour = t->tm_min = t->tm_sec = 0;
2046 pig.restoreTo (checkpoint);
2050 ////////////////////////////////////////////////////////////////////////////////
2051 // eoww [ !<alpha> && !<digit> ]
2052 bool Datetime::initializeEoww (Pig& pig)
2054 auto checkpoint = pig.cursor ();
2056 if (pig.skipLiteral ("eoww"))
2058 auto following = pig.peek ();
2059 if (! unicodeLatinAlpha (following) &&
2060 ! unicodeLatinDigit (following))
2062 time_t now = time (nullptr);
2063 struct tm* t = localtime (&now);
2065 t->tm_mday += 6 - t->tm_wday;
2066 t->tm_hour = t->tm_min = t->tm_sec = 0;
2073 pig.restoreTo (checkpoint);
2077 ////////////////////////////////////////////////////////////////////////////////
2078 // eonww [ !<alpha> && !<digit> ]
2079 bool Datetime::initializeEonww (Pig& pig)
2081 auto checkpoint = pig.cursor ();
2083 if (pig.skipLiteral ("eonww"))
2085 auto following = pig.peek ();
2086 if (! unicodeLatinAlpha (following) &&
2087 ! unicodeLatinDigit (following))
2089 time_t now = time (nullptr);
2090 struct tm* t = localtime (&now);
2092 t->tm_mday += 13 - t->tm_wday;
2093 t->tm_hour = t->tm_min = t->tm_sec = 0;
2100 pig.restoreTo (checkpoint);
2104 ////////////////////////////////////////////////////////////////////////////////
2105 // sopm [ !<alpha> && !<digit> ]
2106 bool Datetime::initializeSopm (Pig& pig)
2108 auto checkpoint = pig.cursor ();
2110 if (pig.skipLiteral ("sopm"))
2112 auto following = pig.peek ();
2113 if (! unicodeLatinAlpha (following) &&
2114 ! unicodeLatinDigit (following))
2116 time_t now = time (nullptr);
2117 struct tm* t = localtime (&now);
2119 t->tm_hour = t->tm_min = t->tm_sec = 0;
2136 pig.restoreTo (checkpoint);
2140 ////////////////////////////////////////////////////////////////////////////////
2141 // som [ !<alpha> && !<digit> ]
2142 bool Datetime::initializeSom (Pig& pig)
2144 auto checkpoint = pig.cursor ();
2146 if (pig.skipLiteral ("som"))
2148 auto following = pig.peek ();
2149 if (! unicodeLatinAlpha (following) &&
2150 ! unicodeLatinDigit (following))
2152 time_t now = time (nullptr);
2153 struct tm* t = localtime (&now);
2155 t->tm_hour = t->tm_min = t->tm_sec = 0;
2163 pig.restoreTo (checkpoint);
2167 ////////////////////////////////////////////////////////////////////////////////
2168 // sonm [ !<alpha> && !<digit> ]
2169 bool Datetime::initializeSonm (Pig& pig)
2171 auto checkpoint = pig.cursor ();
2173 if (pig.skipLiteral ("sonm"))
2175 auto following = pig.peek ();
2176 if (! unicodeLatinAlpha (following) &&
2177 ! unicodeLatinDigit (following))
2179 time_t now = time (nullptr);
2180 struct tm* t = localtime (&now);
2182 t->tm_hour = t->tm_min = t->tm_sec = 0;
2198 pig.restoreTo (checkpoint);
2202 ////////////////////////////////////////////////////////////////////////////////
2203 // eopm [ !<alpha> && !<digit> ]
2204 bool Datetime::initializeEopm (Pig& pig)
2206 auto checkpoint = pig.cursor ();
2208 if (pig.skipLiteral ("eopm"))
2210 auto following = pig.peek ();
2211 if (! unicodeLatinAlpha (following) &&
2212 ! unicodeLatinDigit (following))
2214 time_t now = time (nullptr);
2215 struct tm* t = localtime (&now);
2217 t->tm_hour = t->tm_min = t->tm_sec = 0;
2225 pig.restoreTo (checkpoint);
2229 ////////////////////////////////////////////////////////////////////////////////
2230 // eom [ !<alpha> && !<digit> ]
2231 bool Datetime::initializeEom (Pig& pig)
2233 auto checkpoint = pig.cursor ();
2235 if (pig.skipLiteral ("eom"))
2237 auto following = pig.peek ();
2238 if (! unicodeLatinAlpha (following) &&
2239 ! unicodeLatinDigit (following))
2241 time_t now = time (nullptr);
2242 struct tm* t = localtime (&now);
2244 t->tm_hour = t->tm_min = t->tm_sec = 0;
2260 pig.restoreTo (checkpoint);
2264 ////////////////////////////////////////////////////////////////////////////////
2265 // eonm [ !<alpha> && !<digit> ]
2266 bool Datetime::initializeEonm (Pig& pig)
2268 auto checkpoint = pig.cursor ();
2270 if (pig.skipLiteral ("eonm"))
2272 auto following = pig.peek ();
2273 if (! unicodeLatinAlpha (following) &&
2274 ! unicodeLatinDigit (following))
2276 time_t now = time (nullptr);
2277 struct tm* t = localtime (&now);
2279 t->tm_hour = t->tm_min = t->tm_sec = 0;
2294 pig.restoreTo (checkpoint);
2298 ////////////////////////////////////////////////////////////////////////////////
2299 // sopq [ !<alpha> && !<digit> ]
2300 bool Datetime::initializeSopq (Pig& pig)
2302 auto checkpoint = pig.cursor ();
2304 if (pig.skipLiteral ("sopq"))
2306 auto following = pig.peek ();
2307 if (! unicodeLatinAlpha (following) &&
2308 ! unicodeLatinDigit (following))
2310 time_t now = time (nullptr);
2311 struct tm* t = localtime (&now);
2313 t->tm_mon -= t->tm_mon % 3;
2321 t->tm_hour = t->tm_min = t->tm_sec = 0;
2329 pig.restoreTo (checkpoint);
2333 ////////////////////////////////////////////////////////////////////////////////
2334 // soq [ !<alpha> && !<digit> ]
2335 bool Datetime::initializeSoq (Pig& pig)
2337 auto checkpoint = pig.cursor ();
2339 if (pig.skipLiteral ("soq"))
2341 auto following = pig.peek ();
2342 if (! unicodeLatinAlpha (following) &&
2343 ! unicodeLatinDigit (following))
2345 time_t now = time (nullptr);
2346 struct tm* t = localtime (&now);
2348 t->tm_hour = t->tm_min = t->tm_sec = 0;
2349 t->tm_mon -= t->tm_mon % 3;
2357 pig.restoreTo (checkpoint);
2361 ////////////////////////////////////////////////////////////////////////////////
2362 // sonq [ !<alpha> && !<digit> ]
2363 bool Datetime::initializeSonq (Pig& pig)
2365 auto checkpoint = pig.cursor ();
2367 if (pig.skipLiteral ("sonq"))
2369 auto following = pig.peek ();
2370 if (! unicodeLatinAlpha (following) &&
2371 ! unicodeLatinDigit (following))
2373 time_t now = time (nullptr);
2374 struct tm* t = localtime (&now);
2376 t->tm_mon += 3 - (t->tm_mon % 3);
2383 t->tm_hour = t->tm_min = t->tm_sec = 0;
2391 pig.restoreTo (checkpoint);
2395 ////////////////////////////////////////////////////////////////////////////////
2396 // eopq [ !<alpha> && !<digit> ]
2397 bool Datetime::initializeEopq (Pig& pig)
2399 auto checkpoint = pig.cursor ();
2401 if (pig.skipLiteral ("eopq"))
2403 auto following = pig.peek ();
2404 if (! unicodeLatinAlpha (following) &&
2405 ! unicodeLatinDigit (following))
2407 time_t now = time (nullptr);
2408 struct tm* t = localtime (&now);
2410 t->tm_hour = t->tm_min = t->tm_sec = 0;
2411 t->tm_mon -= t->tm_mon % 3;
2419 pig.restoreTo (checkpoint);
2423 ////////////////////////////////////////////////////////////////////////////////
2424 // eoq [ !<alpha> && !<digit> ]
2425 bool Datetime::initializeEoq (Pig& pig)
2427 auto checkpoint = pig.cursor ();
2429 if (pig.skipLiteral ("eoq"))
2431 auto following = pig.peek ();
2432 if (! unicodeLatinAlpha (following) &&
2433 ! unicodeLatinDigit (following))
2435 time_t now = time (nullptr);
2436 struct tm* t = localtime (&now);
2438 t->tm_mon += 3 - (t->tm_mon % 3);
2445 t->tm_hour = t->tm_min = t->tm_sec = 0;
2453 pig.restoreTo (checkpoint);
2457 ////////////////////////////////////////////////////////////////////////////////
2458 // eonq [ !<alpha> && !<digit> ]
2459 bool Datetime::initializeEonq (Pig& pig)
2461 auto checkpoint = pig.cursor ();
2463 if (pig.skipLiteral ("eonq"))
2465 auto following = pig.peek ();
2466 if (! unicodeLatinAlpha (following) &&
2467 ! unicodeLatinDigit (following))
2469 time_t now = time (nullptr);
2470 struct tm* t = localtime (&now);
2472 t->tm_hour = t->tm_min = t->tm_sec = 0;
2473 t->tm_mon += 6 - (t->tm_mon % 3);
2487 pig.restoreTo (checkpoint);
2491 ////////////////////////////////////////////////////////////////////////////////
2492 // sopy [ !<alpha> && !<digit> ]
2493 bool Datetime::initializeSopy (Pig& pig)
2495 auto checkpoint = pig.cursor ();
2497 if (pig.skipLiteral ("sopy"))
2499 auto following = pig.peek ();
2500 if (! unicodeLatinAlpha (following) &&
2501 ! unicodeLatinDigit (following))
2503 time_t now = time (nullptr);
2504 struct tm* t = localtime (&now);
2506 t->tm_hour = t->tm_min = t->tm_sec = 0;
2516 pig.restoreTo (checkpoint);
2520 ////////////////////////////////////////////////////////////////////////////////
2521 // soy [ !<alpha> && !<digit> ]
2522 bool Datetime::initializeSoy (Pig& pig)
2524 auto checkpoint = pig.cursor ();
2526 if (pig.skipLiteral ("soy"))
2528 auto following = pig.peek ();
2529 if (! unicodeLatinAlpha (following) &&
2530 ! unicodeLatinDigit (following))
2532 time_t now = time (nullptr);
2533 struct tm* t = localtime (&now);
2535 t->tm_hour = t->tm_min = t->tm_sec = 0;
2544 pig.restoreTo (checkpoint);
2548 ////////////////////////////////////////////////////////////////////////////////
2549 // sony [ !<alpha> && !<digit> ]
2550 bool Datetime::initializeSony (Pig& pig)
2552 auto checkpoint = pig.cursor ();
2554 if (pig.skipLiteral ("sony"))
2556 auto following = pig.peek ();
2557 if (! unicodeLatinAlpha (following) &&
2558 ! unicodeLatinDigit (following))
2560 time_t now = time (nullptr);
2561 struct tm* t = localtime (&now);
2563 t->tm_hour = t->tm_min = t->tm_sec = 0;
2573 pig.restoreTo (checkpoint);
2577 ////////////////////////////////////////////////////////////////////////////////
2578 // eopy [ !<alpha> && !<digit> ]
2579 bool Datetime::initializeEopy (Pig& pig)
2581 auto checkpoint = pig.cursor ();
2583 if (pig.skipLiteral ("eopy"))
2585 auto following = pig.peek ();
2586 if (! unicodeLatinAlpha (following) &&
2587 ! unicodeLatinDigit (following))
2589 time_t now = time (nullptr);
2590 struct tm* t = localtime (&now);
2592 t->tm_hour = t->tm_min = t->tm_sec = 0;
2601 pig.restoreTo (checkpoint);
2605 ////////////////////////////////////////////////////////////////////////////////
2606 // eoy [ !<alpha> && !<digit> ]
2607 bool Datetime::initializeEoy (Pig& pig)
2609 auto checkpoint = pig.cursor ();
2611 if (pig.skipLiteral ("eoy"))
2613 auto following = pig.peek ();
2614 if (! unicodeLatinAlpha (following) &&
2615 ! unicodeLatinDigit (following))
2617 time_t now = time (nullptr);
2618 struct tm* t = localtime (&now);
2620 t->tm_hour = t->tm_min = t->tm_sec = 0;
2630 pig.restoreTo (checkpoint);
2634 ////////////////////////////////////////////////////////////////////////////////
2635 // eony [ !<alpha> && !<digit> ]
2636 bool Datetime::initializeEony (Pig& pig)
2638 auto checkpoint = pig.cursor ();
2640 if (pig.skipLiteral ("eony"))
2642 auto following = pig.peek ();
2643 if (! unicodeLatinAlpha (following) &&
2644 ! unicodeLatinDigit (following))
2646 time_t now = time (nullptr);
2647 struct tm* t = localtime (&now);
2649 t->tm_hour = t->tm_min = t->tm_sec = 0;
2659 pig.restoreTo (checkpoint);
2663 ////////////////////////////////////////////////////////////////////////////////
2664 // easter [ !<alpha> && !<digit> ]
2665 // eastermonday [ !<alpha> && !<digit> ]
2666 // ascension [ !<alpha> && !<digit> ]
2667 // pentecost [ !<alpha> && !<digit> ]
2668 // goodfriday [ !<alpha> && !<digit> ]
2669 bool Datetime::initializeEaster (Pig& pig)
2671 auto checkpoint = pig.cursor ();
2673 std::vector <std::string> holidays = {"eastermonday", "easter", "ascension", "pentecost", "goodfriday"};
2674 std::vector <int> offsets = { 1, 0, 39, 49, -2};
2677 for (int holiday = 0; holiday < 5; ++holiday)
2679 if (pig.skipLiteral (holidays[holiday]) &&
2680 ! unicodeLatinAlpha (pig.peek ()) &&
2681 ! unicodeLatinDigit (pig.peek ()))
2683 time_t now = time (nullptr);
2684 struct tm* t = localtime (&now);
2689 // If the result is earlier this year, then recalc for next year.
2692 t = localtime (&now);
2697 // Adjust according to holiday-specific offsets.
2698 t->tm_mday += offsets[holiday];
2705 pig.restoreTo (checkpoint);
2709 ////////////////////////////////////////////////////////////////////////////////
2710 // midsommar [ !<alpha> && !<digit> ]
2711 bool Datetime::initializeMidsommar (Pig& pig)
2713 auto checkpoint = pig.cursor ();
2715 if (pig.skipLiteral ("midsommar"))
2717 auto following = pig.peek ();
2718 if (! unicodeLatinAlpha (following) &&
2719 ! unicodeLatinDigit (following))
2721 time_t now = time (nullptr);
2722 struct tm* t = localtime (&now);
2726 // If the result is earlier this year, then recalc for next year.
2729 t = localtime (&now);
2739 pig.restoreTo (checkpoint);
2743 ////////////////////////////////////////////////////////////////////////////////
2744 // midsommarafton [ !<alpha> && !<digit> ]
2745 // juhannus [ !<alpha> && !<digit> ]
2746 bool Datetime::initializeMidsommarafton (Pig& pig)
2748 auto checkpoint = pig.cursor ();
2750 if (pig.skipLiteral ("midsommarafton") ||
2751 pig.skipLiteral ("juhannus"))
2753 auto following = pig.peek ();
2754 if (! unicodeLatinAlpha (following) &&
2755 ! unicodeLatinDigit (following))
2757 time_t now = time (nullptr);
2758 struct tm* t = localtime (&now);
2762 // If the result is earlier this year, then recalc for next year.
2765 t = localtime (&now);
2775 pig.restoreTo (checkpoint);
2779 ////////////////////////////////////////////////////////////////////////////////
2786 // \d+ [ : \d{2} ] [ am | a | pm | p ] [ !<alpha> && !<digit> && !: && !+ && !- ]
2788 bool Datetime::initializeInformalTime (Pig& pig)
2790 auto checkpoint = pig.cursor ();
2793 bool needDesignator = true; // Require am/pm.
2794 bool haveDesignator = false; // Provided am/pm.
2795 if (pig.getDigit (digit))
2798 if (pig.getDigit (digit))
2799 hours = 10 * hours + digit;
2805 if (! pig.getDigit2 (minutes))
2807 pig.restoreTo (checkpoint);
2813 if (! pig.getDigits (seconds))
2815 pig.restoreTo (checkpoint);
2820 needDesignator = false;
2823 if (pig.skipLiteral ("am") ||
2824 pig.skipLiteral ("a"))
2826 haveDesignator = true;
2831 else if (pig.skipLiteral ("pm") ||
2832 pig.skipLiteral ("p"))
2834 // Note: '12pm is an exception:
2842 haveDesignator = true;
2845 // Informal time needs to be terminated.
2846 auto following = pig.peek ();
2847 if (unicodeLatinAlpha (following) ||
2848 unicodeLatinDigit (following) ||
2853 pig.restoreTo (checkpoint);
2857 if (haveDesignator || ! needDesignator)
2859 // Midnight today + hours:minutes:seconds.
2860 time_t now = time (nullptr);
2861 struct tm* t = localtime (&now);
2863 int now_seconds = (t->tm_hour * 3600) + (t->tm_min * 60) + t->tm_sec;
2864 int calc_seconds = (hours * 3600) + (minutes * 60) + seconds;
2866 if (calc_seconds < now_seconds)
2869 // Basic validation.
2870 if (hours >= 0 && hours < 24 &&
2871 minutes >= 0 && minutes < 60 &&
2872 seconds >= 0 && seconds < 60)
2875 t->tm_min = minutes;
2876 t->tm_sec = seconds;
2885 pig.restoreTo (checkpoint);
2889 ////////////////////////////////////////////////////////////////////////////////
2890 void Datetime::easter (struct tm* t) const
2892 int Y = t->tm_year + 1900;
2898 int f = (b + 8) / 25;
2899 int g = (b - f + 1) / 3;
2900 int h = (19 * a + b - d - g + 15) % 30;
2903 int L = (32 + 2 * e + 2 * i - h - k) % 7;
2904 int m = (a + 11 * h + 22 * L) / 451;
2905 int month = (h + L - 7 * m + 114) / 31;
2906 int day = ((h + L - 7 * m + 114) % 31) + 1;
2908 t->tm_isdst = -1; // Requests that mktime determine summer time effect.
2910 t->tm_mon = month - 1;
2911 t->tm_year = Y - 1900;
2913 t->tm_hour = t->tm_min = t->tm_sec = 0;
2916 ////////////////////////////////////////////////////////////////////////////////
2917 void Datetime::midsommar (struct tm* t) const
2919 t->tm_mon = 5; // June.
2920 t->tm_mday = 20; // Saturday after 20th.
2921 t->tm_hour = t->tm_min = t->tm_sec = 0; // Midnight.
2922 t->tm_isdst = -1; // Probably DST, but check.
2924 time_t then = mktime (t); // Obtain the weekday of June 20th.
2925 struct tm* mid = localtime (&then);
2926 t->tm_mday += 6 - mid->tm_wday; // How many days after 20th.
2929 ////////////////////////////////////////////////////////////////////////////////
2930 void Datetime::midsommarafton (struct tm* t) const
2932 t->tm_mon = 5; // June.
2933 t->tm_mday = 19; // Saturday after 20th.
2934 t->tm_hour = t->tm_min = t->tm_sec = 0; // Midnight.
2935 t->tm_isdst = -1; // Probably DST, but check.
2937 time_t then = mktime (t); // Obtain the weekday of June 19th.
2938 struct tm* mid = localtime (&then);
2939 t->tm_mday += 5 - mid->tm_wday; // How many days after 19th.
2942 ////////////////////////////////////////////////////////////////////////////////
2943 // Suggested date expressions:
2944 // {ordinal} {day} in|of {month}
2945 // last|past|next|this {day}
2946 // last|past|next|this {month}
2947 // last|past|next|this week
2948 // last|past|next|this month
2949 // last|past|next|this weekend
2950 // last|past|next|this year
2951 // {day} last|past|next|this week
2952 // {day} [at] {time}
2959 // Friday before easter
2960 // 3 days before eom
2966 // tomorrow in one year
2969 // 2 weeks ago tuesday
2970 // thursday in 2 weeks
2971 // last day next month
2972 // 10 days from today
2973 // thursday before last weekend in may
2974 // friday last full week in may
2975 // 3rd wednesday this month
2976 // 3 weeks after 2nd tuesday next month
2977 // 100 days from the beginning of the month
2978 // 10 days after last monday
2979 // sunday in the evening
2987 // next business day
2994 // - {number} {unit}
2995 // {ordinal} {unit} in {larger-unit}
2996 // end of day tomorrow
2999 // first thing {day}
3002 ////////////////////////////////////////////////////////////////////////////////
3003 // <ordinal> <weekday> in|of <month>
3004 bool Datetime::initializeNthDayInMonth (const std::vector <std::string>& tokens)
3006 if (tokens.size () == 4)
3009 if (isOrdinal (tokens[0], ordinal))
3011 auto day = Datetime::dayOfWeek (tokens[1]);
3014 if (tokens[2] == "in" ||
3017 auto month = Datetime::monthOfYear (tokens[3]);
3020 std::cout << "# ordinal=" << ordinal << " day=" << day << " in month=" << month << '\n';
3022 // TODO Assume 1st of the month
3023 // TODO Assume current year
3024 // TODO Determine the day
3025 // TODO Project forwards/backwards, to the desired day
3026 // TODO Add ((ordinal - 1) * 7) days
3038 ////////////////////////////////////////////////////////////////////////////////
3039 bool Datetime::isOrdinal (const std::string& token, int& ordinal)
3044 if (p.getDigits (number) &&
3045 p.getRemainder (suffix))
3047 if (((number >= 11 || number <= 13) && suffix == "th") ||
3048 (number % 10 == 1 && suffix == "st") ||
3049 (number % 10 == 2 && suffix == "nd") ||
3050 (number % 10 == 3 && suffix == "rd") ||
3061 ////////////////////////////////////////////////////////////////////////////////
3062 // Validation via simple range checking.
3063 bool Datetime::validate ()
3066 if ((_year && (_year < 1900 || _year > 2200)) ||
3067 (_month && (_month < 1 || _month > 12)) ||
3068 (_week && (_week < 1 || _week > 53)) ||
3069 (_weekday && (_weekday < 0 || _weekday > 6)) ||
3070 (_julian && (_julian < 1 || _julian > Datetime::daysInYear (_year))) ||
3071 (_day && (_day < 1 || _day > Datetime::daysInMonth (_year, _month))) ||
3072 (_seconds && (_seconds < 1 || _seconds > 86400)) ||
3073 (_offset && (_offset < -86400 || _offset > 86400)))
3079 ////////////////////////////////////////////////////////////////////////////////
3080 // int tm_sec; seconds (0 - 60)
3081 // int tm_min; minutes (0 - 59)
3082 // int tm_hour; hours (0 - 23)
3083 // int tm_mday; day of month (1 - 31)
3084 // int tm_mon; month of year (0 - 11)
3085 // int tm_year; year - 1900
3086 // int tm_wday; day of week (Sunday = 0)
3087 // int tm_yday; day of year (0 - 365)
3088 // int tm_isdst; is summer time in effect?
3089 // char *tm_zone; abbreviation of timezone name
3090 // long tm_gmtoff; offset from UTC in seconds
3091 void Datetime::resolve ()
3093 // Don't touch the original values.
3097 int weekday = _weekday;
3098 int julian = _julian;
3100 int seconds = _seconds;
3101 int offset = _offset;
3104 // Get current time.
3105 time_t now = time (nullptr);
3107 // A UTC offset needs to be accommodated. Once the offset is subtracted,
3108 // only local and UTC times remain.
3116 // Get 'now' in the relevant location.
3117 struct tm* t_now = utc ? gmtime (&now) : localtime (&now);
3119 int seconds_now = (t_now->tm_hour * 3600) +
3120 (t_now->tm_min * 60) +
3123 // Project forward one day if the specified seconds are earlier in the day
3124 // than the current seconds.
3125 // TODO This does not cover the inverse case of subtracting 86400.
3131 seconds < seconds_now)
3136 // Convert week + weekday --> julian.
3139 julian = (week * 7) + weekday - dayOfWeek (year, 1, 4) - 3;
3142 // Provide default values for year, month, day.
3145 // Default values for year, month, day:
3150 // - - - --> now now now
3154 year = t_now->tm_year + 1900;
3155 month = t_now->tm_mon + 1;
3156 day = t_now->tm_mday;
3177 t.tm_isdst = -1; // Requests that mktime/gmtime determine summer time effect.
3178 t.tm_year = year - 1900;
3179 t.tm_mon = month - 1;
3182 if (seconds > 86400)
3184 int days = seconds / 86400;
3189 t.tm_hour = seconds / 3600;
3190 t.tm_min = (seconds % 3600) / 60;
3191 t.tm_sec = seconds % 60;
3193 _date = utc ? timegm (&t) : mktime (&t);
3196 ////////////////////////////////////////////////////////////////////////////////
3197 time_t Datetime::toEpoch () const
3202 ////////////////////////////////////////////////////////////////////////////////
3203 std::string Datetime::toEpochString () const
3205 return format ("{1}", _date);
3208 ////////////////////////////////////////////////////////////////////////////////
3209 // 19980119T070000Z = YYYYMMDDThhmmssZ
3210 std::string Datetime::toISO () const
3212 struct tm* t = gmtime (&_date);
3214 std::stringstream iso;
3215 iso << std::setw (4) << std::setfill ('0') << t->tm_year + 1900
3216 << std::setw (2) << std::setfill ('0') << t->tm_mon + 1
3217 << std::setw (2) << std::setfill ('0') << t->tm_mday
3219 << std::setw (2) << std::setfill ('0') << t->tm_hour
3220 << std::setw (2) << std::setfill ('0') << t->tm_min
3221 << std::setw (2) << std::setfill ('0') << t->tm_sec
3227 ////////////////////////////////////////////////////////////////////////////////
3228 // 1998-01-19T07:00:00 = YYYY-MM-DDThh:mm:ss
3229 std::string Datetime::toISOLocalExtended () const
3231 struct tm* t = localtime (&_date);
3233 std::stringstream iso;
3234 iso << std::setw (4) << std::setfill ('0') << t->tm_year + 1900
3236 << std::setw (2) << std::setfill ('0') << t->tm_mon + 1
3238 << std::setw (2) << std::setfill ('0') << t->tm_mday
3240 << std::setw (2) << std::setfill ('0') << t->tm_hour
3242 << std::setw (2) << std::setfill ('0') << t->tm_min
3244 << std::setw (2) << std::setfill ('0') << t->tm_sec;
3249 ////////////////////////////////////////////////////////////////////////////////
3250 double Datetime::toJulian () const
3252 return (_date / 86400.0) + 2440587.5;
3255 ////////////////////////////////////////////////////////////////////////////////
3256 void Datetime::toYMD (int& y, int& m, int& d) const
3258 struct tm* t = localtime (&_date);
3262 y = t->tm_year + 1900;
3265 ////////////////////////////////////////////////////////////////////////////////
3266 const std::string Datetime::toString (const std::string& format) const
3268 std::stringstream formatted;
3269 for (unsigned int i = 0; i < format.length (); ++i)
3274 case 'm': formatted << month (); break;
3275 case 'M': formatted << std::setw (2) << std::setfill ('0') << month (); break;
3276 case 'd': formatted << day (); break;
3277 case 'D': formatted << std::setw (2) << std::setfill ('0') << day (); break;
3278 case 'y': formatted << std::setw (2) << std::setfill ('0') << (year () % 100); break;
3279 case 'Y': formatted << year (); break;
3280 case 'a': formatted << Datetime::dayNameShort (dayOfWeek ()); break;
3281 case 'A': formatted << Datetime::dayName (dayOfWeek ()); break;
3282 case 'b': formatted << Datetime::monthNameShort (month ()); break;
3283 case 'B': formatted << Datetime::monthName (month ()); break;
3284 case 'v': formatted << week (); break;
3285 case 'V': formatted << std::setw (2) << std::setfill ('0') << week (); break;
3286 case 'h': formatted << hour (); break;
3287 case 'H': formatted << std::setw (2) << std::setfill ('0') << hour (); break;
3288 case 'n': formatted << minute (); break;
3289 case 'N': formatted << std::setw (2) << std::setfill ('0') << minute (); break;
3290 case 's': formatted << second (); break;
3291 case 'S': formatted << std::setw (2) << std::setfill ('0') << second (); break;
3292 case 'j': formatted << dayOfYear (); break;
3293 case 'J': formatted << std::setw (3) << std::setfill ('0') << dayOfYear (); break;
3294 case 'w': formatted << dayOfWeek (); break;
3295 default: formatted << static_cast <char> (c); break;
3299 return formatted.str ();
3302 ////////////////////////////////////////////////////////////////////////////////
3303 Datetime Datetime::startOfDay () const
3305 return Datetime (year (), month (), day ());
3308 ////////////////////////////////////////////////////////////////////////////////
3309 Datetime Datetime::startOfWeek () const
3311 Datetime sow (_date);
3312 sow -= (dayOfWeek () * 86400);
3313 return Datetime (sow.year (), sow.month (), sow.day ());
3316 ////////////////////////////////////////////////////////////////////////////////
3317 Datetime Datetime::startOfMonth () const
3319 return Datetime (year (), month (), 1);
3322 ////////////////////////////////////////////////////////////////////////////////
3323 Datetime Datetime::startOfYear () const
3325 return Datetime (year (), 1, 1);
3328 ////////////////////////////////////////////////////////////////////////////////
3329 bool Datetime::valid (const std::string& input, const std::string& format)
3333 Datetime test (input, format);
3344 ////////////////////////////////////////////////////////////////////////////////
3345 bool Datetime::valid (
3346 const int y, const int m, const int d,
3347 const int hr, const int mi, const int se)
3349 if (hr < 0 || hr > 24)
3352 if (mi < 0 || mi > 59)
3355 if (se < 0 || se > 59)
3363 return Datetime::valid (y, m, d);
3366 ////////////////////////////////////////////////////////////////////////////////
3367 bool Datetime::valid (const int y, const int m, const int d)
3369 // Check that the year is valid.
3373 // Check that the month is valid.
3374 if (m < 1 || m > 12)
3377 // Finally check that the days fall within the acceptable range for this
3378 // month, and whether or not this is a leap year.
3379 if (d < 1 || d > Datetime::daysInMonth (y, m))
3385 ////////////////////////////////////////////////////////////////////////////////
3387 bool Datetime::valid (const int y, const int d)
3389 // Check that the year is valid.
3393 if (d < 1 || d > Datetime::daysInYear (y))
3399 ////////////////////////////////////////////////////////////////////////////////
3401 bool Datetime::leapYear (int year)
3403 return ((! (year % 4)) && (year % 100)) ||
3407 ////////////////////////////////////////////////////////////////////////////////
3409 int Datetime::daysInMonth (int year, int month)
3411 // Protect against arguments being passed in the wrong order.
3412 assert (year >= 1969 && year < 2100);
3413 assert (month >= 1 && month <= 31);
3415 static int days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
3417 if (month == 2 && Datetime::leapYear (year))
3420 return days[month - 1];
3423 ////////////////////////////////////////////////////////////////////////////////
3425 int Datetime::daysInYear (int year)
3427 return Datetime::leapYear (year) ? 366 : 365;
3430 ////////////////////////////////////////////////////////////////////////////////
3432 std::string Datetime::monthName (int month)
3435 assert (month <= 12);
3436 return upperCaseFirst (monthNames[month - 1]);
3439 ////////////////////////////////////////////////////////////////////////////////
3441 std::string Datetime::monthNameShort (int month)
3444 assert (month <= 12);
3445 return upperCaseFirst (monthNames[month - 1]).substr (0, 3);
3448 ////////////////////////////////////////////////////////////////////////////////
3450 std::string Datetime::dayName (int dow)
3454 return upperCaseFirst (dayNames[dow]);
3457 ////////////////////////////////////////////////////////////////////////////////
3459 std::string Datetime::dayNameShort (int dow)
3463 return upperCaseFirst (dayNames[dow]).substr (0, 3);
3466 ////////////////////////////////////////////////////////////////////////////////
3468 int Datetime::dayOfWeek (const std::string& input)
3470 if (Datetime::minimumMatchLength== 0)
3471 Datetime::minimumMatchLength = 3;
3473 for (unsigned int i = 0; i < dayNames.size (); ++i)
3474 if (closeEnough (dayNames[i], input, Datetime::minimumMatchLength))
3480 ////////////////////////////////////////////////////////////////////////////////
3481 // Using Zeller's Congruence.
3483 int Datetime::dayOfWeek (int year, int month, int day)
3485 int adj = (14 - month) / 12;
3486 int m = month + 12 * adj - 2;
3488 return (day + (13 * m - 1) / 5 + y + y / 4 - y / 100 + y / 400) % 7;
3491 ////////////////////////////////////////////////////////////////////////////////
3493 int Datetime::monthOfYear (const std::string& input)
3495 if (Datetime::minimumMatchLength== 0)
3496 Datetime::minimumMatchLength = 3;
3498 for (unsigned int i = 0; i < monthNames.size (); ++i)
3499 if (closeEnough (monthNames[i], input, Datetime::minimumMatchLength))
3505 ////////////////////////////////////////////////////////////////////////////////
3507 int Datetime::length (const std::string& format)
3510 for (auto& i : format)
3526 case 'S': len += 2; break;
3530 case 'a': len += 3; break;
3531 case 'Y': len += 4; break;
3533 case 'B': len += 10; break;
3535 // Calculate the width, don't assume a single character width.
3536 default: len += mk_wcwidth (i); break;
3543 ////////////////////////////////////////////////////////////////////////////////
3544 int Datetime::month () const
3546 struct tm* t = localtime (&_date);
3547 return t->tm_mon + 1;
3550 ////////////////////////////////////////////////////////////////////////////////
3551 int Datetime::week () const
3553 struct tm* t = localtime (&_date);
3556 if (Datetime::weekstart == 0)
3557 strftime (weekStr, sizeof (weekStr), "%U", t);
3558 else if (Datetime::weekstart == 1)
3559 strftime (weekStr, sizeof (weekStr), "%V", t);
3561 throw std::string ("The week may only start on a Sunday or Monday.");
3563 int weekNumber = strtol (weekStr, nullptr, 10);
3570 ////////////////////////////////////////////////////////////////////////////////
3571 int Datetime::day () const
3573 struct tm* t = localtime (&_date);
3577 ////////////////////////////////////////////////////////////////////////////////
3578 int Datetime::year () const
3580 struct tm* t = localtime (&_date);
3581 return t->tm_year + 1900;
3584 ////////////////////////////////////////////////////////////////////////////////
3585 int Datetime::dayOfWeek () const
3587 struct tm* t = localtime (&_date);
3591 ////////////////////////////////////////////////////////////////////////////////
3592 int Datetime::dayOfYear () const
3594 struct tm* t = localtime (&_date);
3595 return t->tm_yday + 1;
3598 ////////////////////////////////////////////////////////////////////////////////
3599 int Datetime::hour () const
3601 struct tm* t = localtime (&_date);
3605 ////////////////////////////////////////////////////////////////////////////////
3606 int Datetime::minute () const
3608 struct tm* t = localtime (&_date);
3612 ////////////////////////////////////////////////////////////////////////////////
3613 int Datetime::second () const
3615 struct tm* t = localtime (&_date);
3619 ////////////////////////////////////////////////////////////////////////////////
3620 bool Datetime::operator== (const Datetime& rhs) const
3622 return rhs._date == _date;
3625 ////////////////////////////////////////////////////////////////////////////////
3626 bool Datetime::operator!= (const Datetime& rhs) const
3628 return rhs._date != _date;
3631 ////////////////////////////////////////////////////////////////////////////////
3632 bool Datetime::operator< (const Datetime& rhs) const
3634 return _date < rhs._date;
3637 ////////////////////////////////////////////////////////////////////////////////
3638 bool Datetime::operator> (const Datetime& rhs) const
3640 return _date > rhs._date;
3643 ////////////////////////////////////////////////////////////////////////////////
3644 bool Datetime::operator<= (const Datetime& rhs) const
3646 return _date <= rhs._date;
3649 ////////////////////////////////////////////////////////////////////////////////
3650 bool Datetime::operator>= (const Datetime& rhs) const
3652 return _date >= rhs._date;
3655 ////////////////////////////////////////////////////////////////////////////////
3656 bool Datetime::sameHour (const Datetime& rhs) const
3658 return year () == rhs.year () &&
3659 month () == rhs.month () &&
3660 day () == rhs.day () &&
3661 hour () == rhs.hour ();
3664 ////////////////////////////////////////////////////////////////////////////////
3665 bool Datetime::sameDay (const Datetime& rhs) const
3667 return year () == rhs.year () &&
3668 month () == rhs.month () &&
3669 day () == rhs.day ();
3672 ////////////////////////////////////////////////////////////////////////////////
3673 bool Datetime::sameWeek (const Datetime& rhs) const
3675 return year () == rhs.year () &&
3676 week () == rhs.week ();
3679 ////////////////////////////////////////////////////////////////////////////////
3680 bool Datetime::sameMonth (const Datetime& rhs) const
3682 return year () == rhs.year () &&
3683 month () == rhs.month ();
3686 ////////////////////////////////////////////////////////////////////////////////
3687 bool Datetime::sameQuarter (const Datetime& rhs) const
3689 return year () == rhs.year () &&
3690 ((month () - 1) / 3) == ((rhs.month () - 1) / 3);
3693 ////////////////////////////////////////////////////////////////////////////////
3694 bool Datetime::sameYear (const Datetime& rhs) const
3696 return year () == rhs.year ();
3699 ////////////////////////////////////////////////////////////////////////////////
3700 Datetime Datetime::operator+ (const int delta)
3702 return Datetime (_date + delta);
3705 ////////////////////////////////////////////////////////////////////////////////
3706 Datetime Datetime::operator- (const int delta)
3708 return Datetime (_date - delta);
3711 ////////////////////////////////////////////////////////////////////////////////
3712 Datetime& Datetime::operator+= (const int delta)
3714 _date += (time_t) delta;
3718 ////////////////////////////////////////////////////////////////////////////////
3719 Datetime& Datetime::operator-= (const int delta)
3721 _date -= (time_t) delta;
3725 ////////////////////////////////////////////////////////////////////////////////
3726 time_t Datetime::operator- (const Datetime& rhs)
3728 return _date - rhs._date;
3731 ////////////////////////////////////////////////////////////////////////////////
3732 // Prefix decrement by one day.
3733 void Datetime::operator-- ()
3735 Datetime yesterday = startOfDay () - 1;
3736 yesterday = Datetime (yesterday.year (),
3742 _date = yesterday._date;
3745 ////////////////////////////////////////////////////////////////////////////////
3746 // Postfix decrement by one day.
3747 void Datetime::operator-- (int)
3749 Datetime yesterday = startOfDay () - 1;
3750 yesterday = Datetime (yesterday.year (),
3756 _date = yesterday._date;
3759 ////////////////////////////////////////////////////////////////////////////////
3760 // Prefix increment by one day.
3761 void Datetime::operator++ ()
3763 Datetime tomorrow = (startOfDay () + 90001).startOfDay ();
3764 tomorrow = Datetime (tomorrow.year (),
3770 _date = tomorrow._date;
3773 ////////////////////////////////////////////////////////////////////////////////
3774 // Postfix increment by one day.
3775 void Datetime::operator++ (int)
3777 Datetime tomorrow = (startOfDay () + 90001).startOfDay ();
3778 tomorrow = Datetime (tomorrow.year (),
3784 _date = tomorrow._date;
3787 ////////////////////////////////////////////////////////////////////////////////