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