diff options
author | Derick Rethans <github@derickrethans.nl> | 2018-10-09 11:07:15 +0100 |
---|---|---|
committer | Derick Rethans <github@derickrethans.nl> | 2018-10-09 11:07:15 +0100 |
commit | d16e23d8c92052119779fd7fa98dd38c379a478a (patch) | |
tree | a746628ebb6c6fbd40c1f849e3d85a709505b30e /ext | |
parent | 5527c74c5308c8b299d72bdf4979a64d7daf9371 (diff) | |
parent | bb2eaa6d6112bfc4213b731543cc0a3165793e44 (diff) | |
download | php-git-d16e23d8c92052119779fd7fa98dd38c379a478a.tar.gz |
Merge branch 'PHP-7.3'
Diffstat (limited to 'ext')
-rw-r--r-- | ext/date/lib/parse_date.c | 611 | ||||
-rw-r--r-- | ext/date/lib/parse_date.re | 343 | ||||
-rw-r--r-- | ext/date/lib/parse_iso_intervals.c | 86 | ||||
-rw-r--r-- | ext/date/lib/parse_tz.c | 2 | ||||
-rw-r--r-- | ext/date/lib/timelib.h | 91 | ||||
-rw-r--r-- | ext/date/lib/timezonemap.h | 14 | ||||
-rw-r--r-- | ext/date/lib/tm2unixtime.c | 2 | ||||
-rw-r--r-- | ext/date/lib/unixtime2tm.c | 4 | ||||
-rw-r--r-- | ext/date/tests/bug75577.phpt | 22 |
9 files changed, 834 insertions, 341 deletions
diff --git a/ext/date/lib/parse_date.c b/ext/date/lib/parse_date.c index e79e0ebf61..5be4d36e67 100644 --- a/ext/date/lib/parse_date.c +++ b/ext/date/lib/parse_date.c @@ -1,5 +1,5 @@ -/* Generated by re2c 0.15.3 on Wed Jan 31 11:51:51 2018 */ -#line 1 "ext/date/lib/parse_date.re" +/* Generated by re2c 0.15.3 on Tue Oct 9 10:50:25 2018 */ +#line 1 "parse_date.re" /* * The MIT License (MIT) * @@ -24,6 +24,8 @@ * THE SOFTWARE. */ +/* $Id$ */ + #include "timelib.h" #include "timelib_private.h" @@ -141,9 +143,9 @@ typedef struct _Scanner { } Scanner; typedef struct _timelib_lookup_table { - const char *name; - int type; - int value; + const char *name; + int type; + int value; } timelib_lookup_table; typedef struct _timelib_relunit { @@ -619,7 +621,7 @@ static const timelib_relunit* timelib_lookup_relunit(char **ptr) const timelib_relunit *tp, *value = NULL; while (**ptr != '\0' && **ptr != ' ' && **ptr != ',' && **ptr != '\t' && **ptr != ';' && **ptr != ':' && - **ptr != '/' && **ptr != '.' && **ptr != '-' && **ptr != '(' && **ptr != ')' ) { + **ptr != '/' && **ptr != '.' && **ptr != '-' && **ptr != '(' && **ptr != ')' ) { ++*ptr; } end = *ptr; @@ -771,6 +773,37 @@ static timelib_long timelib_parse_tz_cor(char **ptr) return 0; } +static timelib_long timelib_parse_tz_minutes(char **ptr, timelib_time *t) +{ + timelib_long retval = TIMELIB_UNSET; + char *begin = *ptr; + + /* First character must be +/- */ + if (**ptr != '+' && **ptr != '-') { + return retval; + } + + ++*ptr; + while (isdigit(**ptr)) { + ++*ptr; + } + + if (*begin == '+') { + t->is_localtime = 1; + t->zone_type = TIMELIB_ZONETYPE_OFFSET; + t->dst = 0; + + retval = sMIN(strtol(begin + 1, NULL, 10)); + } else if (*begin == '-') { + t->is_localtime = 1; + t->zone_type = TIMELIB_ZONETYPE_OFFSET; + t->dst = 0; + + retval = -1 * sMIN(strtol(begin + 1, NULL, 10)); + } + return retval; +} + timelib_long timelib_parse_zone(char **ptr, int *dst, timelib_time *t, int *tz_not_found, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_wrapper) { timelib_tzinfo *res; @@ -852,47 +885,47 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) std: s->tok = cursor; s->len = 0; -#line 981 "ext/date/lib/parse_date.re" +#line 1012 "parse_date.re" -#line 862 "<stdout>" +#line 893 "<stdout>" { YYCTYPE yych; unsigned int yyaccept = 0; static const unsigned char yybm[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 100, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 100, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 128, 64, 160, 96, 0, - 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 0, 0, 0, 0, 0, 0, - 0, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 0, 0, 0, 0, 0, - 0, 24, 24, 24, 88, 24, 24, 24, - 88, 24, 24, 24, 24, 24, 88, 24, - 24, 24, 88, 88, 88, 24, 24, 24, - 24, 24, 24, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 100, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 100, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 128, 64, 160, 96, 0, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 0, 0, 0, 0, 0, 0, + 0, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 0, 0, 0, 0, 0, + 0, 24, 24, 24, 88, 24, 24, 24, + 88, 24, 24, 24, 24, 24, 88, 24, + 24, 24, 88, 88, 88, 24, 24, 24, + 24, 24, 24, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, }; YYDEBUG(0, *YYCURSOR); if ((YYLIMIT - YYCURSOR) < 33) YYFILL(33); @@ -994,7 +1027,7 @@ yy2: } yy3: YYDEBUG(3, *YYCURSOR); -#line 1689 "ext/date/lib/parse_date.re" +#line 1720 "parse_date.re" { int tz_not_found; DEBUG_OUTPUT("tzcorrection | tz"); @@ -1007,7 +1040,7 @@ yy3: TIMELIB_DEINIT; return TIMELIB_TIMEZONE; } -#line 1013 "<stdout>" +#line 1044 "<stdout>" yy4: YYDEBUG(4, *YYCURSOR); yych = *++YYCURSOR; @@ -1316,12 +1349,12 @@ yy11: if (yych <= '9') goto yy1377; yy12: YYDEBUG(12, *YYCURSOR); -#line 1784 "ext/date/lib/parse_date.re" +#line 1815 "parse_date.re" { add_error(s, TIMELIB_ERR_UNEXPECTED_CHARACTER, "Unexpected character"); goto std; } -#line 1327 "<stdout>" +#line 1358 "<stdout>" yy13: YYDEBUG(13, *YYCURSOR); yych = *++YYCURSOR; @@ -2565,11 +2598,11 @@ yy48: if (yych <= '9') goto yy54; yy49: YYDEBUG(49, *YYCURSOR); -#line 1773 "ext/date/lib/parse_date.re" +#line 1804 "parse_date.re" { goto std; } -#line 2575 "<stdout>" +#line 2606 "<stdout>" yy50: YYDEBUG(50, *YYCURSOR); yych = *++YYCURSOR; @@ -2578,12 +2611,12 @@ yy51: YYDEBUG(51, *YYCURSOR); ++YYCURSOR; YYDEBUG(52, *YYCURSOR); -#line 1778 "ext/date/lib/parse_date.re" +#line 1809 "parse_date.re" { s->pos = cursor; s->line++; goto std; } -#line 2589 "<stdout>" +#line 2620 "<stdout>" yy53: YYDEBUG(53, *YYCURSOR); yych = *++YYCURSOR; @@ -3019,7 +3052,7 @@ yy73: if (yych == 's') goto yy75; yy74: YYDEBUG(74, *YYCURSOR); -#line 1757 "ext/date/lib/parse_date.re" +#line 1788 "parse_date.re" { timelib_ull i; DEBUG_OUTPUT("relative"); @@ -3034,7 +3067,7 @@ yy74: TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 3040 "<stdout>" +#line 3071 "<stdout>" yy75: YYDEBUG(75, *YYCURSOR); yych = *++YYCURSOR; @@ -3937,7 +3970,7 @@ yy190: } yy191: YYDEBUG(191, *YYCURSOR); -#line 1620 "ext/date/lib/parse_date.re" +#line 1651 "parse_date.re" { const timelib_relunit* relunit; DEBUG_OUTPUT("daytext"); @@ -3954,7 +3987,7 @@ yy191: TIMELIB_DEINIT; return TIMELIB_WEEKDAY; } -#line 3960 "<stdout>" +#line 3991 "<stdout>" yy192: YYDEBUG(192, *YYCURSOR); yych = *++YYCURSOR; @@ -4474,7 +4507,7 @@ yy217: } yy218: YYDEBUG(218, *YYCURSOR); -#line 1679 "ext/date/lib/parse_date.re" +#line 1710 "parse_date.re" { DEBUG_OUTPUT("monthtext"); TIMELIB_INIT; @@ -4483,7 +4516,7 @@ yy218: TIMELIB_DEINIT; return TIMELIB_DATE_TEXT; } -#line 4489 "<stdout>" +#line 4520 "<stdout>" yy219: YYDEBUG(219, *YYCURSOR); ++YYCURSOR; @@ -4732,7 +4765,7 @@ yy231: goto yy237; yy232: YYDEBUG(232, *YYCURSOR); -#line 1425 "ext/date/lib/parse_date.re" +#line 1456 "parse_date.re" { int length = 0; DEBUG_OUTPUT("datetextual | datenoyear"); @@ -4745,7 +4778,7 @@ yy232: TIMELIB_DEINIT; return TIMELIB_DATE_TEXT; } -#line 4751 "<stdout>" +#line 4782 "<stdout>" yy233: YYDEBUG(233, *YYCURSOR); yyaccept = 6; @@ -4872,7 +4905,7 @@ yy245: } yy246: YYDEBUG(246, *YYCURSOR); -#line 1727 "ext/date/lib/parse_date.re" +#line 1758 "parse_date.re" { int tz_not_found; DEBUG_OUTPUT("dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz"); @@ -4901,7 +4934,7 @@ yy246: TIMELIB_DEINIT; return TIMELIB_SHORTDATE_WITH_TIME; } -#line 4907 "<stdout>" +#line 4938 "<stdout>" yy247: YYDEBUG(247, *YYCURSOR); yyaccept = 7; @@ -5199,7 +5232,7 @@ yy270: YYDEBUG(270, *YYCURSOR); ++YYCURSOR; YYDEBUG(271, *YYCURSOR); -#line 1703 "ext/date/lib/parse_date.re" +#line 1734 "parse_date.re" { DEBUG_OUTPUT("dateshortwithtimeshort12 | dateshortwithtimelong12"); TIMELIB_INIT; @@ -5222,7 +5255,7 @@ yy270: TIMELIB_DEINIT; return TIMELIB_SHORTDATE_WITH_TIME; } -#line 5228 "<stdout>" +#line 5259 "<stdout>" yy272: YYDEBUG(272, *YYCURSOR); yych = *++YYCURSOR; @@ -5829,7 +5862,7 @@ yy320: YYDEBUG(321, *YYCURSOR); ++YYCURSOR; YYDEBUG(322, *YYCURSOR); -#line 1397 "ext/date/lib/parse_date.re" +#line 1428 "parse_date.re" { int length = 0; DEBUG_OUTPUT("datenoday"); @@ -5842,7 +5875,7 @@ yy320: TIMELIB_DEINIT; return TIMELIB_DATE_NO_DAY; } -#line 5848 "<stdout>" +#line 5879 "<stdout>" yy323: YYDEBUG(323, *YYCURSOR); yych = *++YYCURSOR; @@ -6073,7 +6106,7 @@ yy327: if (yych <= '9') goto yy331; yy330: YYDEBUG(330, *YYCURSOR); -#line 1541 "ext/date/lib/parse_date.re" +#line 1572 "parse_date.re" { int length = 0; DEBUG_OUTPUT("pgtextshort"); @@ -6086,7 +6119,7 @@ yy330: TIMELIB_DEINIT; return TIMELIB_PG_TEXT; } -#line 6092 "<stdout>" +#line 6123 "<stdout>" yy331: YYDEBUG(331, *YYCURSOR); yych = *++YYCURSOR; @@ -6668,7 +6701,7 @@ yy356: } yy357: YYDEBUG(357, *YYCURSOR); -#line 1599 "ext/date/lib/parse_date.re" +#line 1630 "parse_date.re" { DEBUG_OUTPUT("ago"); TIMELIB_INIT; @@ -6688,7 +6721,7 @@ yy357: TIMELIB_DEINIT; return TIMELIB_AGO; } -#line 6694 "<stdout>" +#line 6725 "<stdout>" yy358: YYDEBUG(358, *YYCURSOR); yyaccept = 5; @@ -8471,7 +8504,7 @@ yy418: ++YYCURSOR; yy419: YYDEBUG(419, *YYCURSOR); -#line 1302 "ext/date/lib/parse_date.re" +#line 1333 "parse_date.re" { DEBUG_OUTPUT("iso8601date4 | iso8601date2 | iso8601dateslash | dateslash"); TIMELIB_INIT; @@ -8482,7 +8515,7 @@ yy419: TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 8488 "<stdout>" +#line 8519 "<stdout>" yy420: YYDEBUG(420, *YYCURSOR); yyaccept = 0; @@ -9126,7 +9159,7 @@ yy439: } yy440: YYDEBUG(440, *YYCURSOR); -#line 1439 "ext/date/lib/parse_date.re" +#line 1470 "parse_date.re" { DEBUG_OUTPUT("datenoyearrev"); TIMELIB_INIT; @@ -9137,7 +9170,7 @@ yy440: TIMELIB_DEINIT; return TIMELIB_DATE_TEXT; } -#line 9143 "<stdout>" +#line 9174 "<stdout>" yy441: YYDEBUG(441, *YYCURSOR); yyaccept = 9; @@ -9278,7 +9311,7 @@ yy452: YYDEBUG(452, *YYCURSOR); ++YYCURSOR; YYDEBUG(453, *YYCURSOR); -#line 1157 "ext/date/lib/parse_date.re" +#line 1188 "parse_date.re" { DEBUG_OUTPUT("timetiny12 | timeshort12 | timelong12"); TIMELIB_INIT; @@ -9294,7 +9327,7 @@ yy452: TIMELIB_DEINIT; return TIMELIB_TIME12; } -#line 9300 "<stdout>" +#line 9331 "<stdout>" yy454: YYDEBUG(454, *YYCURSOR); yyaccept = 10; @@ -9307,7 +9340,7 @@ yy454: } yy455: YYDEBUG(455, *YYCURSOR); -#line 1194 "ext/date/lib/parse_date.re" +#line 1225 "parse_date.re" { int tz_not_found; DEBUG_OUTPUT("timeshort24 | timelong24 | iso8601long"); @@ -9332,7 +9365,7 @@ yy455: TIMELIB_DEINIT; return TIMELIB_TIME24_WITH_ZONE; } -#line 9338 "<stdout>" +#line 9369 "<stdout>" yy456: YYDEBUG(456, *YYCURSOR); yyaccept = 10; @@ -9642,7 +9675,7 @@ yy487: YYDEBUG(487, *YYCURSOR); ++YYCURSOR; YYDEBUG(488, *YYCURSOR); -#line 1174 "ext/date/lib/parse_date.re" +#line 1205 "parse_date.re" { DEBUG_OUTPUT("mssqltime"); TIMELIB_INIT; @@ -9661,7 +9694,7 @@ yy487: TIMELIB_DEINIT; return TIMELIB_TIME24_WITH_ZONE; } -#line 9667 "<stdout>" +#line 9698 "<stdout>" yy489: YYDEBUG(489, *YYCURSOR); yyaccept = 10; @@ -9767,7 +9800,7 @@ yy498: if (yych <= '9') goto yy505; yy499: YYDEBUG(499, *YYCURSOR); -#line 1356 "ext/date/lib/parse_date.re" +#line 1387 "parse_date.re" { int length = 0; DEBUG_OUTPUT("datefull"); @@ -9781,7 +9814,7 @@ yy499: TIMELIB_DEINIT; return TIMELIB_DATE_FULL; } -#line 9787 "<stdout>" +#line 9818 "<stdout>" yy500: YYDEBUG(500, *YYCURSOR); yych = *++YYCURSOR; @@ -10517,7 +10550,7 @@ yy569: YYDEBUG(570, *YYCURSOR); ++YYCURSOR; YYDEBUG(571, *YYCURSOR); -#line 1371 "ext/date/lib/parse_date.re" +#line 1402 "parse_date.re" { DEBUG_OUTPUT("pointed date YYYY"); TIMELIB_INIT; @@ -10528,7 +10561,7 @@ yy569: TIMELIB_DEINIT; return TIMELIB_DATE_FULL_POINTED; } -#line 10534 "<stdout>" +#line 10565 "<stdout>" yy572: YYDEBUG(572, *YYCURSOR); yyaccept = 10; @@ -10564,7 +10597,7 @@ yy575: if (yych <= '9') goto yy569; yy576: YYDEBUG(576, *YYCURSOR); -#line 1383 "ext/date/lib/parse_date.re" +#line 1414 "parse_date.re" { int length = 0; DEBUG_OUTPUT("pointed date YY"); @@ -10577,7 +10610,7 @@ yy576: TIMELIB_DEINIT; return TIMELIB_DATE_FULL_POINTED; } -#line 10583 "<stdout>" +#line 10614 "<stdout>" yy577: YYDEBUG(577, *YYCURSOR); yyaccept = 10; @@ -11218,7 +11251,7 @@ yy620: } yy621: YYDEBUG(621, *YYCURSOR); -#line 1342 "ext/date/lib/parse_date.re" +#line 1373 "parse_date.re" { int length = 0; DEBUG_OUTPUT("gnudateshort"); @@ -11231,7 +11264,7 @@ yy621: TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 11237 "<stdout>" +#line 11268 "<stdout>" yy622: YYDEBUG(622, *YYCURSOR); yyaccept = 12; @@ -11337,7 +11370,7 @@ yy630: } yy631: YYDEBUG(631, *YYCURSOR); -#line 1286 "ext/date/lib/parse_date.re" +#line 1317 "parse_date.re" { int length = 0; DEBUG_OUTPUT("americanshort | american"); @@ -11352,7 +11385,7 @@ yy631: TIMELIB_DEINIT; return TIMELIB_AMERICAN; } -#line 11358 "<stdout>" +#line 11389 "<stdout>" yy632: YYDEBUG(632, *YYCURSOR); yyaccept = 13; @@ -11585,7 +11618,7 @@ yy664: if (yych <= ':') goto yy668; yy665: YYDEBUG(665, *YYCURSOR); -#line 1569 "ext/date/lib/parse_date.re" +#line 1600 "parse_date.re" { int tz_not_found; DEBUG_OUTPUT("clf"); @@ -11605,7 +11638,7 @@ yy665: TIMELIB_DEINIT; return TIMELIB_CLF; } -#line 11611 "<stdout>" +#line 11642 "<stdout>" yy666: YYDEBUG(666, *YYCURSOR); yych = *++YYCURSOR; @@ -12155,7 +12188,7 @@ yy727: } yy728: YYDEBUG(728, *YYCURSOR); -#line 1314 "ext/date/lib/parse_date.re" +#line 1345 "parse_date.re" { int length = 0; DEBUG_OUTPUT("iso8601date2"); @@ -12168,7 +12201,7 @@ yy728: TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 12174 "<stdout>" +#line 12205 "<stdout>" yy729: YYDEBUG(729, *YYCURSOR); yych = *++YYCURSOR; @@ -12207,7 +12240,7 @@ yy735: YYDEBUG(735, *YYCURSOR); ++YYCURSOR; YYDEBUG(736, *YYCURSOR); -#line 1555 "ext/date/lib/parse_date.re" +#line 1586 "parse_date.re" { int length = 0; DEBUG_OUTPUT("pgtextreverse"); @@ -12220,7 +12253,7 @@ yy735: TIMELIB_DEINIT; return TIMELIB_PG_TEXT; } -#line 12226 "<stdout>" +#line 12257 "<stdout>" yy737: YYDEBUG(737, *YYCURSOR); yych = *++YYCURSOR; @@ -12385,7 +12418,7 @@ yy747: } yy748: YYDEBUG(748, *YYCURSOR); -#line 1590 "ext/date/lib/parse_date.re" +#line 1621 "parse_date.re" { DEBUG_OUTPUT("year4"); TIMELIB_INIT; @@ -12393,7 +12426,7 @@ yy748: TIMELIB_DEINIT; return TIMELIB_CLF; } -#line 12399 "<stdout>" +#line 12430 "<stdout>" yy749: YYDEBUG(749, *YYCURSOR); yych = *++YYCURSOR; @@ -12598,7 +12631,7 @@ yy757: } yy758: YYDEBUG(758, *YYCURSOR); -#line 1411 "ext/date/lib/parse_date.re" +#line 1442 "parse_date.re" { int length = 0; DEBUG_OUTPUT("datenodayrev"); @@ -12611,7 +12644,7 @@ yy758: TIMELIB_DEINIT; return TIMELIB_DATE_NO_DAY; } -#line 12617 "<stdout>" +#line 12648 "<stdout>" yy759: YYDEBUG(759, *YYCURSOR); yych = *++YYCURSOR; @@ -12832,7 +12865,7 @@ yy778: if (yych <= '7') goto yy781; yy779: YYDEBUG(779, *YYCURSOR); -#line 1522 "ext/date/lib/parse_date.re" +#line 1553 "parse_date.re" { timelib_sll w, d; DEBUG_OUTPUT("isoweek"); @@ -12850,7 +12883,7 @@ yy779: TIMELIB_DEINIT; return TIMELIB_ISO_WEEK; } -#line 12856 "<stdout>" +#line 12887 "<stdout>" yy780: YYDEBUG(780, *YYCURSOR); yych = *++YYCURSOR; @@ -12860,7 +12893,7 @@ yy781: YYDEBUG(781, *YYCURSOR); ++YYCURSOR; YYDEBUG(782, *YYCURSOR); -#line 1503 "ext/date/lib/parse_date.re" +#line 1534 "parse_date.re" { timelib_sll w, d; DEBUG_OUTPUT("isoweekday"); @@ -12878,7 +12911,7 @@ yy781: TIMELIB_DEINIT; return TIMELIB_ISO_WEEK; } -#line 12884 "<stdout>" +#line 12915 "<stdout>" yy783: YYDEBUG(783, *YYCURSOR); yych = *++YYCURSOR; @@ -12948,7 +12981,7 @@ yy785: } yy786: YYDEBUG(786, *YYCURSOR); -#line 1489 "ext/date/lib/parse_date.re" +#line 1520 "parse_date.re" { int length = 0; DEBUG_OUTPUT("pgydotd"); @@ -12961,7 +12994,7 @@ yy786: TIMELIB_DEINIT; return TIMELIB_PG_YEARDAY; } -#line 12967 "<stdout>" +#line 12998 "<stdout>" yy787: YYDEBUG(787, *YYCURSOR); yych = *++YYCURSOR; @@ -13064,7 +13097,7 @@ yy806: ++YYCURSOR; yy807: YYDEBUG(807, *YYCURSOR); -#line 1463 "ext/date/lib/parse_date.re" +#line 1494 "parse_date.re" { int tz_not_found; DEBUG_OUTPUT("xmlrpc | xmlrpcnocolon | soap | wddx | exif"); @@ -13089,7 +13122,7 @@ yy807: TIMELIB_DEINIT; return TIMELIB_XMLRPC_SOAP; } -#line 13095 "<stdout>" +#line 13126 "<stdout>" yy808: YYDEBUG(808, *YYCURSOR); yych = *++YYCURSOR; @@ -13384,7 +13417,7 @@ yy812: } yy813: YYDEBUG(813, *YYCURSOR); -#line 1451 "ext/date/lib/parse_date.re" +#line 1482 "parse_date.re" { DEBUG_OUTPUT("datenocolon"); TIMELIB_INIT; @@ -13395,7 +13428,7 @@ yy813: TIMELIB_DEINIT; return TIMELIB_DATE_NOCOLON; } -#line 13401 "<stdout>" +#line 13432 "<stdout>" yy814: YYDEBUG(814, *YYCURSOR); yych = *++YYCURSOR; @@ -14314,7 +14347,7 @@ yy937: if (yych <= '9') goto yy960; yy938: YYDEBUG(938, *YYCURSOR); -#line 1328 "ext/date/lib/parse_date.re" +#line 1359 "parse_date.re" { int length = 0; DEBUG_OUTPUT("gnudateshorter"); @@ -14327,7 +14360,7 @@ yy938: TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 14333 "<stdout>" +#line 14364 "<stdout>" yy939: YYDEBUG(939, *YYCURSOR); yyaccept = 21; @@ -15420,7 +15453,7 @@ yy1030: } yy1032: YYDEBUG(1032, *YYCURSOR); -#line 1220 "ext/date/lib/parse_date.re" +#line 1251 "parse_date.re" { DEBUG_OUTPUT("gnunocolon"); TIMELIB_INIT; @@ -15442,7 +15475,7 @@ yy1032: TIMELIB_DEINIT; return TIMELIB_GNU_NOCOLON; } -#line 15448 "<stdout>" +#line 15479 "<stdout>" yy1033: YYDEBUG(1033, *YYCURSOR); yych = *++YYCURSOR; @@ -15540,7 +15573,7 @@ yy1039: } yy1040: YYDEBUG(1040, *YYCURSOR); -#line 1266 "ext/date/lib/parse_date.re" +#line 1297 "parse_date.re" { int tz_not_found; DEBUG_OUTPUT("iso8601nocolon"); @@ -15559,7 +15592,7 @@ yy1040: TIMELIB_DEINIT; return TIMELIB_ISO_NOCOLON; } -#line 15565 "<stdout>" +#line 15596 "<stdout>" yy1041: YYDEBUG(1041, *YYCURSOR); yyaccept = 24; @@ -16535,7 +16568,7 @@ yy1083: } yy1084: YYDEBUG(1084, *YYCURSOR); -#line 1662 "ext/date/lib/parse_date.re" +#line 1693 "parse_date.re" { timelib_sll i; int behavior = 0; @@ -16551,7 +16584,7 @@ yy1084: TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 16557 "<stdout>" +#line 16588 "<stdout>" yy1085: YYDEBUG(1085, *YYCURSOR); ++YYCURSOR; @@ -16602,7 +16635,7 @@ yy1092: YYDEBUG(1092, *YYCURSOR); ++YYCURSOR; YYDEBUG(1093, *YYCURSOR); -#line 1135 "ext/date/lib/parse_date.re" +#line 1166 "parse_date.re" { timelib_sll i; int behavior = 0; @@ -16623,7 +16656,7 @@ yy1092: TIMELIB_DEINIT; return TIMELIB_WEEK_DAY_OF_MONTH; } -#line 16629 "<stdout>" +#line 16660 "<stdout>" yy1094: YYDEBUG(1094, *YYCURSOR); yyaccept = 25; @@ -16731,7 +16764,7 @@ yy1107: } yy1108: YYDEBUG(1108, *YYCURSOR); -#line 1638 "ext/date/lib/parse_date.re" +#line 1669 "parse_date.re" { timelib_sll i; int behavior = 0; @@ -16754,7 +16787,7 @@ yy1108: TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 16760 "<stdout>" +#line 16791 "<stdout>" yy1109: YYDEBUG(1109, *YYCURSOR); yych = *++YYCURSOR; @@ -19603,7 +19636,7 @@ yy1283: } yy1284: YYDEBUG(1284, *YYCURSOR); -#line 1112 "ext/date/lib/parse_date.re" +#line 1143 "parse_date.re" { DEBUG_OUTPUT("backof | frontof"); TIMELIB_INIT; @@ -19625,7 +19658,7 @@ yy1284: TIMELIB_DEINIT; return TIMELIB_LF_DAY_OF_MONTH; } -#line 19631 "<stdout>" +#line 19662 "<stdout>" yy1285: YYDEBUG(1285, *YYCURSOR); yyaccept = 27; @@ -19924,7 +19957,7 @@ yy1306: YYDEBUG(1306, *YYCURSOR); ++YYCURSOR; YYDEBUG(1307, *YYCURSOR); -#line 1095 "ext/date/lib/parse_date.re" +#line 1126 "parse_date.re" { DEBUG_OUTPUT("firstdayof | lastdayof"); TIMELIB_INIT; @@ -19940,7 +19973,7 @@ yy1306: TIMELIB_DEINIT; return TIMELIB_LF_DAY_OF_MONTH; } -#line 19946 "<stdout>" +#line 19977 "<stdout>" yy1308: YYDEBUG(1308, *YYCURSOR); yyaccept = 0; @@ -21429,7 +21462,7 @@ yy1377: if (yych <= '9') goto yy1377; yy1379: YYDEBUG(1379, *YYCURSOR); -#line 1041 "ext/date/lib/parse_date.re" +#line 1072 "parse_date.re" { timelib_ull i; @@ -21454,7 +21487,7 @@ yy1379: TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 21460 "<stdout>" +#line 21491 "<stdout>" yy1380: YYDEBUG(1380, *YYCURSOR); yych = *++YYCURSOR; @@ -21483,7 +21516,7 @@ yy1380: YYDEBUG(1386, *YYCURSOR); ++YYCURSOR; YYDEBUG(1387, *YYCURSOR); -#line 1067 "ext/date/lib/parse_date.re" +#line 1098 "parse_date.re" { timelib_ull i, us; @@ -21510,7 +21543,7 @@ yy1380: TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 21516 "<stdout>" +#line 21547 "<stdout>" yy1388: YYDEBUG(1388, *YYCURSOR); yych = *++YYCURSOR; @@ -21946,7 +21979,7 @@ yy1416: ++YYCURSOR; yy1417: YYDEBUG(1417, *YYCURSOR); -#line 1029 "ext/date/lib/parse_date.re" +#line 1060 "parse_date.re" { DEBUG_OUTPUT("tomorrow"); TIMELIB_INIT; @@ -21957,7 +21990,7 @@ yy1417: TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 21963 "<stdout>" +#line 21994 "<stdout>" yy1418: YYDEBUG(1418, *YYCURSOR); yych = *++YYCURSOR; @@ -21992,7 +22025,7 @@ yy1419: } yy1420: YYDEBUG(1420, *YYCURSOR); -#line 1019 "ext/date/lib/parse_date.re" +#line 1050 "parse_date.re" { DEBUG_OUTPUT("midnight | today"); TIMELIB_INIT; @@ -22001,7 +22034,7 @@ yy1420: TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 22007 "<stdout>" +#line 22038 "<stdout>" yy1421: YYDEBUG(1421, *YYCURSOR); yych = *++YYCURSOR; @@ -24013,7 +24046,7 @@ yy1499: } yy1500: YYDEBUG(1500, *YYCURSOR); -#line 998 "ext/date/lib/parse_date.re" +#line 1029 "parse_date.re" { DEBUG_OUTPUT("now"); TIMELIB_INIT; @@ -24021,7 +24054,7 @@ yy1500: TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 24027 "<stdout>" +#line 24058 "<stdout>" yy1501: YYDEBUG(1501, *YYCURSOR); yych = *++YYCURSOR; @@ -24160,7 +24193,7 @@ yy1507: } yy1508: YYDEBUG(1508, *YYCURSOR); -#line 1007 "ext/date/lib/parse_date.re" +#line 1038 "parse_date.re" { DEBUG_OUTPUT("noon"); TIMELIB_INIT; @@ -24171,7 +24204,7 @@ yy1508: TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 24177 "<stdout>" +#line 24208 "<stdout>" yy1509: YYDEBUG(1509, *YYCURSOR); yyaccept = 0; @@ -24704,7 +24737,7 @@ yy1530: ++YYCURSOR; yy1531: YYDEBUG(1531, *YYCURSOR); -#line 986 "ext/date/lib/parse_date.re" +#line 1017 "parse_date.re" { DEBUG_OUTPUT("yesterday"); TIMELIB_INIT; @@ -24715,7 +24748,7 @@ yy1531: TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 24721 "<stdout>" +#line 24752 "<stdout>" yy1532: YYDEBUG(1532, *YYCURSOR); yyaccept = 0; @@ -24888,7 +24921,7 @@ yy1537: goto yy1531; } } -#line 1788 "ext/date/lib/parse_date.re" +#line 1819 "parse_date.re" } @@ -25010,15 +25043,90 @@ static void timelib_time_reset_unset_fields(timelib_time *time) if (time->us == TIMELIB_UNSET ) time->us = 0; } +static const timelib_format_specifier default_format_map[] = { + {'+', TIMELIB_FORMAT_ALLOW_EXTRA_CHARACTERS}, + {'#', TIMELIB_FORMAT_ANY_SEPARATOR}, + {'j', TIMELIB_FORMAT_DAY_TWO_DIGIT}, + {'d', TIMELIB_FORMAT_DAY_TWO_DIGIT_PADDED}, + {'z', TIMELIB_FORMAT_DAY_OF_YEAR}, + {'S', TIMELIB_FORMAT_DAY_SUFFIX}, + {'U', TIMELIB_FORMAT_EPOCH_SECONDS}, + {'\\', TIMELIB_FORMAT_ESCAPE}, + {'h', TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX}, + {'g', TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX_PADDED}, + {'H', TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX}, + {'G', TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX_PADDED}, + {'a', TIMELIB_FORMAT_MERIDIAN}, + {'A', TIMELIB_FORMAT_MERIDIAN}, + {'u', TIMELIB_FORMAT_MICROSECOND_SIX_DIGIT}, + {'v', TIMELIB_FORMAT_MILLISECOND_THREE_DIGIT}, + {'i', TIMELIB_FORMAT_MINUTE_TWO_DIGIT}, + {'n', TIMELIB_FORMAT_MONTH_TWO_DIGIT}, + {'m', TIMELIB_FORMAT_MONTH_TWO_DIGIT_PADDED}, + {'?', TIMELIB_FORMAT_RANDOM_CHAR}, + {'!', TIMELIB_FORMAT_RESET_ALL}, + {'|', TIMELIB_FORMAT_RESET_ALL_WHEN_NOT_SET}, + {'s', TIMELIB_FORMAT_SECOND_TWO_DIGIT}, + {';', TIMELIB_FORMAT_SEPARATOR}, + {':', TIMELIB_FORMAT_SEPARATOR}, + {'/', TIMELIB_FORMAT_SEPARATOR}, + {'.', TIMELIB_FORMAT_SEPARATOR}, + {',', TIMELIB_FORMAT_SEPARATOR}, + {'-', TIMELIB_FORMAT_SEPARATOR}, + {'(', TIMELIB_FORMAT_SEPARATOR}, + {')', TIMELIB_FORMAT_SEPARATOR}, + {'*', TIMELIB_FORMAT_SKIP_TO_SEPARATOR}, + {'D', TIMELIB_FORMAT_TEXTUAL_DAY_3_LETTER}, + {'l', TIMELIB_FORMAT_TEXTUAL_DAY_FULL}, + {'M', TIMELIB_FORMAT_TEXTUAL_MONTH_3_LETTER}, + {'F', TIMELIB_FORMAT_TEXTUAL_MONTH_FULL}, + {'e', TIMELIB_FORMAT_TIMEZONE_OFFSET}, + {'P', TIMELIB_FORMAT_TIMEZONE_OFFSET}, + {'T', TIMELIB_FORMAT_TIMEZONE_OFFSET}, + {'O', TIMELIB_FORMAT_TIMEZONE_OFFSET}, + {' ', TIMELIB_FORMAT_WHITESPACE}, + {'y', TIMELIB_FORMAT_YEAR_TWO_DIGIT}, + {'Y', TIMELIB_FORMAT_YEAR_FOUR_DIGIT}, + {'\0', TIMELIB_FORMAT_END} +}; + +static const timelib_format_config default_format_config = { + default_format_map, + // No prefix required by default. + '\0' +}; + +static timelib_format_specifier_code timelib_lookup_format(char input, const timelib_format_specifier* format_map) +{ + while (format_map && format_map->specifier != '\0') { + if (format_map->specifier == input) { + return format_map->code; + } + format_map++; + } + return TIMELIB_FORMAT_LITERAL; +} + timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper) { - char *fptr = format; - char *ptr = string; - char *begin; - timelib_sll tmp; - Scanner in; - Scanner *s = ∈ - int allow_extra = 0; + return timelib_parse_from_format_with_map(format, string, len, errors, tzdb, tz_get_wrapper, &default_format_config); +} + +timelib_time *timelib_parse_from_format_with_map(char *format, char *string, size_t len, timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper, const timelib_format_config* format_config) +{ + char *fptr = format; + char *ptr = string; + char *begin; + timelib_sll tmp; + Scanner in; + Scanner *s = ∈ + bool allow_extra = false; + bool prefix_found = false; + int iso_year = TIMELIB_UNSET; + int iso_week_of_year = TIMELIB_UNSET; + int iso_day_of_week = TIMELIB_UNSET; + char prefix_char = format_config->prefix_char; + const timelib_format_specifier *format_map = format_config->format_map; memset(&in, 0, sizeof(in)); in.errors = timelib_malloc(sizeof(timelib_error_container)); @@ -25044,9 +25152,41 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, /* Loop over the format string */ while (*fptr && *ptr) { begin = ptr; - switch (*fptr) { - case 'D': /* three letter day */ - case 'l': /* full day */ + + if (prefix_char) { + /* There are 2 cases where the input string and format string + * should match the next literal: + * + * 1. No prefix has been specified yet in the format, so expect 1:1 + * match. + * 2. Sequential prefix characters indicating that the second + * prefix is escaped. (e.g. "%%" is expecting literal "%") + */ + if ((!prefix_found && *fptr != prefix_char) || + (prefix_found && *fptr == prefix_char)) { + if (*fptr != *ptr) { + add_pbf_error(s, TIMELIB_ERR_FORMAT_LITERAL_MISMATCH, "Format literal not found", string, begin); + } + ptr++; + fptr++; + prefix_found = false; + continue; + } + + if (*fptr == prefix_char) { + fptr++; + prefix_found = true; + continue; + } + + /* Fall through case is that the prefix has been found and the next + * character is the format specifier. */ + prefix_found = false; + } + + switch (timelib_lookup_format(*fptr, format_map)) { + case TIMELIB_FORMAT_TEXTUAL_DAY_3_LETTER: /* three letter day */ + case TIMELIB_FORMAT_TEXTUAL_DAY_FULL: /* full day */ { const timelib_relunit* tmprel = 0; @@ -25062,17 +25202,17 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, } } break; - case 'd': /* two digit day, with leading zero */ - case 'j': /* two digit day, without leading zero */ + case TIMELIB_FORMAT_DAY_TWO_DIGIT: /* two digit day, without leading zero */ + case TIMELIB_FORMAT_DAY_TWO_DIGIT_PADDED: /* two digit day, with leading zero */ TIMELIB_CHECK_NUMBER; if ((s->time->d = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) { add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_DAY, "A two digit day could not be found", string, begin); } break; - case 'S': /* day suffix, ignored, nor checked */ + case TIMELIB_FORMAT_DAY_SUFFIX: /* day suffix, ignored, nor checked */ timelib_skip_day_suffix((char **) &ptr); break; - case 'z': /* day of year - resets month (0 based) - also initializes everything else to !TIMELIB_UNSET */ + case TIMELIB_FORMAT_DAY_OF_YEAR: /* day of year - resets month (0 based) - also initializes everything else to !TIMELIB_UNSET */ TIMELIB_CHECK_NUMBER; if ((tmp = timelib_get_nr((char **) &ptr, 3)) == TIMELIB_UNSET) { add_pbf_error(s, TIMELIB_ERR_NO_THREE_DIGIT_DAY_OF_YEAR, "A three digit day-of-year could not be found", string, begin); @@ -25083,15 +25223,15 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, } break; - case 'm': /* two digit month, with leading zero */ - case 'n': /* two digit month, without leading zero */ + case TIMELIB_FORMAT_MONTH_TWO_DIGIT: /* two digit month, without leading zero */ + case TIMELIB_FORMAT_MONTH_TWO_DIGIT_PADDED: /* two digit month, with leading zero */ TIMELIB_CHECK_NUMBER; if ((s->time->m = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) { add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_MONTH, "A two digit month could not be found", string, begin); } break; - case 'M': /* three letter month */ - case 'F': /* full month */ + case TIMELIB_FORMAT_TEXTUAL_MONTH_3_LETTER: /* three letter month */ + case TIMELIB_FORMAT_TEXTUAL_MONTH_FULL: /* full month */ tmp = timelib_lookup_month((char **) &ptr); if (!tmp) { add_pbf_error(s, TIMELIB_ERR_NO_TEXTUAL_MONTH, "A textual month could not be found", string, begin); @@ -25099,7 +25239,7 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, s->time->m = tmp; } break; - case 'y': /* two digit year */ + case TIMELIB_FORMAT_YEAR_TWO_DIGIT: /* two digit year */ { int length = 0; TIMELIB_CHECK_NUMBER; @@ -25109,14 +25249,14 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, TIMELIB_PROCESS_YEAR(s->time->y, length); } break; - case 'Y': /* four digit year */ + case TIMELIB_FORMAT_YEAR_FOUR_DIGIT: /* four digit year */ TIMELIB_CHECK_NUMBER; if ((s->time->y = timelib_get_nr((char **) &ptr, 4)) == TIMELIB_UNSET) { add_pbf_error(s, TIMELIB_ERR_NO_FOUR_DIGIT_YEAR, "A four digit year could not be found", string, begin); } break; - case 'g': /* two digit hour, with leading zero */ - case 'h': /* two digit hour, without leading zero */ + case TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX: /* two digit hour, without leading zero */ + case TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX_PADDED: /* two digit hour, with leading zero */ TIMELIB_CHECK_NUMBER; if ((s->time->h = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) { add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_HOUR, "A two digit hour could not be found", string, begin); @@ -25125,15 +25265,14 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, add_pbf_error(s, TIMELIB_ERR_HOUR_LARGER_THAN_12, "Hour can not be higher than 12", string, begin); } break; - case 'G': /* two digit hour, with leading zero */ - case 'H': /* two digit hour, without leading zero */ + case TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX_PADDED: /* two digit hour, with leading zero */ + case TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX: /* two digit hour, without leading zero */ TIMELIB_CHECK_NUMBER; if ((s->time->h = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) { add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_HOUR, "A two digit hour could not be found", string, begin); } break; - case 'a': /* am/pm/a.m./p.m. */ - case 'A': /* AM/PM/A.M./P.M. */ + case TIMELIB_FORMAT_MERIDIAN: /* am/pm/a.m./p.m. AM/PM/A.M./P.M. */ if (s->time->h == TIMELIB_UNSET) { add_pbf_error(s, TIMELIB_ERR_MERIDIAN_BEFORE_HOUR, "Meridian can only come after an hour has been found", string, begin); } else if ((tmp = timelib_meridian_with_check((char **) &ptr, s->time->h)) == TIMELIB_UNSET) { @@ -25142,7 +25281,7 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, s->time->h += tmp; } break; - case 'i': /* two digit minute, with leading zero */ + case TIMELIB_FORMAT_MINUTE_TWO_DIGIT: /* two digit minute, with leading zero */ { int length; timelib_sll min; @@ -25156,7 +25295,7 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, } } break; - case 's': /* two digit second, with leading zero */ + case TIMELIB_FORMAT_SECOND_TWO_DIGIT: /* two digit second, with leading zero */ { int length; timelib_sll sec; @@ -25170,7 +25309,7 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, } } break; - case 'u': /* up to six digit microsecond */ + case TIMELIB_FORMAT_MICROSECOND_SIX_DIGIT: /* up to six digit microsecond */ { double f; char *tptr; @@ -25184,10 +25323,24 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, } } break; - case ' ': /* any sort of whitespace (' ' and \t) */ + case TIMELIB_FORMAT_MILLISECOND_THREE_DIGIT: /* up to three digit millisecond */ + { + double f; + char *tptr; + + TIMELIB_CHECK_NUMBER; + tptr = ptr; + if ((f = timelib_get_nr((char **) &ptr, 3)) == TIMELIB_UNSET || (ptr - tptr < 1)) { + add_pbf_error(s, TIMELIB_ERR_NO_THREE_DIGIT_MILLISECOND, "A three digit millisecond could not be found", string, begin); + } else { + s->time->us = (f * pow(10, 3 - (ptr - tptr)) * 1000); + } + } + break; + case TIMELIB_FORMAT_WHITESPACE: /* any sort of whitespace (' ' and \t) */ timelib_eat_spaces((char **) &ptr); break; - case 'U': /* epoch seconds */ + case TIMELIB_FORMAT_EPOCH_SECONDS: /* epoch seconds */ TIMELIB_CHECK_SIGNED_NUMBER; TIMELIB_HAVE_RELATIVE(); tmp = timelib_get_unsigned_nr((char **) &ptr, 24); @@ -25201,36 +25354,15 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, s->time->z = 0; s->time->dst = 0; break; - - case 'e': /* timezone */ - case 'P': /* timezone */ - case 'T': /* timezone */ - case 'O': /* timezone */ - { - int tz_not_found; - s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper); - if (tz_not_found) { - add_pbf_error(s, TIMELIB_ERR_TZID_NOT_FOUND, "The timezone could not be found in the database", string, begin); - } - } - break; - - case '#': /* separation symbol */ - if (*ptr == ';' || *ptr == ':' || *ptr == '/' || *ptr == '.' || *ptr == ',' || *ptr == '-' || *ptr == '(' || *ptr == ')') { + case TIMELIB_FORMAT_ANY_SEPARATOR: /* separation symbol */ + if (timelib_lookup_format(*ptr, format_map) == TIMELIB_FORMAT_SEPARATOR) { ++ptr; } else { add_pbf_error(s, TIMELIB_ERR_NO_SEP_SYMBOL, "The separation symbol ([;:/.,-]) could not be found", string, begin); } break; - case ';': - case ':': - case '/': - case '.': - case ',': - case '-': - case '(': - case ')': + case TIMELIB_FORMAT_SEPARATOR: if (*ptr == *fptr) { ++ptr; } else { @@ -25238,19 +25370,19 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, } break; - case '!': /* reset all fields to default */ + case TIMELIB_FORMAT_RESET_ALL: /* reset all fields to default */ timelib_time_reset_fields(s->time); break; /* break intentionally not missing */ - case '|': /* reset all fields to default when not set */ + case TIMELIB_FORMAT_RESET_ALL_WHEN_NOT_SET: /* reset all fields to default when not set */ timelib_time_reset_unset_fields(s->time); break; /* break intentionally not missing */ - case '?': /* random char */ + case TIMELIB_FORMAT_RANDOM_CHAR: /* random char */ ++ptr; break; - case '\\': /* escaped char */ + case TIMELIB_FORMAT_ESCAPE: /* escaped char */ if(!fptr[1]) { add_pbf_error(s, TIMELIB_ERR_EXPECTED_ESCAPE_CHAR, "Escaped character expected", string, begin); break; @@ -25263,14 +25395,52 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, } break; - case '*': /* random chars until a separator or number ([ \t.,:;/-0123456789]) */ + case TIMELIB_FORMAT_SKIP_TO_SEPARATOR: /* random chars until a separator or number ([ \t.,:;/-0123456789]) */ timelib_eat_until_separator((char **) &ptr); break; - case '+': /* allow extra chars in the format */ - allow_extra = 1; + case TIMELIB_FORMAT_ALLOW_EXTRA_CHARACTERS: /* allow extra chars in the format */ + allow_extra = true; + break; + case TIMELIB_FORMAT_YEAR_ISO: + if ((iso_year = timelib_get_nr((char **) &ptr, 4)) == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_NO_FOUR_DIGIT_YEAR_ISO, "A four digit ISO year could not be found", string, begin); + } + break; + case TIMELIB_FORMAT_WEEK_OF_YEAR_ISO: + if ((iso_week_of_year = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_WEEK, "A two digit ISO week could not be found", string, begin); + } + /* Range is 1 - 53 for ISO week of year */ + if (iso_week_of_year < 1 || iso_week_of_year > 53) { + add_pbf_error(s, TIMELIB_ERR_INVALID_WEEK, "ISO Week must be between 1 and 53", string, begin); + } break; + case TIMELIB_FORMAT_DAY_OF_WEEK_ISO: + if ((iso_day_of_week = timelib_get_nr((char **) &ptr, 1)) == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_NO_DAY_OF_WEEK, "A single digit day of week could not be found", string, begin); + } + if (iso_day_of_week < 1 || iso_day_of_week > 7) { + add_pbf_error(s, TIMELIB_ERR_INVALID_DAY_OF_WEEK, "Day of week must be between 1 and 7", string, begin); + } + break; + case TIMELIB_FORMAT_TIMEZONE_OFFSET: /* timezone */ + { + int tz_not_found; + s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper); + if (tz_not_found) { + add_pbf_error(s, TIMELIB_ERR_TZID_NOT_FOUND, "The timezone could not be found in the database", string, begin); + } + } + break; + case TIMELIB_FORMAT_TIMEZONE_OFFSET_MINUTES: /* timezone format +/-mmm */ + s->time->z = timelib_parse_tz_minutes((char **) &ptr, s->time); + if (s->time->z == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_INVALID_TZ_OFFSET, "Invalid timezone offset in minutes", string, begin); + } + break; + case TIMELIB_FORMAT_LITERAL: default: if (*fptr != *ptr) { add_pbf_error(s, TIMELIB_ERR_WRONG_FORMAT_SEP, "The format separator does not match", string, begin); @@ -25286,32 +25456,32 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, add_pbf_error(s, TIMELIB_ERR_TRAILING_DATA, "Trailing data", string, ptr); } } - /* ignore trailing +'s */ - while (*fptr == '+') { - fptr++; - } + if (*fptr) { - /* Trailing | and ! specifiers are valid. */ + /* Trailing reset specifiers are valid. */ int done = 0; while (*fptr && !done) { - switch (*fptr++) { - case '!': /* reset all fields to default */ + switch (timelib_lookup_format(*fptr, format_map)) { + case TIMELIB_FORMAT_RESET_ALL: /* reset all fields to default */ timelib_time_reset_fields(s->time); break; - case '|': /* reset all fields to default when not set */ + case TIMELIB_FORMAT_RESET_ALL_WHEN_NOT_SET: /* reset all fields to default when not set */ timelib_time_reset_unset_fields(s->time); break; + case TIMELIB_FORMAT_ALLOW_EXTRA_CHARACTERS: + break; default: add_pbf_error(s, TIMELIB_ERR_DATA_MISSING, "Data missing", string, ptr); done = 1; } + fptr++; } } /* clean up a bit */ - if (s->time->h != TIMELIB_UNSET || s->time->i != TIMELIB_UNSET || s->time->s != TIMELIB_UNSET) { + if (s->time->h != TIMELIB_UNSET || s->time->i != TIMELIB_UNSET || s->time->s != TIMELIB_UNSET || s->time->us != TIMELIB_UNSET) { if (s->time->h == TIMELIB_UNSET ) { s->time->h = 0; } @@ -25321,6 +25491,31 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, if (s->time->s == TIMELIB_UNSET ) { s->time->s = 0; } + if (s->time->us == TIMELIB_UNSET ) { + s->time->us = 0; + } + } + + /* Check for mixing of ISO dates with natural dates. */ + if (s->time->y != TIMELIB_UNSET && (iso_week_of_year != TIMELIB_UNSET || iso_year != TIMELIB_UNSET || iso_day_of_week != TIMELIB_UNSET)) { + add_pbf_error(s, TIMELIB_ERR_MIX_ISO_WITH_NATURAL, "Mixing of ISO dates with natural dates is not allowed", string, ptr); + } + if (iso_year != TIMELIB_UNSET && (s->time->y != TIMELIB_UNSET || s->time->m != TIMELIB_UNSET || s->time->d != TIMELIB_UNSET)) { + add_pbf_error(s, TIMELIB_ERR_MIX_ISO_WITH_NATURAL, "Mixing of ISO dates with natural dates is not allowed", string, ptr); + } + + /* Convert ISO values */ + if (iso_year != TIMELIB_UNSET) { + /* Default week of year and day of week to 1. */ + if (iso_week_of_year == TIMELIB_UNSET) { + iso_week_of_year = 1; + } + if (iso_day_of_week == TIMELIB_UNSET) { + iso_day_of_week = 1; + } + timelib_date_from_isodate(iso_year, iso_week_of_year, iso_day_of_week, &s->time->y, &s->time->m, &s->time->d); + } else if (iso_week_of_year != TIMELIB_UNSET || iso_day_of_week != TIMELIB_UNSET) { + add_pbf_warning(s, TIMELIB_WARN_INVALID_DATE, "The parsed date was invalid", string, ptr); } /* do funky checking whether the parsed time was valid time */ diff --git a/ext/date/lib/parse_date.re b/ext/date/lib/parse_date.re index 425f654f16..6c84a71917 100644 --- a/ext/date/lib/parse_date.re +++ b/ext/date/lib/parse_date.re @@ -141,9 +141,9 @@ typedef struct _Scanner { } Scanner; typedef struct _timelib_lookup_table { - const char *name; - int type; - int value; + const char *name; + int type; + int value; } timelib_lookup_table; typedef struct _timelib_relunit { @@ -619,7 +619,7 @@ static const timelib_relunit* timelib_lookup_relunit(char **ptr) const timelib_relunit *tp, *value = NULL; while (**ptr != '\0' && **ptr != ' ' && **ptr != ',' && **ptr != '\t' && **ptr != ';' && **ptr != ':' && - **ptr != '/' && **ptr != '.' && **ptr != '-' && **ptr != '(' && **ptr != ')' ) { + **ptr != '/' && **ptr != '.' && **ptr != '-' && **ptr != '(' && **ptr != ')' ) { ++*ptr; } end = *ptr; @@ -771,6 +771,37 @@ static timelib_long timelib_parse_tz_cor(char **ptr) return 0; } +static timelib_long timelib_parse_tz_minutes(char **ptr, timelib_time *t) +{ + timelib_long retval = TIMELIB_UNSET; + char *begin = *ptr; + + /* First character must be +/- */ + if (**ptr != '+' && **ptr != '-') { + return retval; + } + + ++*ptr; + while (isdigit(**ptr)) { + ++*ptr; + } + + if (*begin == '+') { + t->is_localtime = 1; + t->zone_type = TIMELIB_ZONETYPE_OFFSET; + t->dst = 0; + + retval = sMIN(strtol(begin + 1, NULL, 10)); + } else if (*begin == '-') { + t->is_localtime = 1; + t->zone_type = TIMELIB_ZONETYPE_OFFSET; + t->dst = 0; + + retval = -1 * sMIN(strtol(begin + 1, NULL, 10)); + } + return retval; +} + timelib_long timelib_parse_zone(char **ptr, int *dst, timelib_time *t, int *tz_not_found, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_wrapper) { timelib_tzinfo *res; @@ -1906,15 +1937,90 @@ static void timelib_time_reset_unset_fields(timelib_time *time) if (time->us == TIMELIB_UNSET ) time->us = 0; } +static const timelib_format_specifier default_format_map[] = { + {'+', TIMELIB_FORMAT_ALLOW_EXTRA_CHARACTERS}, + {'#', TIMELIB_FORMAT_ANY_SEPARATOR}, + {'j', TIMELIB_FORMAT_DAY_TWO_DIGIT}, + {'d', TIMELIB_FORMAT_DAY_TWO_DIGIT_PADDED}, + {'z', TIMELIB_FORMAT_DAY_OF_YEAR}, + {'S', TIMELIB_FORMAT_DAY_SUFFIX}, + {'U', TIMELIB_FORMAT_EPOCH_SECONDS}, + {'\\', TIMELIB_FORMAT_ESCAPE}, + {'h', TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX}, + {'g', TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX_PADDED}, + {'H', TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX}, + {'G', TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX_PADDED}, + {'a', TIMELIB_FORMAT_MERIDIAN}, + {'A', TIMELIB_FORMAT_MERIDIAN}, + {'u', TIMELIB_FORMAT_MICROSECOND_SIX_DIGIT}, + {'v', TIMELIB_FORMAT_MILLISECOND_THREE_DIGIT}, + {'i', TIMELIB_FORMAT_MINUTE_TWO_DIGIT}, + {'n', TIMELIB_FORMAT_MONTH_TWO_DIGIT}, + {'m', TIMELIB_FORMAT_MONTH_TWO_DIGIT_PADDED}, + {'?', TIMELIB_FORMAT_RANDOM_CHAR}, + {'!', TIMELIB_FORMAT_RESET_ALL}, + {'|', TIMELIB_FORMAT_RESET_ALL_WHEN_NOT_SET}, + {'s', TIMELIB_FORMAT_SECOND_TWO_DIGIT}, + {';', TIMELIB_FORMAT_SEPARATOR}, + {':', TIMELIB_FORMAT_SEPARATOR}, + {'/', TIMELIB_FORMAT_SEPARATOR}, + {'.', TIMELIB_FORMAT_SEPARATOR}, + {',', TIMELIB_FORMAT_SEPARATOR}, + {'-', TIMELIB_FORMAT_SEPARATOR}, + {'(', TIMELIB_FORMAT_SEPARATOR}, + {')', TIMELIB_FORMAT_SEPARATOR}, + {'*', TIMELIB_FORMAT_SKIP_TO_SEPARATOR}, + {'D', TIMELIB_FORMAT_TEXTUAL_DAY_3_LETTER}, + {'l', TIMELIB_FORMAT_TEXTUAL_DAY_FULL}, + {'M', TIMELIB_FORMAT_TEXTUAL_MONTH_3_LETTER}, + {'F', TIMELIB_FORMAT_TEXTUAL_MONTH_FULL}, + {'e', TIMELIB_FORMAT_TIMEZONE_OFFSET}, + {'P', TIMELIB_FORMAT_TIMEZONE_OFFSET}, + {'T', TIMELIB_FORMAT_TIMEZONE_OFFSET}, + {'O', TIMELIB_FORMAT_TIMEZONE_OFFSET}, + {' ', TIMELIB_FORMAT_WHITESPACE}, + {'y', TIMELIB_FORMAT_YEAR_TWO_DIGIT}, + {'Y', TIMELIB_FORMAT_YEAR_FOUR_DIGIT}, + {'\0', TIMELIB_FORMAT_END} +}; + +static const timelib_format_config default_format_config = { + default_format_map, + // No prefix required by default. + '\0' +}; + +static timelib_format_specifier_code timelib_lookup_format(char input, const timelib_format_specifier* format_map) +{ + while (format_map && format_map->specifier != '\0') { + if (format_map->specifier == input) { + return format_map->code; + } + format_map++; + } + return TIMELIB_FORMAT_LITERAL; +} + timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper) { - char *fptr = format; - char *ptr = string; - char *begin; - timelib_sll tmp; - Scanner in; - Scanner *s = ∈ - int allow_extra = 0; + return timelib_parse_from_format_with_map(format, string, len, errors, tzdb, tz_get_wrapper, &default_format_config); +} + +timelib_time *timelib_parse_from_format_with_map(char *format, char *string, size_t len, timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper, const timelib_format_config* format_config) +{ + char *fptr = format; + char *ptr = string; + char *begin; + timelib_sll tmp; + Scanner in; + Scanner *s = ∈ + bool allow_extra = false; + bool prefix_found = false; + int iso_year = TIMELIB_UNSET; + int iso_week_of_year = TIMELIB_UNSET; + int iso_day_of_week = TIMELIB_UNSET; + char prefix_char = format_config->prefix_char; + const timelib_format_specifier *format_map = format_config->format_map; memset(&in, 0, sizeof(in)); in.errors = timelib_malloc(sizeof(timelib_error_container)); @@ -1940,9 +2046,41 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, /* Loop over the format string */ while (*fptr && *ptr) { begin = ptr; - switch (*fptr) { - case 'D': /* three letter day */ - case 'l': /* full day */ + + if (prefix_char) { + /* There are 2 cases where the input string and format string + * should match the next literal: + * + * 1. No prefix has been specified yet in the format, so expect 1:1 + * match. + * 2. Sequential prefix characters indicating that the second + * prefix is escaped. (e.g. "%%" is expecting literal "%") + */ + if ((!prefix_found && *fptr != prefix_char) || + (prefix_found && *fptr == prefix_char)) { + if (*fptr != *ptr) { + add_pbf_error(s, TIMELIB_ERR_FORMAT_LITERAL_MISMATCH, "Format literal not found", string, begin); + } + ptr++; + fptr++; + prefix_found = false; + continue; + } + + if (*fptr == prefix_char) { + fptr++; + prefix_found = true; + continue; + } + + /* Fall through case is that the prefix has been found and the next + * character is the format specifier. */ + prefix_found = false; + } + + switch (timelib_lookup_format(*fptr, format_map)) { + case TIMELIB_FORMAT_TEXTUAL_DAY_3_LETTER: /* three letter day */ + case TIMELIB_FORMAT_TEXTUAL_DAY_FULL: /* full day */ { const timelib_relunit* tmprel = 0; @@ -1958,17 +2096,17 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, } } break; - case 'd': /* two digit day, with leading zero */ - case 'j': /* two digit day, without leading zero */ + case TIMELIB_FORMAT_DAY_TWO_DIGIT: /* two digit day, without leading zero */ + case TIMELIB_FORMAT_DAY_TWO_DIGIT_PADDED: /* two digit day, with leading zero */ TIMELIB_CHECK_NUMBER; if ((s->time->d = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) { add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_DAY, "A two digit day could not be found", string, begin); } break; - case 'S': /* day suffix, ignored, nor checked */ + case TIMELIB_FORMAT_DAY_SUFFIX: /* day suffix, ignored, nor checked */ timelib_skip_day_suffix((char **) &ptr); break; - case 'z': /* day of year - resets month (0 based) - also initializes everything else to !TIMELIB_UNSET */ + case TIMELIB_FORMAT_DAY_OF_YEAR: /* day of year - resets month (0 based) - also initializes everything else to !TIMELIB_UNSET */ TIMELIB_CHECK_NUMBER; if ((tmp = timelib_get_nr((char **) &ptr, 3)) == TIMELIB_UNSET) { add_pbf_error(s, TIMELIB_ERR_NO_THREE_DIGIT_DAY_OF_YEAR, "A three digit day-of-year could not be found", string, begin); @@ -1979,15 +2117,15 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, } break; - case 'm': /* two digit month, with leading zero */ - case 'n': /* two digit month, without leading zero */ + case TIMELIB_FORMAT_MONTH_TWO_DIGIT: /* two digit month, without leading zero */ + case TIMELIB_FORMAT_MONTH_TWO_DIGIT_PADDED: /* two digit month, with leading zero */ TIMELIB_CHECK_NUMBER; if ((s->time->m = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) { add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_MONTH, "A two digit month could not be found", string, begin); } break; - case 'M': /* three letter month */ - case 'F': /* full month */ + case TIMELIB_FORMAT_TEXTUAL_MONTH_3_LETTER: /* three letter month */ + case TIMELIB_FORMAT_TEXTUAL_MONTH_FULL: /* full month */ tmp = timelib_lookup_month((char **) &ptr); if (!tmp) { add_pbf_error(s, TIMELIB_ERR_NO_TEXTUAL_MONTH, "A textual month could not be found", string, begin); @@ -1995,7 +2133,7 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, s->time->m = tmp; } break; - case 'y': /* two digit year */ + case TIMELIB_FORMAT_YEAR_TWO_DIGIT: /* two digit year */ { int length = 0; TIMELIB_CHECK_NUMBER; @@ -2005,14 +2143,14 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, TIMELIB_PROCESS_YEAR(s->time->y, length); } break; - case 'Y': /* four digit year */ + case TIMELIB_FORMAT_YEAR_FOUR_DIGIT: /* four digit year */ TIMELIB_CHECK_NUMBER; if ((s->time->y = timelib_get_nr((char **) &ptr, 4)) == TIMELIB_UNSET) { add_pbf_error(s, TIMELIB_ERR_NO_FOUR_DIGIT_YEAR, "A four digit year could not be found", string, begin); } break; - case 'g': /* two digit hour, with leading zero */ - case 'h': /* two digit hour, without leading zero */ + case TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX: /* two digit hour, without leading zero */ + case TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX_PADDED: /* two digit hour, with leading zero */ TIMELIB_CHECK_NUMBER; if ((s->time->h = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) { add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_HOUR, "A two digit hour could not be found", string, begin); @@ -2021,15 +2159,14 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, add_pbf_error(s, TIMELIB_ERR_HOUR_LARGER_THAN_12, "Hour can not be higher than 12", string, begin); } break; - case 'G': /* two digit hour, with leading zero */ - case 'H': /* two digit hour, without leading zero */ + case TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX_PADDED: /* two digit hour, with leading zero */ + case TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX: /* two digit hour, without leading zero */ TIMELIB_CHECK_NUMBER; if ((s->time->h = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) { add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_HOUR, "A two digit hour could not be found", string, begin); } break; - case 'a': /* am/pm/a.m./p.m. */ - case 'A': /* AM/PM/A.M./P.M. */ + case TIMELIB_FORMAT_MERIDIAN: /* am/pm/a.m./p.m. AM/PM/A.M./P.M. */ if (s->time->h == TIMELIB_UNSET) { add_pbf_error(s, TIMELIB_ERR_MERIDIAN_BEFORE_HOUR, "Meridian can only come after an hour has been found", string, begin); } else if ((tmp = timelib_meridian_with_check((char **) &ptr, s->time->h)) == TIMELIB_UNSET) { @@ -2038,7 +2175,7 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, s->time->h += tmp; } break; - case 'i': /* two digit minute, with leading zero */ + case TIMELIB_FORMAT_MINUTE_TWO_DIGIT: /* two digit minute, with leading zero */ { int length; timelib_sll min; @@ -2052,7 +2189,7 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, } } break; - case 's': /* two digit second, with leading zero */ + case TIMELIB_FORMAT_SECOND_TWO_DIGIT: /* two digit second, with leading zero */ { int length; timelib_sll sec; @@ -2066,7 +2203,7 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, } } break; - case 'u': /* up to six digit microsecond */ + case TIMELIB_FORMAT_MICROSECOND_SIX_DIGIT: /* up to six digit microsecond */ { double f; char *tptr; @@ -2080,10 +2217,24 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, } } break; - case ' ': /* any sort of whitespace (' ' and \t) */ + case TIMELIB_FORMAT_MILLISECOND_THREE_DIGIT: /* up to three digit millisecond */ + { + double f; + char *tptr; + + TIMELIB_CHECK_NUMBER; + tptr = ptr; + if ((f = timelib_get_nr((char **) &ptr, 3)) == TIMELIB_UNSET || (ptr - tptr < 1)) { + add_pbf_error(s, TIMELIB_ERR_NO_THREE_DIGIT_MILLISECOND, "A three digit millisecond could not be found", string, begin); + } else { + s->time->us = (f * pow(10, 3 - (ptr - tptr)) * 1000); + } + } + break; + case TIMELIB_FORMAT_WHITESPACE: /* any sort of whitespace (' ' and \t) */ timelib_eat_spaces((char **) &ptr); break; - case 'U': /* epoch seconds */ + case TIMELIB_FORMAT_EPOCH_SECONDS: /* epoch seconds */ TIMELIB_CHECK_SIGNED_NUMBER; TIMELIB_HAVE_RELATIVE(); tmp = timelib_get_unsigned_nr((char **) &ptr, 24); @@ -2097,36 +2248,15 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, s->time->z = 0; s->time->dst = 0; break; - - case 'e': /* timezone */ - case 'P': /* timezone */ - case 'T': /* timezone */ - case 'O': /* timezone */ - { - int tz_not_found; - s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper); - if (tz_not_found) { - add_pbf_error(s, TIMELIB_ERR_TZID_NOT_FOUND, "The timezone could not be found in the database", string, begin); - } - } - break; - - case '#': /* separation symbol */ - if (*ptr == ';' || *ptr == ':' || *ptr == '/' || *ptr == '.' || *ptr == ',' || *ptr == '-' || *ptr == '(' || *ptr == ')') { + case TIMELIB_FORMAT_ANY_SEPARATOR: /* separation symbol */ + if (timelib_lookup_format(*ptr, format_map) == TIMELIB_FORMAT_SEPARATOR) { ++ptr; } else { add_pbf_error(s, TIMELIB_ERR_NO_SEP_SYMBOL, "The separation symbol ([;:/.,-]) could not be found", string, begin); } break; - case ';': - case ':': - case '/': - case '.': - case ',': - case '-': - case '(': - case ')': + case TIMELIB_FORMAT_SEPARATOR: if (*ptr == *fptr) { ++ptr; } else { @@ -2134,19 +2264,19 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, } break; - case '!': /* reset all fields to default */ + case TIMELIB_FORMAT_RESET_ALL: /* reset all fields to default */ timelib_time_reset_fields(s->time); break; /* break intentionally not missing */ - case '|': /* reset all fields to default when not set */ + case TIMELIB_FORMAT_RESET_ALL_WHEN_NOT_SET: /* reset all fields to default when not set */ timelib_time_reset_unset_fields(s->time); break; /* break intentionally not missing */ - case '?': /* random char */ + case TIMELIB_FORMAT_RANDOM_CHAR: /* random char */ ++ptr; break; - case '\\': /* escaped char */ + case TIMELIB_FORMAT_ESCAPE: /* escaped char */ if(!fptr[1]) { add_pbf_error(s, TIMELIB_ERR_EXPECTED_ESCAPE_CHAR, "Escaped character expected", string, begin); break; @@ -2159,14 +2289,52 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, } break; - case '*': /* random chars until a separator or number ([ \t.,:;/-0123456789]) */ + case TIMELIB_FORMAT_SKIP_TO_SEPARATOR: /* random chars until a separator or number ([ \t.,:;/-0123456789]) */ timelib_eat_until_separator((char **) &ptr); break; - case '+': /* allow extra chars in the format */ - allow_extra = 1; + case TIMELIB_FORMAT_ALLOW_EXTRA_CHARACTERS: /* allow extra chars in the format */ + allow_extra = true; + break; + case TIMELIB_FORMAT_YEAR_ISO: + if ((iso_year = timelib_get_nr((char **) &ptr, 4)) == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_NO_FOUR_DIGIT_YEAR_ISO, "A four digit ISO year could not be found", string, begin); + } + break; + case TIMELIB_FORMAT_WEEK_OF_YEAR_ISO: + if ((iso_week_of_year = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_WEEK, "A two digit ISO week could not be found", string, begin); + } + /* Range is 1 - 53 for ISO week of year */ + if (iso_week_of_year < 1 || iso_week_of_year > 53) { + add_pbf_error(s, TIMELIB_ERR_INVALID_WEEK, "ISO Week must be between 1 and 53", string, begin); + } + break; + case TIMELIB_FORMAT_DAY_OF_WEEK_ISO: + if ((iso_day_of_week = timelib_get_nr((char **) &ptr, 1)) == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_NO_DAY_OF_WEEK, "A single digit day of week could not be found", string, begin); + } + if (iso_day_of_week < 1 || iso_day_of_week > 7) { + add_pbf_error(s, TIMELIB_ERR_INVALID_DAY_OF_WEEK, "Day of week must be between 1 and 7", string, begin); + } break; + case TIMELIB_FORMAT_TIMEZONE_OFFSET: /* timezone */ + { + int tz_not_found; + s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper); + if (tz_not_found) { + add_pbf_error(s, TIMELIB_ERR_TZID_NOT_FOUND, "The timezone could not be found in the database", string, begin); + } + } + break; + case TIMELIB_FORMAT_TIMEZONE_OFFSET_MINUTES: /* timezone format +/-mmm */ + s->time->z = timelib_parse_tz_minutes((char **) &ptr, s->time); + if (s->time->z == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_INVALID_TZ_OFFSET, "Invalid timezone offset in minutes", string, begin); + } + break; + case TIMELIB_FORMAT_LITERAL: default: if (*fptr != *ptr) { add_pbf_error(s, TIMELIB_ERR_WRONG_FORMAT_SEP, "The format separator does not match", string, begin); @@ -2182,32 +2350,32 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, add_pbf_error(s, TIMELIB_ERR_TRAILING_DATA, "Trailing data", string, ptr); } } - /* ignore trailing +'s */ - while (*fptr == '+') { - fptr++; - } + if (*fptr) { - /* Trailing | and ! specifiers are valid. */ + /* Trailing reset specifiers are valid. */ int done = 0; while (*fptr && !done) { - switch (*fptr++) { - case '!': /* reset all fields to default */ + switch (timelib_lookup_format(*fptr, format_map)) { + case TIMELIB_FORMAT_RESET_ALL: /* reset all fields to default */ timelib_time_reset_fields(s->time); break; - case '|': /* reset all fields to default when not set */ + case TIMELIB_FORMAT_RESET_ALL_WHEN_NOT_SET: /* reset all fields to default when not set */ timelib_time_reset_unset_fields(s->time); break; + case TIMELIB_FORMAT_ALLOW_EXTRA_CHARACTERS: + break; default: add_pbf_error(s, TIMELIB_ERR_DATA_MISSING, "Data missing", string, ptr); done = 1; } + fptr++; } } /* clean up a bit */ - if (s->time->h != TIMELIB_UNSET || s->time->i != TIMELIB_UNSET || s->time->s != TIMELIB_UNSET) { + if (s->time->h != TIMELIB_UNSET || s->time->i != TIMELIB_UNSET || s->time->s != TIMELIB_UNSET || s->time->us != TIMELIB_UNSET) { if (s->time->h == TIMELIB_UNSET ) { s->time->h = 0; } @@ -2217,6 +2385,31 @@ timelib_time *timelib_parse_from_format(char *format, char *string, size_t len, if (s->time->s == TIMELIB_UNSET ) { s->time->s = 0; } + if (s->time->us == TIMELIB_UNSET ) { + s->time->us = 0; + } + } + + /* Check for mixing of ISO dates with natural dates. */ + if (s->time->y != TIMELIB_UNSET && (iso_week_of_year != TIMELIB_UNSET || iso_year != TIMELIB_UNSET || iso_day_of_week != TIMELIB_UNSET)) { + add_pbf_error(s, TIMELIB_ERR_MIX_ISO_WITH_NATURAL, "Mixing of ISO dates with natural dates is not allowed", string, ptr); + } + if (iso_year != TIMELIB_UNSET && (s->time->y != TIMELIB_UNSET || s->time->m != TIMELIB_UNSET || s->time->d != TIMELIB_UNSET)) { + add_pbf_error(s, TIMELIB_ERR_MIX_ISO_WITH_NATURAL, "Mixing of ISO dates with natural dates is not allowed", string, ptr); + } + + /* Convert ISO values */ + if (iso_year != TIMELIB_UNSET) { + /* Default week of year and day of week to 1. */ + if (iso_week_of_year == TIMELIB_UNSET) { + iso_week_of_year = 1; + } + if (iso_day_of_week == TIMELIB_UNSET) { + iso_day_of_week = 1; + } + timelib_date_from_isodate(iso_year, iso_week_of_year, iso_day_of_week, &s->time->y, &s->time->m, &s->time->d); + } else if (iso_week_of_year != TIMELIB_UNSET || iso_day_of_week != TIMELIB_UNSET) { + add_pbf_warning(s, TIMELIB_WARN_INVALID_DATE, "The parsed date was invalid", string, ptr); } /* do funky checking whether the parsed time was valid time */ diff --git a/ext/date/lib/parse_iso_intervals.c b/ext/date/lib/parse_iso_intervals.c index 60708469ec..e3677c2bf6 100644 --- a/ext/date/lib/parse_iso_intervals.c +++ b/ext/date/lib/parse_iso_intervals.c @@ -1,5 +1,5 @@ -/* Generated by re2c 0.15.3 on Wed Jan 31 11:51:44 2018 */ -#line 1 "ext/date/lib/parse_iso_intervals.re" +/* Generated by re2c 0.15.3 on Tue Oct 9 10:50:29 2018 */ +#line 1 "parse_iso_intervals.re" /* * The MIT License (MIT) * @@ -176,7 +176,7 @@ static int scan(Scanner *s) std: s->tok = cursor; s->len = 0; -#line 204 "ext/date/lib/parse_iso_intervals.re" +#line 204 "parse_iso_intervals.re" @@ -185,38 +185,38 @@ std: YYCTYPE yych; unsigned int yyaccept = 0; static const unsigned char yybm[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, }; YYDEBUG(0, *YYCURSOR); if ((YYLIMIT - YYCURSOR) < 20) YYFILL(20); @@ -249,7 +249,7 @@ std: if (yych <= '9') goto yy98; yy3: YYDEBUG(3, *YYCURSOR); -#line 317 "ext/date/lib/parse_iso_intervals.re" +#line 317 "parse_iso_intervals.re" { add_error(s, "Unexpected character"); goto std; @@ -271,7 +271,7 @@ yy5: if (yych == 'T') goto yy14; yy6: YYDEBUG(6, *YYCURSOR); -#line 244 "ext/date/lib/parse_iso_intervals.re" +#line 244 "parse_iso_intervals.re" { timelib_sll nr; int in_time = 0; @@ -317,7 +317,7 @@ yy7: YYDEBUG(7, *YYCURSOR); ++YYCURSOR; YYDEBUG(8, *YYCURSOR); -#line 306 "ext/date/lib/parse_iso_intervals.re" +#line 306 "parse_iso_intervals.re" { goto std; } @@ -326,7 +326,7 @@ yy9: YYDEBUG(9, *YYCURSOR); ++YYCURSOR; YYDEBUG(10, *YYCURSOR); -#line 311 "ext/date/lib/parse_iso_intervals.re" +#line 311 "parse_iso_intervals.re" { s->pos = cursor; s->line++; goto std; @@ -661,7 +661,7 @@ yy51: YYDEBUG(57, *YYCURSOR); ++YYCURSOR; YYDEBUG(58, *YYCURSOR); -#line 286 "ext/date/lib/parse_iso_intervals.re" +#line 286 "parse_iso_intervals.re" { DEBUG_OUTPUT("combinedrep"); TIMELIB_INIT; @@ -809,7 +809,7 @@ yy83: YYDEBUG(83, *YYCURSOR); ++YYCURSOR; YYDEBUG(84, *YYCURSOR); -#line 220 "ext/date/lib/parse_iso_intervals.re" +#line 220 "parse_iso_intervals.re" { timelib_time *current; @@ -910,7 +910,7 @@ yy98: if (yych <= '9') goto yy98; yy100: YYDEBUG(100, *YYCURSOR); -#line 209 "ext/date/lib/parse_iso_intervals.re" +#line 209 "parse_iso_intervals.re" { DEBUG_OUTPUT("recurrences"); TIMELIB_INIT; @@ -922,7 +922,7 @@ yy100: } #line 924 "<stdout>" } -#line 321 "ext/date/lib/parse_iso_intervals.re" +#line 321 "parse_iso_intervals.re" } #ifdef PHP_WIN32 diff --git a/ext/date/lib/parse_tz.c b/ext/date/lib/parse_tz.c index 960e5288f0..5986cd6468 100644 --- a/ext/date/lib/parse_tz.c +++ b/ext/date/lib/parse_tz.c @@ -585,7 +585,7 @@ static ttinfo* fetch_timezone_offset(timelib_tzinfo *tz, timelib_sll ts, timelib return &(tz->type[tz->trans_idx[j]]); } - /* In all other cases we loop through the available transtion times to find + /* In all other cases we loop through the available transition times to find * the correct entry */ for (i = 0; i < tz->bit32.timecnt; i++) { if (ts < tz->trans[i]) { diff --git a/ext/date/lib/timelib.h b/ext/date/lib/timelib.h index 3eb39a5569..3ab2267608 100644 --- a/ext/date/lib/timelib.h +++ b/ext/date/lib/timelib.h @@ -30,6 +30,7 @@ #endif #include <stdlib.h> +#include <stdbool.h> #include <limits.h> #include <inttypes.h> @@ -264,6 +265,16 @@ typedef struct _timelib_abbr_info { #define TIMELIB_ERR_WRONG_FORMAT_SEP 0x219 #define TIMELIB_ERR_TRAILING_DATA 0x21a #define TIMELIB_ERR_DATA_MISSING 0x21b +#define TIMELIB_ERR_NO_THREE_DIGIT_MILLISECOND 0x21c +#define TIMELIB_ERR_NO_FOUR_DIGIT_YEAR_ISO 0x21d +#define TIMELIB_ERR_NO_TWO_DIGIT_WEEK 0x21e +#define TIMELIB_ERR_INVALID_WEEK 0x21f +#define TIMELIB_ERR_NO_DAY_OF_WEEK 0x220 +#define TIMELIB_ERR_INVALID_DAY_OF_WEEK 0x221 +#define TIMELIB_ERR_INVALID_SPECIFIER 0x222 +#define TIMELIB_ERR_INVALID_TZ_OFFSET 0x223 +#define TIMELIB_ERR_FORMAT_LITERAL_MISMATCH 0x224 +#define TIMELIB_ERR_MIX_ISO_WITH_NATURAL 0x225 #define TIMELIB_ZONETYPE_OFFSET 1 #define TIMELIB_ZONETYPE_ABBR 2 @@ -310,9 +321,9 @@ typedef struct _timelib_tzdb { # define timelib_free free #endif -#define TIMELIB_VERSION 201706 -#define TIMELIB_EXTENDED_VERSION 20170600 -#define TIMELIB_ASCII_VERSION "2017.06" +#define TIMELIB_VERSION 201801 +#define TIMELIB_EXTENDED_VERSION 20180002 +#define TIMELIB_ASCII_VERSION "2018.01alpha2" #define TIMELIB_NONE 0x00 #define TIMELIB_OVERRIDE_TIME 0x01 @@ -334,6 +345,60 @@ typedef struct _timelib_tzdb { extern "C" { #endif +typedef enum _timelib_format_specifier_code { + TIMELIB_FORMAT_ALLOW_EXTRA_CHARACTERS = 0, + TIMELIB_FORMAT_ANY_SEPARATOR, + TIMELIB_FORMAT_DAY_TWO_DIGIT, + TIMELIB_FORMAT_DAY_TWO_DIGIT_PADDED, + TIMELIB_FORMAT_DAY_OF_WEEK_ISO, + TIMELIB_FORMAT_DAY_OF_WEEK, + TIMELIB_FORMAT_DAY_OF_YEAR, + TIMELIB_FORMAT_DAY_SUFFIX, + TIMELIB_FORMAT_END, + TIMELIB_FORMAT_EPOCH_SECONDS, + TIMELIB_FORMAT_ESCAPE, + TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX, + TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX_PADDED, + TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX, + TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX_PADDED, + TIMELIB_FORMAT_LITERAL, + TIMELIB_FORMAT_MERIDIAN, + TIMELIB_FORMAT_MICROSECOND_SIX_DIGIT, + TIMELIB_FORMAT_MILLISECOND_THREE_DIGIT, + TIMELIB_FORMAT_MINUTE_TWO_DIGIT, + TIMELIB_FORMAT_MONTH_TWO_DIGIT, + TIMELIB_FORMAT_MONTH_TWO_DIGIT_PADDED, + TIMELIB_FORMAT_RANDOM_CHAR, + TIMELIB_FORMAT_RESET_ALL, + TIMELIB_FORMAT_RESET_ALL_WHEN_NOT_SET, + TIMELIB_FORMAT_SECOND_TWO_DIGIT, + TIMELIB_FORMAT_SEPARATOR, + TIMELIB_FORMAT_SKIP_TO_SEPARATOR, + TIMELIB_FORMAT_TEXTUAL_DAY_3_LETTER, + TIMELIB_FORMAT_TEXTUAL_DAY_FULL, + TIMELIB_FORMAT_TEXTUAL_MONTH_3_LETTER, + TIMELIB_FORMAT_TEXTUAL_MONTH_FULL, + TIMELIB_FORMAT_TIMEZONE_OFFSET, + TIMELIB_FORMAT_TIMEZONE_OFFSET_MINUTES, + TIMELIB_FORMAT_WEEK_OF_YEAR_ISO, + TIMELIB_FORMAT_WEEK_OF_YEAR, + TIMELIB_FORMAT_WHITESPACE, + TIMELIB_FORMAT_YEAR_TWO_DIGIT, + TIMELIB_FORMAT_YEAR_FOUR_DIGIT, + TIMELIB_FORMAT_YEAR_ISO +} timelib_format_specifier_code; + +typedef struct _timelib_format_specifier { + char specifier; + timelib_format_specifier_code code; +} timelib_format_specifier; + +typedef struct _timelib_format_config { + const timelib_format_specifier *format_map; + /* Format speciifiers must be preceded by 'prefix_char' if not '\0'. */ + char prefix_char; +} timelib_format_config; + /* Function pointers */ typedef timelib_tzinfo* (*timelib_tz_get_wrapper)(char *tzname, const timelib_tzdb *tzdb, int *error_code); @@ -416,6 +481,20 @@ timelib_time *timelib_strtotime(char *s, size_t len, timelib_error_container **e */ timelib_time *timelib_parse_from_format(char *format, char *s, size_t len, timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper); +/* Parses the date/time string in 's' with length 'len' into the constituent + * parts of timelib_time* according to the format in 'format' with format + * specifier configuration 'format_config'. + * + * 'format_map' is an array of pairs, with the first element being the format + * specifier as a character and the second element corresponds to the + * representation of the specifier from the enum list + * 'timelib_format_specifier_code'. + * + * Note: 'format_map' must be terminated with specifier '\0' to indicate to the + * parser that there are no more format specifiers in the list. + */ +timelib_time *timelib_parse_from_format_with_map(char *format, char *s, size_t len, timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper, const timelib_format_config* format_config); + /* Fills the gaps in the parsed timelib_time with information from the reference date/time in 'now' * * If any of the 'parsed' y, m, d, h, i or s parameters is unset (TIMELIB_UNSET): @@ -488,8 +567,8 @@ timelib_long timelib_parse_zone(char **ptr, int *dst, timelib_time *t, int *tz_n */ void timelib_strtointerval(char *s, size_t len, timelib_time **begin, timelib_time **end, - timelib_rel_time **period, int *recurrences, - timelib_error_container **errors); + timelib_rel_time **period, int *recurrences, + timelib_error_container **errors); /* From tm2unixtime.c */ @@ -630,7 +709,7 @@ int timelib_timestamp_is_in_dst(timelib_sll ts, timelib_tzinfo *tz); * The returned information contains: the offset in seconds East of UTC (in * 'offset'), whether DST is active ('is_dst'), what the current time zone * abbreviation is ('abbr') and the transition time that got to this state (in - * 'transistion_time'); + * 'transition_time'); */ timelib_time_offset *timelib_get_time_zone_info(timelib_sll ts, timelib_tzinfo *tz); diff --git a/ext/date/lib/timezonemap.h b/ext/date/lib/timezonemap.h index d1aa92386f..e4a96ea57a 100644 --- a/ext/date/lib/timezonemap.h +++ b/ext/date/lib/timezonemap.h @@ -155,9 +155,9 @@ { "bmt", 0, 25632, "Asia/Jakarta" }, { "bmt", 0, 6264, "Europe/Bucharest" }, { "bmt", 0, 6264, "Europe/Chisinau" }, - { "bost", 1, -12756, "America/La_Paz" }, { "bst", 1, 3600, "Europe/London" }, { "bst", 0, 3600, "Europe/London" }, + { "bst", 1, -12756, "America/La_Paz" }, { "bst", 0, -39600, "America/Adak" }, { "bst", 0, -39600, "America/Atka" }, { "bst", 0, -39600, "America/Nome" }, @@ -672,6 +672,9 @@ { "gmt", 0, 0, "Europe/Jersey" }, { "gmt", 0, 0, "Europe/London" }, { "gmt", 0, 0, "GB" }, + { "gmt", 1, 0, "Europe/Bratislava" }, + { "gmt", 1, 0, "Europe/Dublin" }, + { "gmt", 1, 0, "Europe/Prague" }, { "gst", 0, 36000, "Pacific/Guam" }, { "gst", 0, 36000, "Pacific/Saipan" }, { "hdt", 1, -34200, "Pacific/Honolulu" }, @@ -728,8 +731,8 @@ { "kdt", 1, 34200, "ROK" }, { "kdt", 1, 36000, "ROK" }, { "kmt", 0, 5736, "Europe/Vilnius" }, - { "kmt", 0, -18431, "America/Grand_Turk" }, - { "kmt", 0, -18431, "America/Jamaica" }, + { "kmt", 0, -18430, "America/Grand_Turk" }, + { "kmt", 0, -18430, "America/Jamaica" }, { "kmt", 0, 7324, "Europe/Kiev" }, { "kst", 0, 30600, "Asia/Seoul" }, { "kst", 0, 32400, "Asia/Pyongyang" }, @@ -764,7 +767,7 @@ { "mdt", 1, -21600, "Mexico/BajaSur" }, { "mmt", 0, 9017, "Europe/Moscow" }, { "mmt", 0, 9079, "Europe/Moscow" }, - { "mmt", 0, -13484, "America/Montevideo" }, + { "mmt", 0, -13491, "America/Montevideo" }, { "mmt", 0, -20712, "America/Managua" }, { "mmt", 0, -2588, "Africa/Monrovia" }, { "mmt", 0, -2670, "Africa/Monrovia" }, @@ -1042,7 +1045,8 @@ { "wat", 0, 3600, "Africa/Ndjamena" }, { "wat", 0, 3600, "Africa/Niamey" }, { "wat", 0, 3600, "Africa/Porto-Novo" }, - { "wat", 0, 3600, "Africa/Windhoek" }, + { "wat", 0, 3600, "Africa/Sao_Tome" }, + { "wat", 1, 3600, "Africa/Windhoek" }, { "wemt", 1, 7200, "Europe/Lisbon" }, { "wemt", 1, 7200, "Europe/Madrid" }, { "wemt", 1, 7200, "Europe/Monaco" }, diff --git a/ext/date/lib/tm2unixtime.c b/ext/date/lib/tm2unixtime.c index 42a59cfaa6..7d6b7d8844 100644 --- a/ext/date/lib/tm2unixtime.c +++ b/ext/date/lib/tm2unixtime.c @@ -39,7 +39,7 @@ static void do_range_limit_fraction(timelib_sll *fraction, timelib_sll *seconds) *fraction += 1000000; *seconds -= 1; } - if (*fraction > 1000000) { + if (*fraction >= 1000000) { *fraction -= 1000000; *seconds += 1; } diff --git a/ext/date/lib/unixtime2tm.c b/ext/date/lib/unixtime2tm.c index a88b3a6f17..98c4f9b32b 100644 --- a/ext/date/lib/unixtime2tm.c +++ b/ext/date/lib/unixtime2tm.c @@ -96,7 +96,7 @@ void timelib_unixtime2gmt(timelib_time* tm, timelib_sll ts) } TIMELIB_DEBUG(printf("A: ts=%lld, year=%lld, month=%lld, day=%lld,", ts, cur_year, i + 1, tmp_days - months[i]);); - /* That was the date, now we do the tiiiime */ + /* That was the date, now we do the time */ hours = remainder / 3600; minutes = (remainder - hours * 3600) / 60; seconds = remainder % 60; @@ -251,7 +251,7 @@ void timelib_set_timezone(timelib_time *t, timelib_tzinfo *tz) /* Converts the time stored in the struct to localtime if localtime = true, * otherwise it converts it to gmttime. This is only done when necessary - * ofcourse. */ + * of course. */ int timelib_apply_localtime(timelib_time *t, unsigned int localtime) { if (localtime) { diff --git a/ext/date/tests/bug75577.phpt b/ext/date/tests/bug75577.phpt new file mode 100644 index 0000000000..db74908d9d --- /dev/null +++ b/ext/date/tests/bug75577.phpt @@ -0,0 +1,22 @@ +--TEST-- +Test for bug #75577: DateTime::createFromFormat does not accept 'v' format specifier +--INI-- +date.timezone=Europe/London +--FILE-- +<?php +$d = new DateTime("2018-10-09 09:56:45.412000"); +var_dump($s = $d->format(DateTime::RFC3339_EXTENDED)); + +$d2 = DateTime::createFromFormat(DateTime::RFC3339_EXTENDED, $s); +var_dump($d2); +?> +--EXPECTF-- +string(29) "2018-10-09T09:56:45.412+01:00" +object(DateTime)#%d (3) { + ["date"]=> + string(26) "2018-10-09 09:56:45.412000" + ["timezone_type"]=> + int(1) + ["timezone"]=> + string(6) "+01:00" +} |