]> granicus.if.org Git - php/commitdiff
Fixed bug #75577: DateTime::createFromFormat does not accept 'v' format specifier...
authorDerick Rethans <github@derickrethans.nl>
Tue, 9 Oct 2018 10:06:42 +0000 (11:06 +0100)
committerDerick Rethans <github@derickrethans.nl>
Tue, 9 Oct 2018 10:08:40 +0000 (11:08 +0100)
ext/date/lib/parse_date.c
ext/date/lib/parse_date.re
ext/date/lib/parse_iso_intervals.c
ext/date/lib/parse_tz.c
ext/date/lib/timelib.h
ext/date/lib/timezonemap.h
ext/date/lib/tm2unixtime.c
ext/date/lib/unixtime2tm.c

index e79e0ebf61542fde9eb9fe777aae170aa1c68296..5be4d36e6702f7558d3975dbf71d8953fd237deb 100644 (file)
@@ -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 = &in;
-       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 = &in;
+       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 */
index 425f654f16184b6d3972b2d5e462bb1c9eb3ae1d..6c84a719174b53f3aa7c1a5ae0ebdf91b8575132 100644 (file)
@@ -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 = &in;
-       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 = &in;
+       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 */
index 60708469ec50534a7b8aed1aa753974633b42a5d..e3677c2bf6c9b98a80b84b8a142a0fbf78aefeb0 100644 (file)
@@ -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
index 960e5288f0c933aa6dacec1c069b7294b1fe39a8..5986cd6468a545150774a31222ee6c5d20a90cf0 100644 (file)
@@ -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]) {
index 3eb39a55693ec653735d96cb71163240ad79550c..3ab226760872ec444c8e19ce847a33a80405effd 100644 (file)
@@ -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);
 
index d1aa92386fbe584632b6c04f3b4b63d4ff746c71..e4a96ea57af1e67f0f47196954567b60c16bb716 100644 (file)
        { "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"                  },
        { "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"              },
        { "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"                },
        { "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"               },
        { "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"                 },
index 42a59cfaa65538aa30f57a126bdbceee1fe66fe8..7d6b7d8844de71b078fcfb036c08a72d1ee73957 100644 (file)
@@ -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;
        }
index a88b3a6f170c5ae7273ef57206ac531191e8648b..98c4f9b32b9c51d947d02d6c86924aa87d7d16ce 100644 (file)
@@ -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) {