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