1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2013 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Derick Rethans <derick@derickrethans.nl>                    |
16   +----------------------------------------------------------------------+
17 */
18
19/* $Id$ */
20
21#include "timelib.h"
22
23#include <stdio.h>
24#include <ctype.h>
25#include <math.h>
26#include <assert.h>
27
28#ifdef HAVE_STDLIB_H
29#include <stdlib.h>
30#endif
31#ifdef HAVE_STRING_H
32#include <string.h>
33#else
34#include <strings.h>
35#endif
36
37#if defined(_MSC_VER)
38# define strtoll(s, f, b) _atoi64(s)
39#elif !defined(HAVE_STRTOLL)
40# if defined(HAVE_ATOLL)
41#  define strtoll(s, f, b) atoll(s)
42# else
43#  define strtoll(s, f, b) strtol(s, f, b)
44# endif
45#endif
46
47#define TIMELIB_UNSET   -99999
48
49#define TIMELIB_SECOND  1
50#define TIMELIB_MINUTE  2
51#define TIMELIB_HOUR    3
52#define TIMELIB_DAY     4
53#define TIMELIB_MONTH   5
54#define TIMELIB_YEAR    6
55#define TIMELIB_WEEKDAY 7
56#define TIMELIB_SPECIAL 8
57
58#define EOI      257
59#define TIME     258
60#define DATE     259
61
62#define TIMELIB_XMLRPC_SOAP    260
63#define TIMELIB_TIME12         261
64#define TIMELIB_TIME24         262
65#define TIMELIB_GNU_NOCOLON    263
66#define TIMELIB_GNU_NOCOLON_TZ 264
67#define TIMELIB_ISO_NOCOLON    265
68
69#define TIMELIB_AMERICAN       266
70#define TIMELIB_ISO_DATE       267
71#define TIMELIB_DATE_FULL      268
72#define TIMELIB_DATE_TEXT      269
73#define TIMELIB_DATE_NOCOLON   270
74#define TIMELIB_PG_YEARDAY     271
75#define TIMELIB_PG_TEXT        272
76#define TIMELIB_PG_REVERSE     273
77#define TIMELIB_CLF            274
78#define TIMELIB_DATE_NO_DAY    275
79#define TIMELIB_SHORTDATE_WITH_TIME 276
80#define TIMELIB_DATE_FULL_POINTED 277
81#define TIMELIB_TIME24_WITH_ZONE 278
82#define TIMELIB_ISO_WEEK       279
83#define TIMELIB_LF_DAY_OF_MONTH 280
84#define TIMELIB_WEEK_DAY_OF_MONTH 281
85
86#define TIMELIB_TIMEZONE       300
87#define TIMELIB_AGO            301
88
89#define TIMELIB_RELATIVE       310
90
91#define TIMELIB_ERROR          999
92
93/* Some compilers like AIX, defines uchar in sys/types.h */
94#undef uchar
95typedef unsigned char uchar;
96
97#define   BSIZE    8192
98
99#define   YYCTYPE      uchar
100#define   YYCURSOR     cursor
101#define   YYLIMIT      s->lim
102#define   YYMARKER     s->ptr
103#define   YYFILL(n)    return EOI;
104
105#define   RET(i)       {s->cur = cursor; return i;}
106
107#define timelib_string_free free
108
109#define TIMELIB_HAVE_TIME() { if (s->time->have_time) { add_error(s, "Double time specification"); timelib_string_free(str); return TIMELIB_ERROR; } else { s->time->have_time = 1; s->time->h = 0; s->time->i = 0; s->time->s = 0; s->time->f = 0; } }
110#define TIMELIB_UNHAVE_TIME() { s->time->have_time = 0; s->time->h = 0; s->time->i = 0; s->time->s = 0; s->time->f = 0; }
111#define TIMELIB_HAVE_DATE() { if (s->time->have_date) { add_error(s, "Double date specification"); timelib_string_free(str); return TIMELIB_ERROR; } else { s->time->have_date = 1; } }
112#define TIMELIB_UNHAVE_DATE() { s->time->have_date = 0; s->time->d = 0; s->time->m = 0; s->time->y = 0; }
113#define TIMELIB_HAVE_RELATIVE() { s->time->have_relative = 1; }
114#define TIMELIB_HAVE_WEEKDAY_RELATIVE() { s->time->have_relative = 1; s->time->relative.have_weekday_relative = 1; }
115#define TIMELIB_HAVE_SPECIAL_RELATIVE() { s->time->have_relative = 1; s->time->relative.have_special_relative = 1; }
116#define TIMELIB_HAVE_TZ() { s->cur = cursor; if (s->time->have_zone) { s->time->have_zone > 1 ? add_error(s, "Double timezone specification") : add_warning(s, "Double timezone specification"); timelib_string_free(str); s->time->have_zone++; return TIMELIB_ERROR; } else { s->time->have_zone++; } }
117
118#define TIMELIB_INIT  s->cur = cursor; str = timelib_string(s); ptr = str
119#define TIMELIB_DEINIT timelib_string_free(str)
120#define TIMELIB_ADJUST_RELATIVE_WEEKDAY() if (in->time.have_weekday_relative && (in.rel.d > 0)) { in.rel.d -= 7; }
121
122#define TIMELIB_PROCESS_YEAR(x, l) { \
123    if (((x) == TIMELIB_UNSET) || ((l) >= 4)) { \
124    /*  (x) = 0; */          \
125    } else if ((x) < 100) {  \
126        if ((x) < 70) {      \
127            (x) += 2000;     \
128        } else {             \
129            (x) += 1900;     \
130        }                    \
131    }                        \
132}
133
134#ifdef DEBUG_PARSER
135#define DEBUG_OUTPUT(s) printf("%s\n", s);
136#define YYDEBUG(s,c) { if (s != -1) { printf("state: %d ", s); printf("[%c]\n", c); } }
137#else
138#define DEBUG_OUTPUT(s)
139#define YYDEBUG(s,c)
140#endif
141
142#include "timelib_structs.h"
143
144typedef struct timelib_elems {
145    unsigned int   c; /* Number of elements */
146    char         **v; /* Values */
147} timelib_elems;
148
149typedef struct Scanner {
150    int           fd;
151    uchar        *lim, *str, *ptr, *cur, *tok, *pos;
152    unsigned int  line, len;
153    struct timelib_error_container *errors;
154
155    struct timelib_time *time;
156    const timelib_tzdb  *tzdb;
157} Scanner;
158
159typedef struct _timelib_lookup_table {
160    const char *name;
161    int         type;
162    int         value;
163} timelib_lookup_table;
164
165typedef struct _timelib_relunit {
166    const char *name;
167    int         unit;
168    int         multiplier;
169} timelib_relunit;
170
171#define HOUR(a) (int)(a * 60)
172
173/* The timezone table. */
174const static timelib_tz_lookup_table timelib_timezone_lookup[] = {
175#include "timezonemap.h"
176    { NULL, 0, 0, NULL },
177};
178
179const static timelib_tz_lookup_table timelib_timezone_fallbackmap[] = {
180#include "fallbackmap.h"
181    { NULL, 0, 0, NULL },
182};
183
184const static timelib_tz_lookup_table timelib_timezone_utc[] = {
185    { "utc", 0, 0, "UTC" },
186};
187
188static timelib_relunit const timelib_relunit_lookup[] = {
189    { "sec",         TIMELIB_SECOND,  1 },
190    { "secs",        TIMELIB_SECOND,  1 },
191    { "second",      TIMELIB_SECOND,  1 },
192    { "seconds",     TIMELIB_SECOND,  1 },
193    { "min",         TIMELIB_MINUTE,  1 },
194    { "mins",        TIMELIB_MINUTE,  1 },
195    { "minute",      TIMELIB_MINUTE,  1 },
196    { "minutes",     TIMELIB_MINUTE,  1 },
197    { "hour",        TIMELIB_HOUR,    1 },
198    { "hours",       TIMELIB_HOUR,    1 },
199    { "day",         TIMELIB_DAY,     1 },
200    { "days",        TIMELIB_DAY,     1 },
201    { "week",        TIMELIB_DAY,     7 },
202    { "weeks",       TIMELIB_DAY,     7 },
203    { "fortnight",   TIMELIB_DAY,    14 },
204    { "fortnights",  TIMELIB_DAY,    14 },
205    { "forthnight",  TIMELIB_DAY,    14 },
206    { "forthnights", TIMELIB_DAY,    14 },
207    { "month",       TIMELIB_MONTH,   1 },
208    { "months",      TIMELIB_MONTH,   1 },
209    { "year",        TIMELIB_YEAR,    1 },
210    { "years",       TIMELIB_YEAR,    1 },
211
212    { "monday",      TIMELIB_WEEKDAY, 1 },
213    { "mon",         TIMELIB_WEEKDAY, 1 },
214    { "tuesday",     TIMELIB_WEEKDAY, 2 },
215    { "tue",         TIMELIB_WEEKDAY, 2 },
216    { "wednesday",   TIMELIB_WEEKDAY, 3 },
217    { "wed",         TIMELIB_WEEKDAY, 3 },
218    { "thursday",    TIMELIB_WEEKDAY, 4 },
219    { "thu",         TIMELIB_WEEKDAY, 4 },
220    { "friday",      TIMELIB_WEEKDAY, 5 },
221    { "fri",         TIMELIB_WEEKDAY, 5 },
222    { "saturday",    TIMELIB_WEEKDAY, 6 },
223    { "sat",         TIMELIB_WEEKDAY, 6 },
224    { "sunday",      TIMELIB_WEEKDAY, 0 },
225    { "sun",         TIMELIB_WEEKDAY, 0 },
226
227    { "weekday",     TIMELIB_SPECIAL, TIMELIB_SPECIAL_WEEKDAY },
228    { "weekdays",    TIMELIB_SPECIAL, TIMELIB_SPECIAL_WEEKDAY },
229    { NULL,          0,          0 }
230};
231
232/* The relative text table. */
233static timelib_lookup_table const timelib_reltext_lookup[] = {
234    { "first",    0,  1 },
235    { "next",     0,  1 },
236    { "second",   0,  2 },
237    { "third",    0,  3 },
238    { "fourth",   0,  4 },
239    { "fifth",    0,  5 },
240    { "sixth",    0,  6 },
241    { "seventh",  0,  7 },
242    { "eight",    0,  8 },
243    { "eighth",   0,  8 },
244    { "ninth",    0,  9 },
245    { "tenth",    0, 10 },
246    { "eleventh", 0, 11 },
247    { "twelfth",  0, 12 },
248    { "last",     0, -1 },
249    { "previous", 0, -1 },
250    { "this",     1,  0 },
251    { NULL,       1,  0 }
252};
253
254/* The month table. */
255static timelib_lookup_table const timelib_month_lookup[] = {
256    { "jan",  0,  1 },
257    { "feb",  0,  2 },
258    { "mar",  0,  3 },
259    { "apr",  0,  4 },
260    { "may",  0,  5 },
261    { "jun",  0,  6 },
262    { "jul",  0,  7 },
263    { "aug",  0,  8 },
264    { "sep",  0,  9 },
265    { "sept", 0,  9 },
266    { "oct",  0, 10 },
267    { "nov",  0, 11 },
268    { "dec",  0, 12 },
269    { "i",    0,  1 },
270    { "ii",   0,  2 },
271    { "iii",  0,  3 },
272    { "iv",   0,  4 },
273    { "v",    0,  5 },
274    { "vi",   0,  6 },
275    { "vii",  0,  7 },
276    { "viii", 0,  8 },
277    { "ix",   0,  9 },
278    { "x",    0, 10 },
279    { "xi",   0, 11 },
280    { "xii",  0, 12 },
281
282    { "january",   0,  1 },
283    { "february",  0,  2 },
284    { "march",     0,  3 },
285    { "april",     0,  4 },
286    { "may",       0,  5 },
287    { "june",      0,  6 },
288    { "july",      0,  7 },
289    { "august",    0,  8 },
290    { "september", 0,  9 },
291    { "october",   0, 10 },
292    { "november",  0, 11 },
293    { "december",  0, 12 },
294    {  NULL,       0,  0 }
295};
296
297#if 0
298static char* timelib_ltrim(char *s)
299{
300    char *ptr = s;
301    while (ptr[0] == ' ' || ptr[0] == '\t') {
302        ptr++;
303    }
304    return ptr;
305}
306#endif
307
308#if 0
309uchar *fill(Scanner *s, uchar *cursor){
310    if(!s->eof){
311        unsigned int cnt = s->tok - s->bot;
312        if(cnt){
313            memcpy(s->bot, s->tok, s->lim - s->tok);
314            s->tok = s->bot;
315            s->ptr -= cnt;
316            cursor -= cnt;
317            s->pos -= cnt;
318            s->lim -= cnt;
319        }
320        if((s->top - s->lim) < BSIZE){
321            uchar *buf = (uchar*) malloc(((s->lim - s->bot) + BSIZE)*sizeof(uchar));
322            memcpy(buf, s->tok, s->lim - s->tok);
323            s->tok = buf;
324            s->ptr = &buf[s->ptr - s->bot];
325            cursor = &buf[cursor - s->bot];
326            s->pos = &buf[s->pos - s->bot];
327            s->lim = &buf[s->lim - s->bot];
328            s->top = &s->lim[BSIZE];
329            free(s->bot);
330            s->bot = buf;
331        }
332        if((cnt = read(s->fd, (char*) s->lim, BSIZE)) != BSIZE){
333            s->eof = &s->lim[cnt]; *(s->eof)++ = '\n';
334        }
335        s->lim += cnt;
336    }
337    return cursor;
338}
339#endif
340
341static void add_warning(Scanner *s, char *error)
342{
343    s->errors->warning_count++;
344    s->errors->warning_messages = realloc(s->errors->warning_messages, s->errors->warning_count * sizeof(timelib_error_message));
345    s->errors->warning_messages[s->errors->warning_count - 1].position = s->tok ? s->tok - s->str : 0;
346    s->errors->warning_messages[s->errors->warning_count - 1].character = s->tok ? *s->tok : 0;
347    s->errors->warning_messages[s->errors->warning_count - 1].message = strdup(error);
348}
349
350static void add_error(Scanner *s, char *error)
351{
352    s->errors->error_count++;
353    s->errors->error_messages = realloc(s->errors->error_messages, s->errors->error_count * sizeof(timelib_error_message));
354    s->errors->error_messages[s->errors->error_count - 1].position = s->tok ? s->tok - s->str : 0;
355    s->errors->error_messages[s->errors->error_count - 1].character = s->tok ? *s->tok : 0;
356    s->errors->error_messages[s->errors->error_count - 1].message = strdup(error);
357}
358
359static void add_pbf_warning(Scanner *s, char *error, char *sptr, char *cptr)
360{
361    s->errors->warning_count++;
362    s->errors->warning_messages = realloc(s->errors->warning_messages, s->errors->warning_count * sizeof(timelib_error_message));
363    s->errors->warning_messages[s->errors->warning_count - 1].position = cptr - sptr;
364    s->errors->warning_messages[s->errors->warning_count - 1].character = *cptr;
365    s->errors->warning_messages[s->errors->warning_count - 1].message = strdup(error);
366}
367
368static void add_pbf_error(Scanner *s, char *error, char *sptr, char *cptr)
369{
370    s->errors->error_count++;
371    s->errors->error_messages = realloc(s->errors->error_messages, s->errors->error_count * sizeof(timelib_error_message));
372    s->errors->error_messages[s->errors->error_count - 1].position = cptr - sptr;
373    s->errors->error_messages[s->errors->error_count - 1].character = *cptr;
374    s->errors->error_messages[s->errors->error_count - 1].message = strdup(error);
375}
376
377static timelib_sll timelib_meridian(char **ptr, timelib_sll h)
378{
379    timelib_sll retval = 0;
380
381    while (!strchr("AaPp", **ptr)) {
382        ++*ptr;
383    }
384    if (**ptr == 'a' || **ptr == 'A') {
385        if (h == 12) {
386            retval = -12;
387        }
388    } else if (h != 12) {
389        retval = 12;
390    }
391    ++*ptr;
392    if (**ptr == '.') {
393        *ptr += 3;
394    } else {
395        ++*ptr;
396    }
397    return retval;
398}
399
400static timelib_sll timelib_meridian_with_check(char **ptr, timelib_sll h)
401{
402    timelib_sll retval = 0;
403
404    while (!strchr("AaPp", **ptr)) {
405        ++*ptr;
406    }
407    if (**ptr == 'a' || **ptr == 'A') {
408        if (h == 12) {
409            retval = -12;
410        }
411    } else if (h != 12) {
412        retval = 12;
413    }
414    ++*ptr;
415    if (**ptr == '.') {
416        ++*ptr;
417        if (**ptr != 'm' && **ptr != 'M') {
418            return TIMELIB_UNSET;
419        }
420        ++*ptr;
421        if (**ptr != '.' ) {
422            return TIMELIB_UNSET;
423        }
424        ++*ptr;
425    } else if (**ptr == 'm' || **ptr == 'M') {
426        ++*ptr;
427    } else {
428        return TIMELIB_UNSET;
429    }
430    return retval;
431}
432
433static char *timelib_string(Scanner *s)
434{
435    char *tmp = calloc(1, s->cur - s->tok + 1);
436    memcpy(tmp, s->tok, s->cur - s->tok);
437
438    return tmp;
439}
440
441static timelib_sll timelib_get_nr_ex(char **ptr, int max_length, int *scanned_length)
442{
443    char *begin, *end, *str;
444    timelib_sll tmp_nr = TIMELIB_UNSET;
445    int len = 0;
446
447    while ((**ptr < '0') || (**ptr > '9')) {
448        if (**ptr == '\0') {
449            return TIMELIB_UNSET;
450        }
451        ++*ptr;
452    }
453    begin = *ptr;
454    while ((**ptr >= '0') && (**ptr <= '9') && len < max_length) {
455        ++*ptr;
456        ++len;
457    }
458    end = *ptr;
459    if (scanned_length) {
460        *scanned_length = end - begin;
461    }
462    str = calloc(1, end - begin + 1);
463    memcpy(str, begin, end - begin);
464    tmp_nr = strtoll(str, NULL, 10);
465    free(str);
466    return tmp_nr;
467}
468
469static timelib_sll timelib_get_nr(char **ptr, int max_length)
470{
471    return timelib_get_nr_ex(ptr, max_length, NULL);
472}
473
474static void timelib_skip_day_suffix(char **ptr)
475{
476    if (isspace(**ptr)) {
477        return;
478    }
479    if (!strncasecmp(*ptr, "nd", 2) || !strncasecmp(*ptr, "rd", 2) ||!strncasecmp(*ptr, "st", 2) || !strncasecmp(*ptr, "th", 2)) {
480        *ptr += 2;
481    }
482}
483
484static double timelib_get_frac_nr(char **ptr, int max_length)
485{
486    char *begin, *end, *str;
487    double tmp_nr = TIMELIB_UNSET;
488    int len = 0;
489
490    while ((**ptr != '.') && (**ptr != ':') && ((**ptr < '0') || (**ptr > '9'))) {
491        if (**ptr == '\0') {
492            return TIMELIB_UNSET;
493        }
494        ++*ptr;
495    }
496    begin = *ptr;
497    while (((**ptr == '.') || (**ptr == ':') || ((**ptr >= '0') && (**ptr <= '9'))) && len < max_length) {
498        ++*ptr;
499        ++len;
500    }
501    end = *ptr;
502    str = calloc(1, end - begin + 1);
503    memcpy(str, begin, end - begin);
504    if (str[0] == ':') {
505        str[0] = '.';
506    }
507    tmp_nr = strtod(str, NULL);
508    free(str);
509    return tmp_nr;
510}
511
512static timelib_ull timelib_get_unsigned_nr(char **ptr, int max_length)
513{
514    timelib_ull dir = 1;
515
516    while (((**ptr < '0') || (**ptr > '9')) && (**ptr != '+') && (**ptr != '-')) {
517        if (**ptr == '\0') {
518            return TIMELIB_UNSET;
519        }
520        ++*ptr;
521    }
522
523    while (**ptr == '+' || **ptr == '-')
524    {
525        if (**ptr == '-') {
526            dir *= -1;
527        }
528        ++*ptr;
529    }
530    return dir * timelib_get_nr(ptr, max_length);
531}
532
533static long timelib_parse_tz_cor(char **ptr)
534{
535    char *begin = *ptr, *end;
536    long  tmp;
537
538    while (isdigit(**ptr) || **ptr == ':') {
539        ++*ptr;
540    }
541    end = *ptr;
542    switch (end - begin) {
543        case 1:
544        case 2:
545            return HOUR(strtol(begin, NULL, 10));
546            break;
547        case 3:
548        case 4:
549            if (begin[1] == ':') {
550                tmp = HOUR(strtol(begin, NULL, 10)) + strtol(begin + 2, NULL, 10);
551                return tmp;
552            } else if (begin[2] == ':') {
553                tmp = HOUR(strtol(begin, NULL, 10)) + strtol(begin + 3, NULL, 10);
554                return tmp;
555            } else {
556                tmp = strtol(begin, NULL, 10);
557                return HOUR(tmp / 100) + tmp % 100;
558            }
559        case 5:
560            tmp = HOUR(strtol(begin, NULL, 10)) + strtol(begin + 3, NULL, 10);
561            return tmp;
562    }
563    return 0;
564}
565
566static timelib_sll timelib_lookup_relative_text(char **ptr, int *behavior)
567{
568    char *word;
569    char *begin = *ptr, *end;
570    timelib_sll  value = 0;
571    const timelib_lookup_table *tp;
572
573    while ((**ptr >= 'A' && **ptr <= 'Z') || (**ptr >= 'a' && **ptr <= 'z')) {
574        ++*ptr;
575    }
576    end = *ptr;
577    word = calloc(1, end - begin + 1);
578    memcpy(word, begin, end - begin);
579
580    for (tp = timelib_reltext_lookup; tp->name; tp++) {
581        if (strcasecmp(word, tp->name) == 0) {
582            value = tp->value;
583            *behavior = tp->type;
584        }
585    }
586
587    free(word);
588    return value;
589}
590
591static timelib_sll timelib_get_relative_text(char **ptr, int *behavior)
592{
593    while (**ptr == ' ' || **ptr == '\t' || **ptr == '-' || **ptr == '/') {
594        ++*ptr;
595    }
596    return timelib_lookup_relative_text(ptr, behavior);
597}
598
599static long timelib_lookup_month(char **ptr)
600{
601    char *word;
602    char *begin = *ptr, *end;
603    long  value = 0;
604    const timelib_lookup_table *tp;
605
606    while ((**ptr >= 'A' && **ptr <= 'Z') || (**ptr >= 'a' && **ptr <= 'z')) {
607        ++*ptr;
608    }
609    end = *ptr;
610    word = calloc(1, end - begin + 1);
611    memcpy(word, begin, end - begin);
612
613    for (tp = timelib_month_lookup; tp->name; tp++) {
614        if (strcasecmp(word, tp->name) == 0) {
615            value = tp->value;
616        }
617    }
618
619    free(word);
620    return value;
621}
622
623static long timelib_get_month(char **ptr)
624{
625    while (**ptr == ' ' || **ptr == '\t' || **ptr == '-' || **ptr == '.' || **ptr == '/') {
626        ++*ptr;
627    }
628    return timelib_lookup_month(ptr);
629}
630
631static void timelib_eat_spaces(char **ptr)
632{
633    while (**ptr == ' ' || **ptr == '\t') {
634        ++*ptr;
635    }
636}
637
638static void timelib_eat_until_separator(char **ptr)
639{
640    ++*ptr;
641    while (strchr(" \t.,:;/-0123456789", **ptr) == NULL) {
642        ++*ptr;
643    }
644}
645
646static const timelib_relunit* timelib_lookup_relunit(char **ptr)
647{
648    char *word;
649    char *begin = *ptr, *end;
650    const timelib_relunit *tp, *value = NULL;
651
652    while (**ptr != '\0' && **ptr != ' ' && **ptr != ',' && **ptr != '\t') {
653        ++*ptr;
654    }
655    end = *ptr;
656    word = calloc(1, end - begin + 1);
657    memcpy(word, begin, end - begin);
658
659    for (tp = timelib_relunit_lookup; tp->name; tp++) {
660        if (strcasecmp(word, tp->name) == 0) {
661            value = tp;
662            break;
663        }
664    }
665
666    free(word);
667    return value;
668}
669
670static void timelib_set_relative(char **ptr, timelib_sll amount, int behavior, Scanner *s)
671{
672    const timelib_relunit* relunit;
673
674    if (!(relunit = timelib_lookup_relunit(ptr))) {
675        return;
676    }
677
678    switch (relunit->unit) {
679        case TIMELIB_SECOND: s->time->relative.s += amount * relunit->multiplier; break;
680        case TIMELIB_MINUTE: s->time->relative.i += amount * relunit->multiplier; break;
681        case TIMELIB_HOUR:   s->time->relative.h += amount * relunit->multiplier; break;
682        case TIMELIB_DAY:    s->time->relative.d += amount * relunit->multiplier; break;
683        case TIMELIB_MONTH:  s->time->relative.m += amount * relunit->multiplier; break;
684        case TIMELIB_YEAR:   s->time->relative.y += amount * relunit->multiplier; break;
685
686        case TIMELIB_WEEKDAY:
687            TIMELIB_HAVE_WEEKDAY_RELATIVE();
688            TIMELIB_UNHAVE_TIME();
689            s->time->relative.d += (amount > 0 ? amount - 1 : amount) * 7;
690            s->time->relative.weekday = relunit->multiplier;
691            s->time->relative.weekday_behavior = behavior;
692            break;
693
694        case TIMELIB_SPECIAL:
695            TIMELIB_HAVE_SPECIAL_RELATIVE();
696            TIMELIB_UNHAVE_TIME();
697            s->time->relative.special.type = relunit->multiplier;
698            s->time->relative.special.amount = amount;
699    }
700}
701
702const static timelib_tz_lookup_table* zone_search(const char *word, long gmtoffset, int isdst)
703{
704    int first_found = 0;
705    const timelib_tz_lookup_table  *tp, *first_found_elem = NULL;
706    const timelib_tz_lookup_table  *fmp;
707
708    if (strcasecmp("utc", word) == 0 || strcasecmp("gmt", word) == 0) {
709        return timelib_timezone_utc;
710    }
711
712    for (tp = timelib_timezone_lookup; tp->name; tp++) {
713        if (strcasecmp(word, tp->name) == 0) {
714            if (!first_found) {
715                first_found = 1;
716                first_found_elem = tp;
717                if (gmtoffset == -1) {
718                    return tp;
719                }
720            }
721            if (tp->gmtoffset == gmtoffset) {
722                return tp;
723            }
724        }
725    }
726    if (first_found) {
727        return first_found_elem;
728    }
729
730    for (tp = timelib_timezone_lookup; tp->name; tp++) {
731        if (tp->full_tz_name && strcasecmp(word, tp->full_tz_name) == 0) {
732            if (!first_found) {
733                first_found = 1;
734                first_found_elem = tp;
735                if (gmtoffset == -1) {
736                    return tp;
737                }
738            }
739            if (tp->gmtoffset == gmtoffset) {
740                return tp;
741            }
742        }
743    }
744    if (first_found) {
745        return first_found_elem;
746    }
747
748
749    /* Still didn't find anything, let's find the zone solely based on
750     * offset/isdst then */
751    for (fmp = timelib_timezone_fallbackmap; fmp->name; fmp++) {
752        if ((fmp->gmtoffset * 3600) == gmtoffset && fmp->type == isdst) {
753            return fmp;
754        }
755    }
756    return NULL;
757}
758
759static long timelib_lookup_zone(char **ptr, int *dst, char **tz_abbr, int *found)
760{
761    char *word;
762    char *begin = *ptr, *end;
763    long  value = 0;
764    const timelib_tz_lookup_table *tp;
765
766    while (**ptr != '\0' && **ptr != ')' && **ptr != ' ') {
767        ++*ptr;
768    }
769    end = *ptr;
770    word = calloc(1, end - begin + 1);
771    memcpy(word, begin, end - begin);
772
773    if ((tp = zone_search(word, -1, 0))) {
774        value = -tp->gmtoffset / 60;
775        *dst = tp->type;
776        value += tp->type * 60;
777        *found = 1;
778    } else {
779        *found = 0;
780    }
781
782    *tz_abbr = word;
783    return value;
784}
785
786static long timelib_get_zone(char **ptr, int *dst, timelib_time *t, int *tz_not_found, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_wrapper)
787{
788    timelib_tzinfo *res;
789    long            retval = 0;
790
791    *tz_not_found = 0;
792
793    while (**ptr == ' ' || **ptr == '\t' || **ptr == '(') {
794        ++*ptr;
795    }
796    if ((*ptr)[0] == 'G' && (*ptr)[1] == 'M' && (*ptr)[2] == 'T' && ((*ptr)[3] == '+' || (*ptr)[3] == '-')) {
797        *ptr += 3;
798    }
799    if (**ptr == '+') {
800        ++*ptr;
801        t->is_localtime = 1;
802        t->zone_type = TIMELIB_ZONETYPE_OFFSET;
803        *tz_not_found = 0;
804        t->dst = 0;
805
806        retval = -1 * timelib_parse_tz_cor(ptr);
807    } else if (**ptr == '-') {
808        ++*ptr;
809        t->is_localtime = 1;
810        t->zone_type = TIMELIB_ZONETYPE_OFFSET;
811        *tz_not_found = 0;
812        t->dst = 0;
813
814        retval = timelib_parse_tz_cor(ptr);
815    } else {
816        int found = 0;
817        long offset;
818        char *tz_abbr;
819
820        t->is_localtime = 1;
821
822        offset = timelib_lookup_zone(ptr, dst, &tz_abbr, &found);
823        if (found) {
824            t->zone_type = TIMELIB_ZONETYPE_ABBR;
825        }
826#if 0
827        /* If we found a TimeZone identifier, use it */
828        if (tz_name) {
829            t->tz_info = timelib_parse_tzfile(tz_name);
830            t->zone_type = TIMELIB_ZONETYPE_ID;
831        }
832#endif
833        /* If we have a TimeZone identifier to start with, use it */
834        if (strstr(tz_abbr, "/") || strcmp(tz_abbr, "UTC") == 0) {
835            if ((res = tz_wrapper(tz_abbr, tzdb)) != NULL) {
836                t->tz_info = res;
837                t->zone_type = TIMELIB_ZONETYPE_ID;
838                found++;
839            }
840        }
841        if (found && t->zone_type != TIMELIB_ZONETYPE_ID) {
842            timelib_time_tz_abbr_update(t, tz_abbr);
843        }
844        free(tz_abbr);
845        *tz_not_found = (found == 0);
846        retval = offset;
847    }
848    while (**ptr == ')') {
849        ++*ptr;
850    }
851    return retval;
852}
853
854#define timelib_split_free(arg) {       \
855    int i;                         \
856    for (i = 0; i < arg.c; i++) {  \
857        free(arg.v[i]);            \
858    }                              \
859    if (arg.v) {                   \
860        free(arg.v);               \
861    }                              \
862}
863
864static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
865{
866    uchar *cursor = s->cur;
867    char *str, *ptr = NULL;
868
869std:
870    s->tok = cursor;
871    s->len = 0;
872/*!re2c
873any = [\000-\377];
874
875space = [ \t]+;
876frac = "."[0-9]+;
877
878ago = 'ago';
879
880hour24 = [01]?[0-9] | "2"[0-4];
881hour24lz = [01][0-9] | "2"[0-4];
882hour12 = "0"?[1-9] | "1"[0-2];
883minute = [0-5]?[0-9];
884minutelz = [0-5][0-9];
885second = minute | "60";
886secondlz = minutelz | "60";
887meridian = ([AaPp] "."? [Mm] "."?) [\000\t ];
888tz = "("? [A-Za-z]{1,6} ")"? | [A-Z][a-z]+([_/-][A-Za-z]+)+;
889tzcorrection = "GMT"? [+-] hour24 ":"? minute?;
890
891daysuf = "st" | "nd" | "rd" | "th";
892
893month = "0"? [0-9] | "1"[0-2];
894day   = (([0-2]?[0-9]) | ("3"[01])) daysuf?;
895year  = [0-9]{1,4};
896year2 = [0-9]{2};
897year4 = [0-9]{4};
898year4withsign = [+-]? [0-9]{4};
899
900dayofyear = "00"[1-9] | "0"[1-9][0-9] | [1-2][0-9][0-9] | "3"[0-5][0-9] | "36"[0-6];
901weekofyear = "0"[1-9] | [1-4][0-9] | "5"[0-3];
902
903monthlz = "0" [0-9] | "1" [0-2];
904daylz   = "0" [0-9] | [1-2][0-9] | "3" [01];
905
906dayfull = 'sunday' | 'monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday';
907dayabbr = 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat' | 'sun';
908dayspecial = 'weekday' | 'weekdays';
909daytext = dayfull | dayabbr | dayspecial;
910
911monthfull = 'january' | 'february' | 'march' | 'april' | 'may' | 'june' | 'july' | 'august' | 'september' | 'october' | 'november' | 'december';
912monthabbr = 'jan' | 'feb' | 'mar' | 'apr' | 'may' | 'jun' | 'jul' | 'aug' | 'sep' | 'sept' | 'oct' | 'nov' | 'dec';
913monthroman = "I" | "II" | "III" | "IV" | "V" | "VI" | "VII" | "VIII" | "IX" | "X" | "XI" | "XII";
914monthtext = monthfull | monthabbr | monthroman;
915
916/* Time formats */
917timetiny12 = hour12 space? meridian;
918timeshort12 = hour12[:.]minutelz space? meridian;
919timelong12 = hour12[:.]minute[:.]secondlz space? meridian;
920
921timeshort24 = 't'? hour24[:.]minute;
922timelong24 =  't'? hour24[:.]minute[:.]second;
923iso8601long =  't'? hour24 [:.] minute [:.] second frac;
924
925/* iso8601shorttz = hour24 [:] minutelz space? (tzcorrection | tz); */
926iso8601normtz =  't'? hour24 [:.] minute [:.] secondlz space? (tzcorrection | tz);
927/* iso8601longtz =  hour24 [:] minute [:] secondlz frac space? (tzcorrection | tz); */
928
929gnunocolon       = 't'? hour24lz minutelz;
930/* gnunocolontz     = hour24lz minutelz space? (tzcorrection | tz); */
931iso8601nocolon   = 't'? hour24lz minutelz secondlz;
932/* iso8601nocolontz = hour24lz minutelz secondlz space? (tzcorrection | tz); */
933
934/* Date formats */
935americanshort    = month "/" day;
936american         = month "/" day "/" year;
937iso8601dateslash = year4 "/" monthlz "/" daylz "/"?;
938dateslash        = year4 "/" month "/" day;
939iso8601date4     = year4withsign "-" monthlz "-" daylz;
940iso8601date2     = year2 "-" monthlz "-" daylz;
941gnudateshorter   = year4 "-" month;
942gnudateshort     = year "-" month "-" day;
943pointeddate4     = day [.\t-] month [.-] year4;
944pointeddate2     = day [.\t] month "." year2;
945datefull         = day ([ \t.-])* monthtext ([ \t.-])* year;
946datenoday        = monthtext ([ .\t-])* year4;
947datenodayrev     = year4 ([ .\t-])* monthtext;
948datetextual      = monthtext ([ .\t-])* day [,.stndrh\t ]+ year;
949datenoyear       = monthtext ([ .\t-])* day [,.stndrh\t ]*;
950datenoyearrev    = day ([ .\t-])* monthtext;
951datenocolon      = year4 monthlz daylz;
952
953/* Special formats */
954soap             = year4 "-" monthlz "-" daylz "T" hour24lz ":" minutelz ":" secondlz frac tzcorrection?;
955xmlrpc           = year4 monthlz daylz "T" hour24 ":" minutelz ":" secondlz;
956xmlrpcnocolon    = year4 monthlz daylz 't' hour24 minutelz secondlz;
957wddx             = year4 "-" month "-" day "T" hour24 ":" minute ":" second;
958pgydotd          = year4 "."? dayofyear;
959pgtextshort      = monthabbr "-" daylz "-" year;
960pgtextreverse    = year "-" monthabbr "-" daylz;
961mssqltime        = hour12 ":" minutelz ":" secondlz [:.] [0-9]+ meridian;
962isoweekday       = year4 "-"? "W" weekofyear "-"? [0-7];
963isoweek          = year4 "-"? "W" weekofyear;
964exif             = year4 ":" monthlz ":" daylz " " hour24lz ":" minutelz ":" secondlz;
965firstdayof       = 'first day of'?;
966lastdayof        = 'last day of'?;
967backof           = 'back of ' hour24 space? meridian?;
968frontof          = 'front of ' hour24 space? meridian?;
969
970/* Common Log Format: 10/Oct/2000:13:55:36 -0700 */
971clf              = day "/" monthabbr "/" year4 ":" hour24lz ":" minutelz ":" secondlz space tzcorrection;
972
973/* Timestamp format: @1126396800 */
974timestamp        = "@" "-"? [0-9]+;
975
976/* To fix some ambiguities */
977dateshortwithtimeshort12  = datenoyear timeshort12;
978dateshortwithtimelong12   = datenoyear timelong12;
979dateshortwithtimeshort  = datenoyear timeshort24;
980dateshortwithtimelong   = datenoyear timelong24;
981dateshortwithtimelongtz = datenoyear iso8601normtz;
982
983/*
984 * Relative regexps
985 */
986reltextnumber = 'first'|'second'|'third'|'fourth'|'fifth'|'sixth'|'seventh'|'eight'|'eighth'|'ninth'|'tenth'|'eleventh'|'twelfth';
987reltexttext = 'next'|'last'|'previous'|'this';
988reltextunit = (('sec'|'second'|'min'|'minute'|'hour'|'day'|'fortnight'|'forthnight'|'month'|'year') 's'?) | 'weeks' | daytext;
989
990relnumber = ([+-]*[ \t]*[0-9]+);
991relative = relnumber space? (reltextunit | 'week' );
992relativetext = (reltextnumber|reltexttext) space reltextunit;
993relativetextweek = reltexttext space 'week';
994
995weekdayof        = (reltextnumber|reltexttext) space (dayfull|dayabbr) space 'of';
996
997*/
998
999/*!re2c
1000    /* so that vim highlights correctly */
1001    'yesterday'
1002    {
1003        DEBUG_OUTPUT("yesterday");
1004        TIMELIB_INIT;
1005        TIMELIB_HAVE_RELATIVE();
1006        TIMELIB_UNHAVE_TIME();
1007
1008        s->time->relative.d = -1;
1009        TIMELIB_DEINIT;
1010        return TIMELIB_RELATIVE;
1011    }
1012
1013    'now'
1014    {
1015        DEBUG_OUTPUT("now");
1016        TIMELIB_INIT;
1017
1018        TIMELIB_DEINIT;
1019        return TIMELIB_RELATIVE;
1020    }
1021
1022    'noon'
1023    {
1024        DEBUG_OUTPUT("noon");
1025        TIMELIB_INIT;
1026        TIMELIB_UNHAVE_TIME();
1027        TIMELIB_HAVE_TIME();
1028        s->time->h = 12;
1029
1030        TIMELIB_DEINIT;
1031        return TIMELIB_RELATIVE;
1032    }
1033
1034    'midnight' | 'today'
1035    {
1036        DEBUG_OUTPUT("midnight | today");
1037        TIMELIB_INIT;
1038        TIMELIB_UNHAVE_TIME();
1039
1040        TIMELIB_DEINIT;
1041        return TIMELIB_RELATIVE;
1042    }
1043
1044    'tomorrow'
1045    {
1046        DEBUG_OUTPUT("tomorrow");
1047        TIMELIB_INIT;
1048        TIMELIB_HAVE_RELATIVE();
1049        TIMELIB_UNHAVE_TIME();
1050
1051        s->time->relative.d = 1;
1052        TIMELIB_DEINIT;
1053        return TIMELIB_RELATIVE;
1054    }
1055
1056    timestamp
1057    {
1058        timelib_ull i;
1059
1060        TIMELIB_INIT;
1061        TIMELIB_HAVE_RELATIVE();
1062        TIMELIB_UNHAVE_DATE();
1063        TIMELIB_UNHAVE_TIME();
1064        TIMELIB_HAVE_TZ();
1065
1066        i = timelib_get_unsigned_nr((char **) &ptr, 24);
1067        s->time->y = 1970;
1068        s->time->m = 1;
1069        s->time->d = 1;
1070        s->time->h = s->time->i = s->time->s = 0;
1071        s->time->f = 0.0;
1072        s->time->relative.s += i;
1073        s->time->is_localtime = 1;
1074        s->time->zone_type = TIMELIB_ZONETYPE_OFFSET;
1075        s->time->z = 0;
1076        s->time->dst = 0;
1077
1078        TIMELIB_DEINIT;
1079        return TIMELIB_RELATIVE;
1080    }
1081
1082    firstdayof | lastdayof
1083    {
1084        DEBUG_OUTPUT("firstdayof | lastdayof");
1085        TIMELIB_INIT;
1086        TIMELIB_HAVE_RELATIVE();
1087
1088        /* skip "last day of" or "first day of" */
1089        if (*ptr == 'l') {
1090            s->time->relative.first_last_day_of = 2;
1091        } else {
1092            s->time->relative.first_last_day_of = 1;
1093        }
1094
1095        TIMELIB_DEINIT;
1096        return TIMELIB_LF_DAY_OF_MONTH;
1097    }
1098
1099    backof | frontof
1100    {
1101        DEBUG_OUTPUT("backof | frontof");
1102        TIMELIB_INIT;
1103        TIMELIB_UNHAVE_TIME();
1104        TIMELIB_HAVE_TIME();
1105
1106        if (*ptr == 'b') {
1107            s->time->h = timelib_get_nr((char **) &ptr, 2);
1108            s->time->i = 15;
1109        } else {
1110            s->time->h = timelib_get_nr((char **) &ptr, 2) - 1;
1111            s->time->i = 45;
1112        }
1113        if (*ptr != '\0' ) {
1114            timelib_eat_spaces((char **) &ptr);
1115            s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1116        }
1117
1118        TIMELIB_DEINIT;
1119        return TIMELIB_LF_DAY_OF_MONTH;
1120    }
1121
1122    weekdayof
1123    {
1124        timelib_sll i;
1125        int         behavior = 0;
1126        DEBUG_OUTPUT("weekdayof");
1127        TIMELIB_INIT;
1128        TIMELIB_HAVE_RELATIVE();
1129        TIMELIB_HAVE_SPECIAL_RELATIVE();
1130
1131        i = timelib_get_relative_text((char **) &ptr, &behavior);
1132        timelib_eat_spaces((char **) &ptr);
1133        if (i > 0) { /* first, second... etc */
1134            s->time->relative.special.type = TIMELIB_SPECIAL_DAY_OF_WEEK_IN_MONTH;
1135            timelib_set_relative((char **) &ptr, i, 1, s);
1136        } else { /* last */
1137            s->time->relative.special.type = TIMELIB_SPECIAL_LAST_DAY_OF_WEEK_IN_MONTH;
1138            timelib_set_relative((char **) &ptr, i, behavior, s);
1139        }
1140        TIMELIB_DEINIT;
1141        return TIMELIB_WEEK_DAY_OF_MONTH;
1142    }
1143
1144    timetiny12 | timeshort12 | timelong12
1145    {
1146        DEBUG_OUTPUT("timetiny12 | timeshort12 | timelong12");
1147        TIMELIB_INIT;
1148        TIMELIB_HAVE_TIME();
1149        s->time->h = timelib_get_nr((char **) &ptr, 2);
1150        if (*ptr == ':' || *ptr == '.') {
1151            s->time->i = timelib_get_nr((char **) &ptr, 2);
1152            if (*ptr == ':' || *ptr == '.') {
1153                s->time->s = timelib_get_nr((char **) &ptr, 2);
1154            }
1155        }
1156        s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1157        TIMELIB_DEINIT;
1158        return TIMELIB_TIME12;
1159    }
1160
1161    mssqltime
1162    {
1163        DEBUG_OUTPUT("mssqltime");
1164        TIMELIB_INIT;
1165        TIMELIB_HAVE_TIME();
1166        s->time->h = timelib_get_nr((char **) &ptr, 2);
1167        s->time->i = timelib_get_nr((char **) &ptr, 2);
1168        if (*ptr == ':' || *ptr == '.') {
1169            s->time->s = timelib_get_nr((char **) &ptr, 2);
1170
1171            if (*ptr == ':' || *ptr == '.') {
1172                s->time->f = timelib_get_frac_nr((char **) &ptr, 8);
1173            }
1174        }
1175        timelib_eat_spaces((char **) &ptr);
1176        s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1177        TIMELIB_DEINIT;
1178        return TIMELIB_TIME24_WITH_ZONE;
1179    }
1180
1181    timeshort24 | timelong24 /* | iso8601short | iso8601norm */ | iso8601long /*| iso8601shorttz | iso8601normtz | iso8601longtz*/
1182    {
1183        int tz_not_found;
1184        DEBUG_OUTPUT("timeshort24 | timelong24 | iso8601long");
1185        TIMELIB_INIT;
1186        TIMELIB_HAVE_TIME();
1187        s->time->h = timelib_get_nr((char **) &ptr, 2);
1188        s->time->i = timelib_get_nr((char **) &ptr, 2);
1189        if (*ptr == ':' || *ptr == '.') {
1190            s->time->s = timelib_get_nr((char **) &ptr, 2);
1191
1192            if (*ptr == '.') {
1193                s->time->f = timelib_get_frac_nr((char **) &ptr, 8);
1194            }
1195        }
1196
1197        if (*ptr != '\0') {
1198            s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1199            if (tz_not_found) {
1200                add_error(s, "The timezone could not be found in the database");
1201            }
1202        }
1203        TIMELIB_DEINIT;
1204        return TIMELIB_TIME24_WITH_ZONE;
1205    }
1206
1207    gnunocolon
1208    {
1209        DEBUG_OUTPUT("gnunocolon");
1210        TIMELIB_INIT;
1211        switch (s->time->have_time) {
1212            case 0:
1213                s->time->h = timelib_get_nr((char **) &ptr, 2);
1214                s->time->i = timelib_get_nr((char **) &ptr, 2);
1215                s->time->s = 0;
1216                break;
1217            case 1:
1218                s->time->y = timelib_get_nr((char **) &ptr, 4);
1219                break;
1220            default:
1221                TIMELIB_DEINIT;
1222                add_error(s, "Double time specification");
1223                return TIMELIB_ERROR;
1224        }
1225        s->time->have_time++;
1226        TIMELIB_DEINIT;
1227        return TIMELIB_GNU_NOCOLON;
1228    }
1229/*
1230    gnunocolontz
1231    {
1232        DEBUG_OUTPUT("gnunocolontz");
1233        TIMELIB_INIT;
1234        switch (s->time->have_time) {
1235            case 0:
1236                s->time->h = timelib_get_nr((char **) &ptr, 2);
1237                s->time->i = timelib_get_nr((char **) &ptr, 2);
1238                s->time->s = 0;
1239                s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, s->tzdb, tz_get_wrapper);
1240                break;
1241            case 1:
1242                s->time->y = timelib_get_nr((char **) &ptr, 4);
1243                break;
1244            default:
1245                TIMELIB_DEINIT;
1246                return TIMELIB_ERROR;
1247        }
1248        s->time->have_time++;
1249        TIMELIB_DEINIT;
1250        return TIMELIB_GNU_NOCOLON_TZ;
1251    }
1252*/
1253    iso8601nocolon /*| iso8601nocolontz*/
1254    {
1255        int tz_not_found;
1256        DEBUG_OUTPUT("iso8601nocolon");
1257        TIMELIB_INIT;
1258        TIMELIB_HAVE_TIME();
1259        s->time->h = timelib_get_nr((char **) &ptr, 2);
1260        s->time->i = timelib_get_nr((char **) &ptr, 2);
1261        s->time->s = timelib_get_nr((char **) &ptr, 2);
1262
1263        if (*ptr != '\0') {
1264            s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1265            if (tz_not_found) {
1266                add_error(s, "The timezone could not be found in the database");
1267            }
1268        }
1269        TIMELIB_DEINIT;
1270        return TIMELIB_ISO_NOCOLON;
1271    }
1272
1273    americanshort | american
1274    {
1275        int length = 0;
1276        DEBUG_OUTPUT("americanshort | american");
1277        TIMELIB_INIT;
1278        TIMELIB_HAVE_DATE();
1279        s->time->m = timelib_get_nr((char **) &ptr, 2);
1280        s->time->d = timelib_get_nr((char **) &ptr, 2);
1281        if (*ptr == '/') {
1282            s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1283            TIMELIB_PROCESS_YEAR(s->time->y, length);
1284        }
1285        TIMELIB_DEINIT;
1286        return TIMELIB_AMERICAN;
1287    }
1288
1289    iso8601date4 | iso8601dateslash | dateslash
1290    {
1291        DEBUG_OUTPUT("iso8601date4 | iso8601date2 | iso8601dateslash | dateslash");
1292        TIMELIB_INIT;
1293        TIMELIB_HAVE_DATE();
1294        s->time->y = timelib_get_unsigned_nr((char **) &ptr, 4);
1295        s->time->m = timelib_get_nr((char **) &ptr, 2);
1296        s->time->d = timelib_get_nr((char **) &ptr, 2);
1297        TIMELIB_DEINIT;
1298        return TIMELIB_ISO_DATE;
1299    }
1300
1301    iso8601date2
1302    {
1303        int length = 0;
1304        DEBUG_OUTPUT("iso8601date2");
1305        TIMELIB_INIT;
1306        TIMELIB_HAVE_DATE();
1307        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1308        s->time->m = timelib_get_nr((char **) &ptr, 2);
1309        s->time->d = timelib_get_nr((char **) &ptr, 2);
1310        TIMELIB_PROCESS_YEAR(s->time->y, length);
1311        TIMELIB_DEINIT;
1312        return TIMELIB_ISO_DATE;
1313    }
1314
1315    gnudateshorter
1316    {
1317        int length = 0;
1318        DEBUG_OUTPUT("gnudateshorter");
1319        TIMELIB_INIT;
1320        TIMELIB_HAVE_DATE();
1321        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1322        s->time->m = timelib_get_nr((char **) &ptr, 2);
1323        s->time->d = 1;
1324        TIMELIB_PROCESS_YEAR(s->time->y, length);
1325        TIMELIB_DEINIT;
1326        return TIMELIB_ISO_DATE;
1327    }
1328
1329    gnudateshort
1330    {
1331        int length = 0;
1332        DEBUG_OUTPUT("gnudateshort");
1333        TIMELIB_INIT;
1334        TIMELIB_HAVE_DATE();
1335        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1336        s->time->m = timelib_get_nr((char **) &ptr, 2);
1337        s->time->d = timelib_get_nr((char **) &ptr, 2);
1338        TIMELIB_PROCESS_YEAR(s->time->y, length);
1339        TIMELIB_DEINIT;
1340        return TIMELIB_ISO_DATE;
1341    }
1342
1343    datefull
1344    {
1345        int length = 0;
1346        DEBUG_OUTPUT("datefull");
1347        TIMELIB_INIT;
1348        TIMELIB_HAVE_DATE();
1349        s->time->d = timelib_get_nr((char **) &ptr, 2);
1350        timelib_skip_day_suffix((char **) &ptr);
1351        s->time->m = timelib_get_month((char **) &ptr);
1352        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1353        TIMELIB_PROCESS_YEAR(s->time->y, length);
1354        TIMELIB_DEINIT;
1355        return TIMELIB_DATE_FULL;
1356    }
1357
1358    pointeddate4
1359    {
1360        DEBUG_OUTPUT("pointed date YYYY");
1361        TIMELIB_INIT;
1362        TIMELIB_HAVE_DATE();
1363        s->time->d = timelib_get_nr((char **) &ptr, 2);
1364        s->time->m = timelib_get_nr((char **) &ptr, 2);
1365        s->time->y = timelib_get_nr((char **) &ptr, 4);
1366        TIMELIB_DEINIT;
1367        return TIMELIB_DATE_FULL_POINTED;
1368    }
1369
1370    pointeddate2
1371    {
1372        int length = 0;
1373        DEBUG_OUTPUT("pointed date YY");
1374        TIMELIB_INIT;
1375        TIMELIB_HAVE_DATE();
1376        s->time->d = timelib_get_nr((char **) &ptr, 2);
1377        s->time->m = timelib_get_nr((char **) &ptr, 2);
1378        s->time->y = timelib_get_nr_ex((char **) &ptr, 2, &length);
1379        TIMELIB_PROCESS_YEAR(s->time->y, length);
1380        TIMELIB_DEINIT;
1381        return TIMELIB_DATE_FULL_POINTED;
1382    }
1383
1384    datenoday
1385    {
1386        int length = 0;
1387        DEBUG_OUTPUT("datenoday");
1388        TIMELIB_INIT;
1389        TIMELIB_HAVE_DATE();
1390        s->time->m = timelib_get_month((char **) &ptr);
1391        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1392        s->time->d = 1;
1393        TIMELIB_PROCESS_YEAR(s->time->y, length);
1394        TIMELIB_DEINIT;
1395        return TIMELIB_DATE_NO_DAY;
1396    }
1397
1398    datenodayrev
1399    {
1400        int length = 0;
1401        DEBUG_OUTPUT("datenodayrev");
1402        TIMELIB_INIT;
1403        TIMELIB_HAVE_DATE();
1404        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1405        s->time->m = timelib_get_month((char **) &ptr);
1406        s->time->d = 1;
1407        TIMELIB_PROCESS_YEAR(s->time->y, length);
1408        TIMELIB_DEINIT;
1409        return TIMELIB_DATE_NO_DAY;
1410    }
1411
1412    datetextual | datenoyear
1413    {
1414        int length = 0;
1415        DEBUG_OUTPUT("datetextual | datenoyear");
1416        TIMELIB_INIT;
1417        TIMELIB_HAVE_DATE();
1418        s->time->m = timelib_get_month((char **) &ptr);
1419        s->time->d = timelib_get_nr((char **) &ptr, 2);
1420        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1421        TIMELIB_PROCESS_YEAR(s->time->y, length);
1422        TIMELIB_DEINIT;
1423        return TIMELIB_DATE_TEXT;
1424    }
1425
1426    datenoyearrev
1427    {
1428        DEBUG_OUTPUT("datenoyearrev");
1429        TIMELIB_INIT;
1430        TIMELIB_HAVE_DATE();
1431        s->time->d = timelib_get_nr((char **) &ptr, 2);
1432        timelib_skip_day_suffix((char **) &ptr);
1433        s->time->m = timelib_get_month((char **) &ptr);
1434        TIMELIB_DEINIT;
1435        return TIMELIB_DATE_TEXT;
1436    }
1437
1438    datenocolon
1439    {
1440        DEBUG_OUTPUT("datenocolon");
1441        TIMELIB_INIT;
1442        TIMELIB_HAVE_DATE();
1443        s->time->y = timelib_get_nr((char **) &ptr, 4);
1444        s->time->m = timelib_get_nr((char **) &ptr, 2);
1445        s->time->d = timelib_get_nr((char **) &ptr, 2);
1446        TIMELIB_DEINIT;
1447        return TIMELIB_DATE_NOCOLON;
1448    }
1449
1450    xmlrpc | xmlrpcnocolon | soap | wddx | exif
1451    {
1452        int tz_not_found;
1453        DEBUG_OUTPUT("xmlrpc | xmlrpcnocolon | soap | wddx | exif");
1454        TIMELIB_INIT;
1455        TIMELIB_HAVE_TIME();
1456        TIMELIB_HAVE_DATE();
1457        s->time->y = timelib_get_nr((char **) &ptr, 4);
1458        s->time->m = timelib_get_nr((char **) &ptr, 2);
1459        s->time->d = timelib_get_nr((char **) &ptr, 2);
1460        s->time->h = timelib_get_nr((char **) &ptr, 2);
1461        s->time->i = timelib_get_nr((char **) &ptr, 2);
1462        s->time->s = timelib_get_nr((char **) &ptr, 2);
1463        if (*ptr == '.') {
1464            s->time->f = timelib_get_frac_nr((char **) &ptr, 9);
1465            if (*ptr) { /* timezone is optional */
1466                s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1467                if (tz_not_found) {
1468                    add_error(s, "The timezone could not be found in the database");
1469                }
1470            }
1471        }
1472        TIMELIB_DEINIT;
1473        return TIMELIB_XMLRPC_SOAP;
1474    }
1475
1476    pgydotd
1477    {
1478        int length = 0;
1479        DEBUG_OUTPUT("pgydotd");
1480        TIMELIB_INIT;
1481        TIMELIB_HAVE_DATE();
1482        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1483        s->time->d = timelib_get_nr((char **) &ptr, 3);
1484        s->time->m = 1;
1485        TIMELIB_PROCESS_YEAR(s->time->y, length);
1486        TIMELIB_DEINIT;
1487        return TIMELIB_PG_YEARDAY;
1488    }
1489
1490    isoweekday
1491    {
1492        timelib_sll w, d;
1493        DEBUG_OUTPUT("isoweekday");
1494        TIMELIB_INIT;
1495        TIMELIB_HAVE_DATE();
1496        TIMELIB_HAVE_RELATIVE();
1497
1498        s->time->y = timelib_get_nr((char **) &ptr, 4);
1499        w = timelib_get_nr((char **) &ptr, 2);
1500        d = timelib_get_nr((char **) &ptr, 1);
1501        s->time->m = 1;
1502        s->time->d = 1;
1503        s->time->relative.d = timelib_daynr_from_weeknr(s->time->y, w, d);
1504
1505        TIMELIB_DEINIT;
1506        return TIMELIB_ISO_WEEK;
1507    }
1508
1509    isoweek
1510    {
1511        timelib_sll w, d;
1512        DEBUG_OUTPUT("isoweek");
1513        TIMELIB_INIT;
1514        TIMELIB_HAVE_DATE();
1515        TIMELIB_HAVE_RELATIVE();
1516
1517        s->time->y = timelib_get_nr((char **) &ptr, 4);
1518        w = timelib_get_nr((char **) &ptr, 2);
1519        d = 1;
1520        s->time->m = 1;
1521        s->time->d = 1;
1522        s->time->relative.d = timelib_daynr_from_weeknr(s->time->y, w, d);
1523
1524        TIMELIB_DEINIT;
1525        return TIMELIB_ISO_WEEK;
1526    }
1527
1528    pgtextshort
1529    {
1530        int length = 0;
1531        DEBUG_OUTPUT("pgtextshort");
1532        TIMELIB_INIT;
1533        TIMELIB_HAVE_DATE();
1534        s->time->m = timelib_get_month((char **) &ptr);
1535        s->time->d = timelib_get_nr((char **) &ptr, 2);
1536        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1537        TIMELIB_PROCESS_YEAR(s->time->y, length);
1538        TIMELIB_DEINIT;
1539        return TIMELIB_PG_TEXT;
1540    }
1541
1542    pgtextreverse
1543    {
1544        int length = 0;
1545        DEBUG_OUTPUT("pgtextreverse");
1546        TIMELIB_INIT;
1547        TIMELIB_HAVE_DATE();
1548        s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1549        s->time->m = timelib_get_month((char **) &ptr);
1550        s->time->d = timelib_get_nr((char **) &ptr, 2);
1551        TIMELIB_PROCESS_YEAR(s->time->y, length);
1552        TIMELIB_DEINIT;
1553        return TIMELIB_PG_TEXT;
1554    }
1555
1556    clf
1557    {
1558        int tz_not_found;
1559        DEBUG_OUTPUT("clf");
1560        TIMELIB_INIT;
1561        TIMELIB_HAVE_TIME();
1562        TIMELIB_HAVE_DATE();
1563        s->time->d = timelib_get_nr((char **) &ptr, 2);
1564        s->time->m = timelib_get_month((char **) &ptr);
1565        s->time->y = timelib_get_nr((char **) &ptr, 4);
1566        s->time->h = timelib_get_nr((char **) &ptr, 2);
1567        s->time->i = timelib_get_nr((char **) &ptr, 2);
1568        s->time->s = timelib_get_nr((char **) &ptr, 2);
1569        s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1570        if (tz_not_found) {
1571            add_error(s, "The timezone could not be found in the database");
1572        }
1573        TIMELIB_DEINIT;
1574        return TIMELIB_CLF;
1575    }
1576
1577    year4
1578    {
1579        DEBUG_OUTPUT("year4");
1580        TIMELIB_INIT;
1581        s->time->y = timelib_get_nr((char **) &ptr, 4);
1582        TIMELIB_DEINIT;
1583        return TIMELIB_CLF;
1584    }
1585
1586    ago
1587    {
1588        DEBUG_OUTPUT("ago");
1589        TIMELIB_INIT;
1590        s->time->relative.y = 0 - s->time->relative.y;
1591        s->time->relative.m = 0 - s->time->relative.m;
1592        s->time->relative.d = 0 - s->time->relative.d;
1593        s->time->relative.h = 0 - s->time->relative.h;
1594        s->time->relative.i = 0 - s->time->relative.i;
1595        s->time->relative.s = 0 - s->time->relative.s;
1596        s->time->relative.weekday = 0 - s->time->relative.weekday;
1597        if (s->time->relative.weekday == 0) {
1598            s->time->relative.weekday = -7;
1599        }
1600        if (s->time->relative.have_special_relative && s->time->relative.special.type == TIMELIB_SPECIAL_WEEKDAY) {
1601            s->time->relative.special.amount = 0 - s->time->relative.special.amount;
1602        }
1603        TIMELIB_DEINIT;
1604        return TIMELIB_AGO;
1605    }
1606
1607    daytext
1608    {
1609        const timelib_relunit* relunit;
1610        DEBUG_OUTPUT("daytext");
1611        TIMELIB_INIT;
1612        TIMELIB_HAVE_RELATIVE();
1613        TIMELIB_HAVE_WEEKDAY_RELATIVE();
1614        TIMELIB_UNHAVE_TIME();
1615        relunit = timelib_lookup_relunit((char**) &ptr);
1616        s->time->relative.weekday = relunit->multiplier;
1617        if (s->time->relative.weekday_behavior != 2) {
1618            s->time->relative.weekday_behavior = 1;
1619        }
1620
1621        TIMELIB_DEINIT;
1622        return TIMELIB_WEEKDAY;
1623    }
1624
1625    relativetextweek
1626    {
1627        timelib_sll i;
1628        int         behavior = 0;
1629        DEBUG_OUTPUT("relativetextweek");
1630        TIMELIB_INIT;
1631        TIMELIB_HAVE_RELATIVE();
1632
1633        while(*ptr) {
1634            i = timelib_get_relative_text((char **) &ptr, &behavior);
1635            timelib_eat_spaces((char **) &ptr);
1636            timelib_set_relative((char **) &ptr, i, behavior, s);
1637            s->time->relative.weekday_behavior = 2;
1638
1639            /* to handle the format weekday + last/this/next week */
1640            if (s->time->relative.have_weekday_relative == 0) {
1641                TIMELIB_HAVE_WEEKDAY_RELATIVE();
1642                s->time->relative.weekday = 1;
1643            }
1644        }
1645        TIMELIB_DEINIT;
1646        return TIMELIB_RELATIVE;
1647    }
1648
1649    relativetext
1650    {
1651        timelib_sll i;
1652        int         behavior = 0;
1653        DEBUG_OUTPUT("relativetext");
1654        TIMELIB_INIT;
1655        TIMELIB_HAVE_RELATIVE();
1656
1657        while(*ptr) {
1658            i = timelib_get_relative_text((char **) &ptr, &behavior);
1659            timelib_eat_spaces((char **) &ptr);
1660            timelib_set_relative((char **) &ptr, i, behavior, s);
1661        }
1662        TIMELIB_DEINIT;
1663        return TIMELIB_RELATIVE;
1664    }
1665
1666    monthfull | monthabbr
1667    {
1668        DEBUG_OUTPUT("monthtext");
1669        TIMELIB_INIT;
1670        TIMELIB_HAVE_DATE();
1671        s->time->m = timelib_lookup_month((char **) &ptr);
1672        TIMELIB_DEINIT;
1673        return TIMELIB_DATE_TEXT;
1674    }
1675
1676    tzcorrection | tz
1677    {
1678        int tz_not_found;
1679        DEBUG_OUTPUT("tzcorrection | tz");
1680        TIMELIB_INIT;
1681        TIMELIB_HAVE_TZ();
1682        s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1683        if (tz_not_found) {
1684            add_error(s, "The timezone could not be found in the database");
1685        }
1686        TIMELIB_DEINIT;
1687        return TIMELIB_TIMEZONE;
1688    }
1689
1690    dateshortwithtimeshort12 | dateshortwithtimelong12
1691    {
1692        DEBUG_OUTPUT("dateshortwithtimeshort12 | dateshortwithtimelong12");
1693        TIMELIB_INIT;
1694        TIMELIB_HAVE_DATE();
1695        s->time->m = timelib_get_month((char **) &ptr);
1696        s->time->d = timelib_get_nr((char **) &ptr, 2);
1697
1698        TIMELIB_HAVE_TIME();
1699        s->time->h = timelib_get_nr((char **) &ptr, 2);
1700        s->time->i = timelib_get_nr((char **) &ptr, 2);
1701        if (*ptr == ':' || *ptr == '.') {
1702            s->time->s = timelib_get_nr((char **) &ptr, 2);
1703
1704            if (*ptr == '.') {
1705                s->time->f = timelib_get_frac_nr((char **) &ptr, 8);
1706            }
1707        }
1708
1709        s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1710        TIMELIB_DEINIT;
1711        return TIMELIB_SHORTDATE_WITH_TIME;
1712    }
1713
1714    dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz
1715    {
1716        int tz_not_found;
1717        DEBUG_OUTPUT("dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz");
1718        TIMELIB_INIT;
1719        TIMELIB_HAVE_DATE();
1720        s->time->m = timelib_get_month((char **) &ptr);
1721        s->time->d = timelib_get_nr((char **) &ptr, 2);
1722
1723        TIMELIB_HAVE_TIME();
1724        s->time->h = timelib_get_nr((char **) &ptr, 2);
1725        s->time->i = timelib_get_nr((char **) &ptr, 2);
1726        if (*ptr == ':') {
1727            s->time->s = timelib_get_nr((char **) &ptr, 2);
1728
1729            if (*ptr == '.') {
1730                s->time->f = timelib_get_frac_nr((char **) &ptr, 8);
1731            }
1732        }
1733
1734        if (*ptr != '\0') {
1735            s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1736            if (tz_not_found) {
1737                add_error(s, "The timezone could not be found in the database");
1738            }
1739        }
1740        TIMELIB_DEINIT;
1741        return TIMELIB_SHORTDATE_WITH_TIME;
1742    }
1743
1744    relative
1745    {
1746        timelib_ull i;
1747        DEBUG_OUTPUT("relative");
1748        TIMELIB_INIT;
1749        TIMELIB_HAVE_RELATIVE();
1750
1751        while(*ptr) {
1752            i = timelib_get_unsigned_nr((char **) &ptr, 24);
1753            timelib_eat_spaces((char **) &ptr);
1754            timelib_set_relative((char **) &ptr, i, 1, s);
1755        }
1756        TIMELIB_DEINIT;
1757        return TIMELIB_RELATIVE;
1758    }
1759
1760    [ .,\t]
1761    {
1762        goto std;
1763    }
1764
1765    "\000"|"\n"
1766    {
1767        s->pos = cursor; s->line++;
1768        goto std;
1769    }
1770
1771    any
1772    {
1773        add_error(s, "Unexpected character");
1774        goto std;
1775    }
1776*/
1777}
1778
1779/*!max:re2c */
1780
1781timelib_time* timelib_strtotime(char *s, int len, struct timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper)
1782{
1783    Scanner in;
1784    int t;
1785    char *e = s + len - 1;
1786
1787    memset(&in, 0, sizeof(in));
1788    in.errors = malloc(sizeof(struct timelib_error_container));
1789    in.errors->warning_count = 0;
1790    in.errors->warning_messages = NULL;
1791    in.errors->error_count = 0;
1792    in.errors->error_messages = NULL;
1793
1794    if (len > 0) {
1795        while (isspace(*s) && s < e) {
1796            s++;
1797        }
1798        while (isspace(*e) && e > s) {
1799            e--;
1800        }
1801    }
1802    if (e - s < 0) {
1803        in.time = timelib_time_ctor();
1804        add_error(&in, "Empty string");
1805        if (errors) {
1806            *errors = in.errors;
1807        } else {
1808            timelib_error_container_dtor(in.errors);
1809        }
1810        in.time->y = in.time->d = in.time->m = in.time->h = in.time->i = in.time->s = in.time->f = in.time->dst = in.time->z = TIMELIB_UNSET;
1811        in.time->is_localtime = in.time->zone_type = 0;
1812        return in.time;
1813    }
1814    e++;
1815
1816    in.str = malloc((e - s) + YYMAXFILL);
1817    memset(in.str, 0, (e - s) + YYMAXFILL);
1818    memcpy(in.str, s, (e - s));
1819    in.lim = in.str + (e - s) + YYMAXFILL;
1820    in.cur = in.str;
1821    in.time = timelib_time_ctor();
1822    in.time->y = TIMELIB_UNSET;
1823    in.time->d = TIMELIB_UNSET;
1824    in.time->m = TIMELIB_UNSET;
1825    in.time->h = TIMELIB_UNSET;
1826    in.time->i = TIMELIB_UNSET;
1827    in.time->s = TIMELIB_UNSET;
1828    in.time->f = TIMELIB_UNSET;
1829    in.time->z = TIMELIB_UNSET;
1830    in.time->dst = TIMELIB_UNSET;
1831    in.tzdb = tzdb;
1832    in.time->is_localtime = 0;
1833    in.time->zone_type = 0;
1834
1835    do {
1836        t = scan(&in, tz_get_wrapper);
1837#ifdef DEBUG_PARSER
1838        printf("%d\n", t);
1839#endif
1840    } while(t != EOI);
1841
1842    /* do funky checking whether the parsed time was valid time */
1843    if (in.time->have_time && !timelib_valid_time( in.time->h, in.time->i, in.time->s)) {
1844        add_warning(&in, "The parsed time was invalid");
1845    }
1846    /* do funky checking whether the parsed date was valid date */
1847    if (in.time->have_date && !timelib_valid_date( in.time->y, in.time->m, in.time->d)) {
1848        add_warning(&in, "The parsed date was invalid");
1849    }
1850
1851    free(in.str);
1852    if (errors) {
1853        *errors = in.errors;
1854    } else {
1855        timelib_error_container_dtor(in.errors);
1856    }
1857    return in.time;
1858}
1859
1860#define TIMELIB_CHECK_NUMBER                                           \
1861        if (strchr("0123456789", *ptr) == NULL)                        \
1862        {                                                              \
1863            add_pbf_error(s, "Unexpected data found.", string, begin); \
1864        }
1865
1866static void timelib_time_reset_fields(timelib_time *time)
1867{
1868    assert(time != NULL);
1869
1870    time->y = 1970;
1871    time->m = 1;
1872    time->d = 1;
1873    time->h = time->i = time->s = 0;
1874    time->f = 0.0;
1875    time->tz_info = NULL;
1876}
1877
1878static void timelib_time_reset_unset_fields(timelib_time *time)
1879{
1880    assert(time != NULL);
1881
1882    if (time->y == TIMELIB_UNSET ) time->y = 1970;
1883    if (time->m == TIMELIB_UNSET ) time->m = 1;
1884    if (time->d == TIMELIB_UNSET ) time->d = 1;
1885    if (time->h == TIMELIB_UNSET ) time->h = 0;
1886    if (time->i == TIMELIB_UNSET ) time->i = 0;
1887    if (time->s == TIMELIB_UNSET ) time->s = 0;
1888    if (time->f == TIMELIB_UNSET ) time->f = 0.0;
1889}
1890
1891timelib_time *timelib_parse_from_format(char *format, char *string, int len, timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper)
1892{
1893    char       *fptr = format;
1894    char       *ptr = string;
1895    char       *begin;
1896    timelib_sll tmp;
1897    Scanner in;
1898    Scanner *s = &in;
1899    int allow_extra = 0;
1900
1901    memset(&in, 0, sizeof(in));
1902    in.errors = malloc(sizeof(struct timelib_error_container));
1903    in.errors->warning_count = 0;
1904    in.errors->warning_messages = NULL;
1905    in.errors->error_count = 0;
1906    in.errors->error_messages = NULL;
1907
1908    in.time = timelib_time_ctor();
1909    in.time->y = TIMELIB_UNSET;
1910    in.time->d = TIMELIB_UNSET;
1911    in.time->m = TIMELIB_UNSET;
1912    in.time->h = TIMELIB_UNSET;
1913    in.time->i = TIMELIB_UNSET;
1914    in.time->s = TIMELIB_UNSET;
1915    in.time->f = TIMELIB_UNSET;
1916    in.time->z = TIMELIB_UNSET;
1917    in.time->dst = TIMELIB_UNSET;
1918    in.tzdb = tzdb;
1919    in.time->is_localtime = 0;
1920    in.time->zone_type = 0;
1921
1922    /* Loop over the format string */
1923    while (*fptr && *ptr) {
1924        begin = ptr;
1925        switch (*fptr) {
1926            case 'D': /* three letter day */
1927            case 'l': /* full day */
1928                {
1929                    const timelib_relunit* tmprel = 0;
1930
1931                    tmprel = timelib_lookup_relunit((char **) &ptr);
1932                    if (!tmprel) {
1933                        add_pbf_error(s, "A textual day could not be found", string, begin);
1934                        break;
1935                    } else {
1936                        in.time->have_relative = 1;
1937                        in.time->relative.have_weekday_relative = 1;
1938                        in.time->relative.weekday = tmprel->multiplier;
1939                        in.time->relative.weekday_behavior = 1;
1940                    }
1941                }
1942                break;
1943            case 'd': /* two digit day, with leading zero */
1944            case 'j': /* two digit day, without leading zero */
1945                TIMELIB_CHECK_NUMBER;
1946                if ((s->time->d = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) {
1947                    add_pbf_error(s, "A two digit day could not be found", string, begin);
1948                }
1949                break;
1950            case 'S': /* day suffix, ignored, nor checked */
1951                timelib_skip_day_suffix((char **) &ptr);
1952                break;
1953            case 'z': /* day of year - resets month (0 based) - also initializes everything else to !TIMELIB_UNSET */
1954                TIMELIB_CHECK_NUMBER;
1955                if ((tmp = timelib_get_nr((char **) &ptr, 3)) == TIMELIB_UNSET) {
1956                    add_pbf_error(s, "A three digit day-of-year could not be found", string, begin);
1957                } else {
1958                    s->time->m = 1;
1959                    s->time->d = tmp + 1;
1960                    timelib_do_normalize(s->time);
1961                }
1962                break;
1963
1964            case 'm': /* two digit month, with leading zero */
1965            case 'n': /* two digit month, without leading zero */
1966                TIMELIB_CHECK_NUMBER;
1967                if ((s->time->m = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) {
1968                    add_pbf_error(s, "A two digit month could not be found", string, begin);
1969                }
1970                break;
1971            case 'M': /* three letter month */
1972            case 'F': /* full month */
1973                tmp = timelib_lookup_month((char **) &ptr);
1974                if (!tmp) {
1975                    add_pbf_error(s, "A textual month could not be found", string, begin);
1976                } else {
1977                    s->time->m = tmp;
1978                }
1979                break;
1980            case 'y': /* two digit year */
1981                {
1982                    int length = 0;
1983                    TIMELIB_CHECK_NUMBER;
1984                    if ((s->time->y = timelib_get_nr_ex((char **) &ptr, 2, &length)) == TIMELIB_UNSET) {
1985                        add_pbf_error(s, "A two digit year could not be found", string, begin);
1986                    }
1987                    TIMELIB_PROCESS_YEAR(s->time->y, length);
1988                }
1989                break;
1990            case 'Y': /* four digit year */
1991                TIMELIB_CHECK_NUMBER;
1992                if ((s->time->y = timelib_get_nr((char **) &ptr, 4)) == TIMELIB_UNSET) {
1993                    add_pbf_error(s, "A four digit year could not be found", string, begin);
1994                }
1995                break;
1996            case 'g': /* two digit hour, with leading zero */
1997            case 'h': /* two digit hour, without leading zero */
1998                TIMELIB_CHECK_NUMBER;
1999                if ((s->time->h = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) {
2000                    add_pbf_error(s, "A two digit hour could not be found", string, begin);
2001                }
2002                if (s->time->h > 12) {
2003                    add_pbf_error(s, "Hour can not be higher than 12", string, begin);
2004                }
2005                break;
2006            case 'G': /* two digit hour, with leading zero */
2007            case 'H': /* two digit hour, without leading zero */
2008                TIMELIB_CHECK_NUMBER;
2009                if ((s->time->h = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) {
2010                    add_pbf_error(s, "A two digit hour could not be found", string, begin);
2011                }
2012                break;
2013            case 'a': /* am/pm/a.m./p.m. */
2014            case 'A': /* AM/PM/A.M./P.M. */
2015                if (s->time->h == TIMELIB_UNSET) {
2016                    add_pbf_error(s, "Meridian can only come after an hour has been found", string, begin);
2017                } else if ((tmp = timelib_meridian_with_check((char **) &ptr, s->time->h)) == TIMELIB_UNSET) {
2018                    add_pbf_error(s, "A meridian could not be found", string, begin);
2019                } else {
2020                    s->time->h += tmp;
2021                }
2022                break;
2023            case 'i': /* two digit minute, with leading zero */
2024                {
2025                    int length;
2026                    timelib_sll min;
2027
2028                    TIMELIB_CHECK_NUMBER;
2029                    min = timelib_get_nr_ex((char **) &ptr, 2, &length);
2030                    if (min == TIMELIB_UNSET || length != 2) {
2031                        add_pbf_error(s, "A two digit minute could not be found", string, begin);
2032                    } else {
2033                        s->time->i = min;
2034                    }
2035                }
2036                break;
2037            case 's': /* two digit second, with leading zero */
2038                {
2039                    int length;
2040                    timelib_sll sec;
2041
2042                    TIMELIB_CHECK_NUMBER;
2043                    sec = timelib_get_nr_ex((char **) &ptr, 2, &length);
2044                    if (sec == TIMELIB_UNSET || length != 2) {
2045                        add_pbf_error(s, "A two second minute could not be found", string, begin);
2046                    } else {
2047                        s->time->s = sec;
2048                    }
2049                }
2050                break;
2051            case 'u': /* up to six digit millisecond */
2052                {
2053                    double f;
2054                    char *tptr;
2055
2056                    TIMELIB_CHECK_NUMBER;
2057                    tptr = ptr;
2058                    if ((f = timelib_get_nr((char **) &ptr, 6)) == TIMELIB_UNSET || (ptr - tptr < 1)) {
2059                        add_pbf_error(s, "A six digit millisecond could not be found", string, begin);
2060                    } else {
2061                        s->time->f = (f / pow(10, (ptr - tptr)));
2062                    }
2063                }
2064                break;
2065            case ' ': /* any sort of whitespace (' ' and \t) */
2066                timelib_eat_spaces((char **) &ptr);
2067                break;
2068            case 'U': /* epoch seconds */
2069                TIMELIB_CHECK_NUMBER;
2070                TIMELIB_HAVE_RELATIVE();
2071                tmp = timelib_get_unsigned_nr((char **) &ptr, 24);
2072                s->time->y = 1970;
2073                s->time->m = 1;
2074                s->time->d = 1;
2075                s->time->h = s->time->i = s->time->s = 0;
2076                s->time->f = 0.0;
2077                s->time->relative.s += tmp;
2078                s->time->is_localtime = 1;
2079                s->time->zone_type = TIMELIB_ZONETYPE_OFFSET;
2080                s->time->z = 0;
2081                s->time->dst = 0;
2082                break;
2083
2084            case 'e': /* timezone */
2085            case 'P': /* timezone */
2086            case 'T': /* timezone */
2087            case 'O': /* timezone */
2088                {
2089                    int tz_not_found;
2090                    s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
2091                    if (tz_not_found) {
2092                        add_pbf_error(s, "The timezone could not be found in the database", string, begin);
2093                    }
2094                }
2095                break;
2096
2097            case '#': /* separation symbol */
2098                if (*ptr == ';' || *ptr == ':' || *ptr == '/' || *ptr == '.' || *ptr == ',' || *ptr == '-' || *ptr == '(' || *ptr == ')') {
2099                    ++ptr;
2100                } else {
2101                    add_pbf_error(s, "The separation symbol ([;:/.,-]) could not be found", string, begin);
2102                }
2103                break;
2104
2105            case ';':
2106            case ':':
2107            case '/':
2108            case '.':
2109            case ',':
2110            case '-':
2111            case '(':
2112            case ')':
2113                if (*ptr == *fptr) {
2114                    ++ptr;
2115                } else {
2116                    add_pbf_error(s, "The separation symbol could not be found", string, begin);
2117                }
2118                break;
2119
2120            case '!': /* reset all fields to default */
2121                timelib_time_reset_fields(s->time);
2122                break; /* break intentionally not missing */
2123
2124            case '|': /* reset all fields to default when not set */
2125                timelib_time_reset_unset_fields(s->time);
2126                break; /* break intentionally not missing */
2127
2128            case '?': /* random char */
2129                ++ptr;
2130                break;
2131
2132            case '\\': /* escaped char */
2133                *fptr++;
2134                if (*ptr == *fptr) {
2135                    ++ptr;
2136                } else {
2137                    add_pbf_error(s, "The escaped character could not be found", string, begin);
2138                }
2139                break;
2140
2141            case '*': /* random chars until a separator or number ([ \t.,:;/-0123456789]) */
2142                timelib_eat_until_separator((char **) &ptr);
2143                break;
2144
2145            case '+': /* allow extra chars in the format */
2146                allow_extra = 1;
2147                break;
2148
2149            default:
2150                if (*fptr != *ptr) {
2151                    add_pbf_error(s, "The format separator does not match", string, begin);
2152                }
2153                ptr++;
2154        }
2155        fptr++;
2156    }
2157    if (*ptr) {
2158        if (allow_extra) {
2159            add_pbf_warning(s, "Trailing data", string, ptr);
2160        } else {
2161            add_pbf_error(s, "Trailing data", string, ptr);
2162        }
2163    }
2164    /* ignore trailing +'s */
2165    while (*fptr == '+') {
2166        fptr++;
2167    }
2168    if (*fptr) {
2169        /* Trailing | and ! specifiers are valid. */
2170        int done = 0;
2171        while (*fptr && !done) {
2172            switch (*fptr++) {
2173                case '!': /* reset all fields to default */
2174                    timelib_time_reset_fields(s->time);
2175                    break;
2176
2177                case '|': /* reset all fields to default when not set */
2178                    timelib_time_reset_unset_fields(s->time);
2179                    break;
2180
2181                default:
2182                    add_pbf_error(s, "Data missing", string, ptr);
2183                    done = 1;
2184            }
2185        }
2186    }
2187
2188    /* clean up a bit */
2189    if (s->time->h != TIMELIB_UNSET || s->time->i != TIMELIB_UNSET || s->time->s != TIMELIB_UNSET) {
2190        if (s->time->h == TIMELIB_UNSET ) {
2191            s->time->h = 0;
2192        }
2193        if (s->time->i == TIMELIB_UNSET ) {
2194            s->time->i = 0;
2195        }
2196        if (s->time->s == TIMELIB_UNSET ) {
2197            s->time->s = 0;
2198        }
2199    }
2200
2201    /* do funky checking whether the parsed time was valid time */
2202    if (s->time->h != TIMELIB_UNSET && s->time->i != TIMELIB_UNSET &&
2203        s->time->s != TIMELIB_UNSET &&
2204        !timelib_valid_time( s->time->h, s->time->i, s->time->s)) {
2205        add_pbf_warning(s, "The parsed time was invalid", string, ptr);
2206    }
2207    /* do funky checking whether the parsed date was valid date */
2208    if (s->time->y != TIMELIB_UNSET && s->time->m != TIMELIB_UNSET &&
2209        s->time->d != TIMELIB_UNSET &&
2210        !timelib_valid_date( s->time->y, s->time->m, s->time->d)) {
2211        add_pbf_warning(s, "The parsed date was invalid", string, ptr);
2212    }
2213
2214    if (errors) {
2215        *errors = in.errors;
2216    } else {
2217        timelib_error_container_dtor(in.errors);
2218    }
2219    return in.time;
2220}
2221
2222void timelib_fill_holes(timelib_time *parsed, timelib_time *now, int options)
2223{
2224    if (!(options & TIMELIB_OVERRIDE_TIME) && parsed->have_date && !parsed->have_time) {
2225        parsed->h = 0;
2226        parsed->i = 0;
2227        parsed->s = 0;
2228        parsed->f = 0;
2229    }
2230    if (parsed->y == TIMELIB_UNSET) parsed->y = now->y != TIMELIB_UNSET ? now->y : 0;
2231    if (parsed->d == TIMELIB_UNSET) parsed->d = now->d != TIMELIB_UNSET ? now->d : 0;
2232    if (parsed->m == TIMELIB_UNSET) parsed->m = now->m != TIMELIB_UNSET ? now->m : 0;
2233    if (parsed->h == TIMELIB_UNSET) parsed->h = now->h != TIMELIB_UNSET ? now->h : 0;
2234    if (parsed->i == TIMELIB_UNSET) parsed->i = now->i != TIMELIB_UNSET ? now->i : 0;
2235    if (parsed->s == TIMELIB_UNSET) parsed->s = now->s != TIMELIB_UNSET ? now->s : 0;
2236    if (parsed->f == TIMELIB_UNSET) parsed->f = now->f != TIMELIB_UNSET ? now->f : 0;
2237    if (parsed->z == TIMELIB_UNSET) parsed->z = now->z != TIMELIB_UNSET ? now->z : 0;
2238    if (parsed->dst == TIMELIB_UNSET) parsed->dst = now->dst != TIMELIB_UNSET ? now->dst : 0;
2239
2240    if (!parsed->tz_abbr) {
2241        parsed->tz_abbr = now->tz_abbr ? strdup(now->tz_abbr) : NULL;
2242    }
2243    if (!parsed->tz_info) {
2244        parsed->tz_info = now->tz_info ? (!(options & TIMELIB_NO_CLONE) ? timelib_tzinfo_clone(now->tz_info) : now->tz_info) : NULL;
2245    }
2246    if (parsed->zone_type == 0 && now->zone_type != 0) {
2247        parsed->zone_type = now->zone_type;
2248/*      parsed->tz_abbr = now->tz_abbr ? strdup(now->tz_abbr) : NULL;
2249        parsed->tz_info = now->tz_info ? timelib_tzinfo_clone(now->tz_info) : NULL;
2250*/      parsed->is_localtime = 1;
2251    }
2252/*  timelib_dump_date(parsed, 2);
2253    timelib_dump_date(now, 2);
2254*/
2255}
2256
2257char *timelib_timezone_id_from_abbr(const char *abbr, long gmtoffset, int isdst)
2258{
2259    const timelib_tz_lookup_table *tp;
2260
2261    tp = zone_search(abbr, gmtoffset, isdst);
2262    if (tp) {
2263        return (tp->full_tz_name);
2264    } else {
2265        return NULL;
2266    }
2267}
2268
2269const timelib_tz_lookup_table *timelib_timezone_abbreviations_list(void)
2270{
2271    return timelib_timezone_lookup;
2272}
2273
2274#ifdef DEBUG_PARSER_STUB
2275int main(void)
2276{
2277    timelib_time time = timelib_strtotime("May 12");
2278
2279    printf ("%04d-%02d-%02d %02d:%02d:%02d.%-5d %+04d %1d",
2280        time.y, time.m, time.d, time.h, time.i, time.s, time.f, time.z, time.dst);
2281    if (time.have_relative) {
2282        printf ("%3dY %3dM %3dD / %3dH %3dM %3dS",
2283            time.relative.y, time.relative.m, time.relative.d, time.relative.h, time.relative.i, time.relative.s);
2284    }
2285    if (time.have_weekday_relative) {
2286        printf (" / %d", time.relative.weekday);
2287    }
2288    if (time.have_weeknr_day) {
2289        printf(" / %dW%d", time.relative.weeknr_day.weeknr, time.relative.weeknr_day.dayofweek);
2290    }
2291    return 0;
2292}
2293#endif
2294
2295/*
2296 * vim: syntax=c
2297 */
2298