xref: /PHP-7.4/ext/date/lib/parse_date.re (revision fac3fbcb)
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			if (begin[2] != ':') {
768				return 0;
769			}
770			tmp = sHOUR(strtol(begin, NULL, 10)) + sMIN(strtol(begin + 3, NULL, 10));
771			return tmp;
772	}
773	return 0;
774}
775
776static timelib_long timelib_parse_tz_minutes(char **ptr, timelib_time *t)
777{
778	timelib_long retval = TIMELIB_UNSET;
779	char *begin = *ptr;
780
781	/* First character must be +/- */
782	if (**ptr != '+' && **ptr != '-') {
783		return retval;
784	}
785
786	++*ptr;
787	while (isdigit(**ptr)) {
788		++*ptr;
789	}
790
791	if (*begin == '+') {
792		t->is_localtime = 1;
793		t->zone_type = TIMELIB_ZONETYPE_OFFSET;
794		t->dst = 0;
795
796		retval = sMIN(strtol(begin + 1, NULL, 10));
797	} else if (*begin == '-') {
798		t->is_localtime = 1;
799		t->zone_type = TIMELIB_ZONETYPE_OFFSET;
800		t->dst = 0;
801
802		retval = -1 * sMIN(strtol(begin + 1, NULL, 10));
803	}
804	return retval;
805}
806
807timelib_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)
808{
809	timelib_tzinfo *res;
810	timelib_long            retval = 0;
811
812	*tz_not_found = 0;
813
814	while (**ptr == ' ' || **ptr == '\t' || **ptr == '(') {
815		++*ptr;
816	}
817	if ((*ptr)[0] == 'G' && (*ptr)[1] == 'M' && (*ptr)[2] == 'T' && ((*ptr)[3] == '+' || (*ptr)[3] == '-')) {
818		*ptr += 3;
819	}
820	if (**ptr == '+') {
821		++*ptr;
822		t->is_localtime = 1;
823		t->zone_type = TIMELIB_ZONETYPE_OFFSET;
824		*tz_not_found = 0;
825		t->dst = 0;
826
827		retval = timelib_parse_tz_cor(ptr);
828	} else if (**ptr == '-') {
829		++*ptr;
830		t->is_localtime = 1;
831		t->zone_type = TIMELIB_ZONETYPE_OFFSET;
832		*tz_not_found = 0;
833		t->dst = 0;
834
835		retval = -1 * timelib_parse_tz_cor(ptr);
836	} else {
837		int found = 0;
838		timelib_long offset = 0;
839		char *tz_abbr;
840
841		t->is_localtime = 1;
842
843		/* First, we lookup by abbreviation only */
844		offset = timelib_lookup_abbr(ptr, dst, &tz_abbr, &found);
845		if (found) {
846			t->zone_type = TIMELIB_ZONETYPE_ABBR;
847			timelib_time_tz_abbr_update(t, tz_abbr);
848		}
849
850		/* Otherwise, we look if we have a TimeZone identifier */
851		if (!found || strcmp("UTC", tz_abbr) == 0) {
852			int dummy_error_code;
853
854			if ((res = tz_wrapper(tz_abbr, tzdb, &dummy_error_code)) != NULL) {
855				t->tz_info = res;
856				t->zone_type = TIMELIB_ZONETYPE_ID;
857				found++;
858			}
859		}
860		timelib_free(tz_abbr);
861		*tz_not_found = (found == 0);
862		retval = offset;
863	}
864	while (**ptr == ')') {
865		++*ptr;
866	}
867	return retval;
868}
869
870#define timelib_split_free(arg) {       \
871	int i;                         \
872	for (i = 0; i < arg.c; i++) {  \
873		timelib_free(arg.v[i]);    \
874	}                              \
875	if (arg.v) {                   \
876		timelib_free(arg.v);       \
877	}                              \
878}
879
880static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
881{
882	uchar *cursor = s->cur;
883	char *str, *ptr = NULL;
884
885std:
886	s->tok = cursor;
887	s->len = 0;
888/*!re2c
889any = [\000-\377];
890
891space = [ \t]+;
892frac = "."[0-9]+;
893
894ago = 'ago';
895
896hour24 = [01]?[0-9] | "2"[0-4];
897hour24lz = [01][0-9] | "2"[0-4];
898hour12 = "0"?[1-9] | "1"[0-2];
899minute = [0-5]?[0-9];
900minutelz = [0-5][0-9];
901second = minute | "60";
902secondlz = minutelz | "60";
903meridian = ([AaPp] "."? [Mm] "."?) [\000\t ];
904tz = "("? [A-Za-z]{1,6} ")"? | [A-Z][a-z]+([_/-][A-Za-z]+)+;
905tzcorrection = "GMT"? [+-] hour24 ":"? minute?;
906
907daysuf = "st" | "nd" | "rd" | "th";
908
909month = "0"? [0-9] | "1"[0-2];
910day   = (([0-2]?[0-9]) | ("3"[01])) daysuf?;
911year  = [0-9]{1,4};
912year2 = [0-9]{2};
913year4 = [0-9]{4};
914year4withsign = [+-]? [0-9]{4};
915
916dayofyear = "00"[1-9] | "0"[1-9][0-9] | [1-2][0-9][0-9] | "3"[0-5][0-9] | "36"[0-6];
917weekofyear = "0"[1-9] | [1-4][0-9] | "5"[0-3];
918
919monthlz = "0" [0-9] | "1" [0-2];
920daylz   = "0" [0-9] | [1-2][0-9] | "3" [01];
921
922dayfull = 'sunday' | 'monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday';
923dayabbr = 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat' | 'sun';
924dayspecial = 'weekday' | 'weekdays';
925daytext = dayfull | dayabbr | dayspecial;
926
927monthfull = 'january' | 'february' | 'march' | 'april' | 'may' | 'june' | 'july' | 'august' | 'september' | 'october' | 'november' | 'december';
928monthabbr = 'jan' | 'feb' | 'mar' | 'apr' | 'may' | 'jun' | 'jul' | 'aug' | 'sep' | 'sept' | 'oct' | 'nov' | 'dec';
929monthroman = "I" | "II" | "III" | "IV" | "V" | "VI" | "VII" | "VIII" | "IX" | "X" | "XI" | "XII";
930monthtext = monthfull | monthabbr | monthroman;
931
932/* Time formats */
933timetiny12 = hour12 space? meridian;
934timeshort12 = hour12[:.]minutelz space? meridian;
935timelong12 = hour12[:.]minute[:.]secondlz space? meridian;
936
937timeshort24 = 't'? hour24[:.]minute;
938timelong24 =  't'? hour24[:.]minute[:.]second;
939iso8601long =  't'? hour24 [:.] minute [:.] second frac;
940
941/* iso8601shorttz = hour24 [:] minutelz space? (tzcorrection | tz); */
942iso8601normtz =  't'? hour24 [:.] minute [:.] secondlz space? (tzcorrection | tz);
943/* iso8601longtz =  hour24 [:] minute [:] secondlz frac space? (tzcorrection | tz); */
944
945gnunocolon       = 't'? hour24lz minutelz;
946/* gnunocolontz     = hour24lz minutelz space? (tzcorrection | tz); */
947iso8601nocolon   = 't'? hour24lz minutelz secondlz;
948/* iso8601nocolontz = hour24lz minutelz secondlz space? (tzcorrection | tz); */
949
950/* Date formats */
951americanshort    = month "/" day;
952american         = month "/" day "/" year;
953iso8601dateslash = year4 "/" monthlz "/" daylz "/"?;
954dateslash        = year4 "/" month "/" day;
955iso8601date4     = year4withsign "-" monthlz "-" daylz;
956iso8601date2     = year2 "-" monthlz "-" daylz;
957gnudateshorter   = year4 "-" month;
958gnudateshort     = year "-" month "-" day;
959pointeddate4     = day [.\t-] month [.-] year4;
960pointeddate2     = day [.\t] month "." year2;
961datefull         = day ([ \t.-])* monthtext ([ \t.-])* year;
962datenoday        = monthtext ([ .\t-])* year4;
963datenodayrev     = year4 ([ .\t-])* monthtext;
964datetextual      = monthtext ([ .\t-])* day [,.stndrh\t ]+ year;
965datenoyear       = monthtext ([ .\t-])* day ([,.stndrh\t ]+|[\000]);
966datenoyearrev    = day ([ .\t-])* monthtext;
967datenocolon      = year4 monthlz daylz;
968
969/* Special formats */
970soap             = year4 "-" monthlz "-" daylz "T" hour24lz ":" minutelz ":" secondlz frac tzcorrection?;
971xmlrpc           = year4 monthlz daylz "T" hour24 ":" minutelz ":" secondlz;
972xmlrpcnocolon    = year4 monthlz daylz 't' hour24 minutelz secondlz;
973wddx             = year4 "-" month "-" day "T" hour24 ":" minute ":" second;
974pgydotd          = year4 "."? dayofyear;
975pgtextshort      = monthabbr "-" daylz "-" year;
976pgtextreverse    = year "-" monthabbr "-" daylz;
977mssqltime        = hour12 ":" minutelz ":" secondlz [:.] [0-9]+ meridian;
978isoweekday       = year4 "-"? "W" weekofyear "-"? [0-7];
979isoweek          = year4 "-"? "W" weekofyear;
980exif             = year4 ":" monthlz ":" daylz " " hour24lz ":" minutelz ":" secondlz;
981firstdayof       = 'first day of';
982lastdayof        = 'last day of';
983backof           = 'back of ' hour24 (space? meridian)?;
984frontof          = 'front of ' hour24 (space? meridian)?;
985
986/* Common Log Format: 10/Oct/2000:13:55:36 -0700 */
987clf              = day "/" monthabbr "/" year4 ":" hour24lz ":" minutelz ":" secondlz space tzcorrection;
988
989/* Timestamp format: @1126396800 */
990timestamp        = "@" "-"? [0-9]+;
991timestampms      = "@" "-"? [0-9]+ "." [0-9]{6};
992
993/* To fix some ambiguities */
994dateshortwithtimeshort12  = datenoyear timeshort12;
995dateshortwithtimelong12   = datenoyear timelong12;
996dateshortwithtimeshort  = datenoyear timeshort24;
997dateshortwithtimelong   = datenoyear timelong24;
998dateshortwithtimelongtz = datenoyear iso8601normtz;
999
1000/*
1001 * Relative regexps
1002 */
1003reltextnumber = 'first'|'second'|'third'|'fourth'|'fifth'|'sixth'|'seventh'|'eight'|'eighth'|'ninth'|'tenth'|'eleventh'|'twelfth';
1004reltexttext = 'next'|'last'|'previous'|'this';
1005reltextunit = 'ms' | 'µs' | (('msec'|'millisecond'|'µsec'|'microsecond'|'usec'|'sec'|'second'|'min'|'minute'|'hour'|'day'|'fortnight'|'forthnight'|'month'|'year') 's'?) | 'weeks' | daytext;
1006
1007relnumber = ([+-]*[ \t]*[0-9]{1,13});
1008relative = relnumber space? (reltextunit | 'week' );
1009relativetext = (reltextnumber|reltexttext) space reltextunit;
1010relativetextweek = reltexttext space 'week';
1011
1012weekdayof        = (reltextnumber|reltexttext) space (dayfull|dayabbr) space 'of';
1013
1014*/
1015
1016/*!re2c
1017	/* so that vim highlights correctly */
1018	'yesterday'
1019	{
1020		DEBUG_OUTPUT("yesterday");
1021		TIMELIB_INIT;
1022		TIMELIB_HAVE_RELATIVE();
1023		TIMELIB_UNHAVE_TIME();
1024
1025		s->time->relative.d = -1;
1026		TIMELIB_DEINIT;
1027		return TIMELIB_RELATIVE;
1028	}
1029
1030	'now'
1031	{
1032		DEBUG_OUTPUT("now");
1033		TIMELIB_INIT;
1034
1035		TIMELIB_DEINIT;
1036		return TIMELIB_RELATIVE;
1037	}
1038
1039	'noon'
1040	{
1041		DEBUG_OUTPUT("noon");
1042		TIMELIB_INIT;
1043		TIMELIB_UNHAVE_TIME();
1044		TIMELIB_HAVE_TIME();
1045		s->time->h = 12;
1046
1047		TIMELIB_DEINIT;
1048		return TIMELIB_RELATIVE;
1049	}
1050
1051	'midnight' | 'today'
1052	{
1053		DEBUG_OUTPUT("midnight | today");
1054		TIMELIB_INIT;
1055		TIMELIB_UNHAVE_TIME();
1056
1057		TIMELIB_DEINIT;
1058		return TIMELIB_RELATIVE;
1059	}
1060
1061	'tomorrow'
1062	{
1063		DEBUG_OUTPUT("tomorrow");
1064		TIMELIB_INIT;
1065		TIMELIB_HAVE_RELATIVE();
1066		TIMELIB_UNHAVE_TIME();
1067
1068		s->time->relative.d = 1;
1069		TIMELIB_DEINIT;
1070		return TIMELIB_RELATIVE;
1071	}
1072
1073	timestamp
1074	{
1075		timelib_ull i;
1076
1077		TIMELIB_INIT;
1078		TIMELIB_HAVE_RELATIVE();
1079		TIMELIB_UNHAVE_DATE();
1080		TIMELIB_UNHAVE_TIME();
1081		TIMELIB_HAVE_TZ();
1082
1083		i = timelib_get_unsigned_nr((char **) &ptr, 24);
1084		s->time->y = 1970;
1085		s->time->m = 1;
1086		s->time->d = 1;
1087		s->time->h = s->time->i = s->time->s = 0;
1088		s->time->us = 0;
1089		s->time->relative.s += i;
1090		s->time->is_localtime = 1;
1091		s->time->zone_type = TIMELIB_ZONETYPE_OFFSET;
1092		s->time->z = 0;
1093		s->time->dst = 0;
1094
1095		TIMELIB_DEINIT;
1096		return TIMELIB_RELATIVE;
1097	}
1098
1099	timestampms
1100	{
1101		timelib_ull i, us;
1102
1103		TIMELIB_INIT;
1104		TIMELIB_HAVE_RELATIVE();
1105		TIMELIB_UNHAVE_DATE();
1106		TIMELIB_UNHAVE_TIME();
1107		TIMELIB_HAVE_TZ();
1108
1109		i = timelib_get_unsigned_nr((char **) &ptr, 24);
1110		us = timelib_get_unsigned_nr((char **) &ptr, 24);
1111		s->time->y = 1970;
1112		s->time->m = 1;
1113		s->time->d = 1;
1114		s->time->h = s->time->i = s->time->s = 0;
1115		s->time->us = 0;
1116		s->time->relative.s += i;
1117		s->time->relative.us = us;
1118		s->time->is_localtime = 1;
1119		s->time->zone_type = TIMELIB_ZONETYPE_OFFSET;
1120		s->time->z = 0;
1121		s->time->dst = 0;
1122
1123		TIMELIB_DEINIT;
1124		return TIMELIB_RELATIVE;
1125	}
1126
1127	firstdayof | lastdayof
1128	{
1129		DEBUG_OUTPUT("firstdayof | lastdayof");
1130		TIMELIB_INIT;
1131		TIMELIB_HAVE_RELATIVE();
1132
1133		/* skip "last day of" or "first day of" */
1134		if (*ptr == 'l' || *ptr == 'L') {
1135			s->time->relative.first_last_day_of = TIMELIB_SPECIAL_LAST_DAY_OF_MONTH;
1136		} else {
1137			s->time->relative.first_last_day_of = TIMELIB_SPECIAL_FIRST_DAY_OF_MONTH;
1138		}
1139
1140		TIMELIB_DEINIT;
1141		return TIMELIB_LF_DAY_OF_MONTH;
1142	}
1143
1144	backof | frontof
1145	{
1146		DEBUG_OUTPUT("backof | frontof");
1147		TIMELIB_INIT;
1148		TIMELIB_UNHAVE_TIME();
1149		TIMELIB_HAVE_TIME();
1150
1151		if (*ptr == 'b') {
1152			s->time->h = timelib_get_nr((char **) &ptr, 2);
1153			s->time->i = 15;
1154		} else {
1155			s->time->h = timelib_get_nr((char **) &ptr, 2) - 1;
1156			s->time->i = 45;
1157		}
1158		if (*ptr != '\0' ) {
1159			timelib_eat_spaces((char **) &ptr);
1160			s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1161		}
1162
1163		TIMELIB_DEINIT;
1164		return TIMELIB_LF_DAY_OF_MONTH;
1165	}
1166
1167	weekdayof
1168	{
1169		timelib_sll i;
1170		int         behavior = 0;
1171		DEBUG_OUTPUT("weekdayof");
1172		TIMELIB_INIT;
1173		TIMELIB_HAVE_RELATIVE();
1174		TIMELIB_HAVE_SPECIAL_RELATIVE();
1175
1176		i = timelib_get_relative_text((char **) &ptr, &behavior);
1177		timelib_eat_spaces((char **) &ptr);
1178		if (i > 0) { /* first, second... etc */
1179			s->time->relative.special.type = TIMELIB_SPECIAL_DAY_OF_WEEK_IN_MONTH;
1180			timelib_set_relative((char **) &ptr, i, 1, s);
1181		} else { /* last */
1182			s->time->relative.special.type = TIMELIB_SPECIAL_LAST_DAY_OF_WEEK_IN_MONTH;
1183			timelib_set_relative((char **) &ptr, i, behavior, s);
1184		}
1185		TIMELIB_DEINIT;
1186		return TIMELIB_WEEK_DAY_OF_MONTH;
1187	}
1188
1189	timetiny12 | timeshort12 | timelong12
1190	{
1191		DEBUG_OUTPUT("timetiny12 | timeshort12 | timelong12");
1192		TIMELIB_INIT;
1193		TIMELIB_HAVE_TIME();
1194		s->time->h = timelib_get_nr((char **) &ptr, 2);
1195		if (*ptr == ':' || *ptr == '.') {
1196			s->time->i = timelib_get_nr((char **) &ptr, 2);
1197			if (*ptr == ':' || *ptr == '.') {
1198				s->time->s = timelib_get_nr((char **) &ptr, 2);
1199			}
1200		}
1201		s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1202		TIMELIB_DEINIT;
1203		return TIMELIB_TIME12;
1204	}
1205
1206	mssqltime
1207	{
1208		DEBUG_OUTPUT("mssqltime");
1209		TIMELIB_INIT;
1210		TIMELIB_HAVE_TIME();
1211		s->time->h = timelib_get_nr((char **) &ptr, 2);
1212		s->time->i = timelib_get_nr((char **) &ptr, 2);
1213		if (*ptr == ':' || *ptr == '.') {
1214			s->time->s = timelib_get_nr((char **) &ptr, 2);
1215
1216			if (*ptr == ':' || *ptr == '.') {
1217				s->time->us = timelib_get_frac_nr((char **) &ptr, 8);
1218			}
1219		}
1220		timelib_eat_spaces((char **) &ptr);
1221		s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1222		TIMELIB_DEINIT;
1223		return TIMELIB_TIME24_WITH_ZONE;
1224	}
1225
1226	timeshort24 | timelong24 /* | iso8601short | iso8601norm */ | iso8601long /*| iso8601shorttz | iso8601normtz | iso8601longtz*/
1227	{
1228		int tz_not_found;
1229		DEBUG_OUTPUT("timeshort24 | timelong24 | iso8601long");
1230		TIMELIB_INIT;
1231		TIMELIB_HAVE_TIME();
1232		s->time->h = timelib_get_nr((char **) &ptr, 2);
1233		s->time->i = timelib_get_nr((char **) &ptr, 2);
1234		if (*ptr == ':' || *ptr == '.') {
1235			s->time->s = timelib_get_nr((char **) &ptr, 2);
1236
1237			if (*ptr == '.') {
1238				s->time->us = timelib_get_frac_nr((char **) &ptr, 8);
1239			}
1240		}
1241
1242		if (*ptr != '\0') {
1243			s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1244			if (tz_not_found) {
1245				add_error(s, TIMELIB_ERR_TZID_NOT_FOUND, "The timezone could not be found in the database");
1246			}
1247		}
1248		TIMELIB_DEINIT;
1249		return TIMELIB_TIME24_WITH_ZONE;
1250	}
1251
1252	gnunocolon
1253	{
1254		DEBUG_OUTPUT("gnunocolon");
1255		TIMELIB_INIT;
1256		switch (s->time->have_time) {
1257			case 0:
1258				s->time->h = timelib_get_nr((char **) &ptr, 2);
1259				s->time->i = timelib_get_nr((char **) &ptr, 2);
1260				s->time->s = 0;
1261				break;
1262			case 1:
1263				s->time->y = timelib_get_nr((char **) &ptr, 4);
1264				break;
1265			default:
1266				TIMELIB_DEINIT;
1267				add_error(s, TIMELIB_ERR_DOUBLE_TIME, "Double time specification");
1268				return TIMELIB_ERROR;
1269		}
1270		s->time->have_time++;
1271		TIMELIB_DEINIT;
1272		return TIMELIB_GNU_NOCOLON;
1273	}
1274/*
1275	gnunocolontz
1276	{
1277		DEBUG_OUTPUT("gnunocolontz");
1278		TIMELIB_INIT;
1279		switch (s->time->have_time) {
1280			case 0:
1281				s->time->h = timelib_get_nr((char **) &ptr, 2);
1282				s->time->i = timelib_get_nr((char **) &ptr, 2);
1283				s->time->s = 0;
1284				s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, s->tzdb, tz_get_wrapper);
1285				break;
1286			case 1:
1287				s->time->y = timelib_get_nr((char **) &ptr, 4);
1288				break;
1289			default:
1290				TIMELIB_DEINIT;
1291				return TIMELIB_ERROR;
1292		}
1293		s->time->have_time++;
1294		TIMELIB_DEINIT;
1295		return TIMELIB_GNU_NOCOLON_TZ;
1296	}
1297*/
1298	iso8601nocolon /*| iso8601nocolontz*/
1299	{
1300		int tz_not_found;
1301		DEBUG_OUTPUT("iso8601nocolon");
1302		TIMELIB_INIT;
1303		TIMELIB_HAVE_TIME();
1304		s->time->h = timelib_get_nr((char **) &ptr, 2);
1305		s->time->i = timelib_get_nr((char **) &ptr, 2);
1306		s->time->s = timelib_get_nr((char **) &ptr, 2);
1307
1308		if (*ptr != '\0') {
1309			s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1310			if (tz_not_found) {
1311				add_error(s, TIMELIB_ERR_TZID_NOT_FOUND, "The timezone could not be found in the database");
1312			}
1313		}
1314		TIMELIB_DEINIT;
1315		return TIMELIB_ISO_NOCOLON;
1316	}
1317
1318	americanshort | american
1319	{
1320		int length = 0;
1321		DEBUG_OUTPUT("americanshort | american");
1322		TIMELIB_INIT;
1323		TIMELIB_HAVE_DATE();
1324		s->time->m = timelib_get_nr((char **) &ptr, 2);
1325		s->time->d = timelib_get_nr((char **) &ptr, 2);
1326		if (*ptr == '/') {
1327			s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1328			TIMELIB_PROCESS_YEAR(s->time->y, length);
1329		}
1330		TIMELIB_DEINIT;
1331		return TIMELIB_AMERICAN;
1332	}
1333
1334	iso8601date4 | iso8601dateslash | dateslash
1335	{
1336		DEBUG_OUTPUT("iso8601date4 | iso8601date2 | iso8601dateslash | dateslash");
1337		TIMELIB_INIT;
1338		TIMELIB_HAVE_DATE();
1339		s->time->y = timelib_get_unsigned_nr((char **) &ptr, 4);
1340		s->time->m = timelib_get_nr((char **) &ptr, 2);
1341		s->time->d = timelib_get_nr((char **) &ptr, 2);
1342		TIMELIB_DEINIT;
1343		return TIMELIB_ISO_DATE;
1344	}
1345
1346	iso8601date2
1347	{
1348		int length = 0;
1349		DEBUG_OUTPUT("iso8601date2");
1350		TIMELIB_INIT;
1351		TIMELIB_HAVE_DATE();
1352		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1353		s->time->m = timelib_get_nr((char **) &ptr, 2);
1354		s->time->d = timelib_get_nr((char **) &ptr, 2);
1355		TIMELIB_PROCESS_YEAR(s->time->y, length);
1356		TIMELIB_DEINIT;
1357		return TIMELIB_ISO_DATE;
1358	}
1359
1360	gnudateshorter
1361	{
1362		int length = 0;
1363		DEBUG_OUTPUT("gnudateshorter");
1364		TIMELIB_INIT;
1365		TIMELIB_HAVE_DATE();
1366		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1367		s->time->m = timelib_get_nr((char **) &ptr, 2);
1368		s->time->d = 1;
1369		TIMELIB_PROCESS_YEAR(s->time->y, length);
1370		TIMELIB_DEINIT;
1371		return TIMELIB_ISO_DATE;
1372	}
1373
1374	gnudateshort
1375	{
1376		int length = 0;
1377		DEBUG_OUTPUT("gnudateshort");
1378		TIMELIB_INIT;
1379		TIMELIB_HAVE_DATE();
1380		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1381		s->time->m = timelib_get_nr((char **) &ptr, 2);
1382		s->time->d = timelib_get_nr((char **) &ptr, 2);
1383		TIMELIB_PROCESS_YEAR(s->time->y, length);
1384		TIMELIB_DEINIT;
1385		return TIMELIB_ISO_DATE;
1386	}
1387
1388	datefull
1389	{
1390		int length = 0;
1391		DEBUG_OUTPUT("datefull");
1392		TIMELIB_INIT;
1393		TIMELIB_HAVE_DATE();
1394		s->time->d = timelib_get_nr((char **) &ptr, 2);
1395		timelib_skip_day_suffix((char **) &ptr);
1396		s->time->m = timelib_get_month((char **) &ptr);
1397		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1398		TIMELIB_PROCESS_YEAR(s->time->y, length);
1399		TIMELIB_DEINIT;
1400		return TIMELIB_DATE_FULL;
1401	}
1402
1403	pointeddate4
1404	{
1405		DEBUG_OUTPUT("pointed date YYYY");
1406		TIMELIB_INIT;
1407		TIMELIB_HAVE_DATE();
1408		s->time->d = timelib_get_nr((char **) &ptr, 2);
1409		s->time->m = timelib_get_nr((char **) &ptr, 2);
1410		s->time->y = timelib_get_nr((char **) &ptr, 4);
1411		TIMELIB_DEINIT;
1412		return TIMELIB_DATE_FULL_POINTED;
1413	}
1414
1415	pointeddate2
1416	{
1417		int length = 0;
1418		DEBUG_OUTPUT("pointed date YY");
1419		TIMELIB_INIT;
1420		TIMELIB_HAVE_DATE();
1421		s->time->d = timelib_get_nr((char **) &ptr, 2);
1422		s->time->m = timelib_get_nr((char **) &ptr, 2);
1423		s->time->y = timelib_get_nr_ex((char **) &ptr, 2, &length);
1424		TIMELIB_PROCESS_YEAR(s->time->y, length);
1425		TIMELIB_DEINIT;
1426		return TIMELIB_DATE_FULL_POINTED;
1427	}
1428
1429	datenoday
1430	{
1431		int length = 0;
1432		DEBUG_OUTPUT("datenoday");
1433		TIMELIB_INIT;
1434		TIMELIB_HAVE_DATE();
1435		s->time->m = timelib_get_month((char **) &ptr);
1436		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1437		s->time->d = 1;
1438		TIMELIB_PROCESS_YEAR(s->time->y, length);
1439		TIMELIB_DEINIT;
1440		return TIMELIB_DATE_NO_DAY;
1441	}
1442
1443	datenodayrev
1444	{
1445		int length = 0;
1446		DEBUG_OUTPUT("datenodayrev");
1447		TIMELIB_INIT;
1448		TIMELIB_HAVE_DATE();
1449		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1450		s->time->m = timelib_get_month((char **) &ptr);
1451		s->time->d = 1;
1452		TIMELIB_PROCESS_YEAR(s->time->y, length);
1453		TIMELIB_DEINIT;
1454		return TIMELIB_DATE_NO_DAY;
1455	}
1456
1457	datetextual | datenoyear
1458	{
1459		int length = 0;
1460		DEBUG_OUTPUT("datetextual | datenoyear");
1461		TIMELIB_INIT;
1462		TIMELIB_HAVE_DATE();
1463		s->time->m = timelib_get_month((char **) &ptr);
1464		s->time->d = timelib_get_nr((char **) &ptr, 2);
1465		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1466		TIMELIB_PROCESS_YEAR(s->time->y, length);
1467		TIMELIB_DEINIT;
1468		return TIMELIB_DATE_TEXT;
1469	}
1470
1471	datenoyearrev
1472	{
1473		DEBUG_OUTPUT("datenoyearrev");
1474		TIMELIB_INIT;
1475		TIMELIB_HAVE_DATE();
1476		s->time->d = timelib_get_nr((char **) &ptr, 2);
1477		timelib_skip_day_suffix((char **) &ptr);
1478		s->time->m = timelib_get_month((char **) &ptr);
1479		TIMELIB_DEINIT;
1480		return TIMELIB_DATE_TEXT;
1481	}
1482
1483	datenocolon
1484	{
1485		DEBUG_OUTPUT("datenocolon");
1486		TIMELIB_INIT;
1487		TIMELIB_HAVE_DATE();
1488		s->time->y = timelib_get_nr((char **) &ptr, 4);
1489		s->time->m = timelib_get_nr((char **) &ptr, 2);
1490		s->time->d = timelib_get_nr((char **) &ptr, 2);
1491		TIMELIB_DEINIT;
1492		return TIMELIB_DATE_NOCOLON;
1493	}
1494
1495	xmlrpc | xmlrpcnocolon | soap | wddx | exif
1496	{
1497		int tz_not_found;
1498		DEBUG_OUTPUT("xmlrpc | xmlrpcnocolon | soap | wddx | exif");
1499		TIMELIB_INIT;
1500		TIMELIB_HAVE_TIME();
1501		TIMELIB_HAVE_DATE();
1502		s->time->y = timelib_get_nr((char **) &ptr, 4);
1503		s->time->m = timelib_get_nr((char **) &ptr, 2);
1504		s->time->d = timelib_get_nr((char **) &ptr, 2);
1505		s->time->h = timelib_get_nr((char **) &ptr, 2);
1506		s->time->i = timelib_get_nr((char **) &ptr, 2);
1507		s->time->s = timelib_get_nr((char **) &ptr, 2);
1508		if (*ptr == '.') {
1509			s->time->us = timelib_get_frac_nr((char **) &ptr, 9);
1510			if (*ptr) { /* timezone is optional */
1511				s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1512				if (tz_not_found) {
1513					add_error(s, TIMELIB_ERR_TZID_NOT_FOUND, "The timezone could not be found in the database");
1514				}
1515			}
1516		}
1517		TIMELIB_DEINIT;
1518		return TIMELIB_XMLRPC_SOAP;
1519	}
1520
1521	pgydotd
1522	{
1523		int length = 0;
1524		DEBUG_OUTPUT("pgydotd");
1525		TIMELIB_INIT;
1526		TIMELIB_HAVE_DATE();
1527		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1528		s->time->d = timelib_get_nr((char **) &ptr, 3);
1529		s->time->m = 1;
1530		TIMELIB_PROCESS_YEAR(s->time->y, length);
1531		TIMELIB_DEINIT;
1532		return TIMELIB_PG_YEARDAY;
1533	}
1534
1535	isoweekday
1536	{
1537		timelib_sll w, d;
1538		DEBUG_OUTPUT("isoweekday");
1539		TIMELIB_INIT;
1540		TIMELIB_HAVE_DATE();
1541		TIMELIB_HAVE_RELATIVE();
1542
1543		s->time->y = timelib_get_nr((char **) &ptr, 4);
1544		w = timelib_get_nr((char **) &ptr, 2);
1545		d = timelib_get_nr((char **) &ptr, 1);
1546		s->time->m = 1;
1547		s->time->d = 1;
1548		s->time->relative.d = timelib_daynr_from_weeknr(s->time->y, w, d);
1549
1550		TIMELIB_DEINIT;
1551		return TIMELIB_ISO_WEEK;
1552	}
1553
1554	isoweek
1555	{
1556		timelib_sll w, d;
1557		DEBUG_OUTPUT("isoweek");
1558		TIMELIB_INIT;
1559		TIMELIB_HAVE_DATE();
1560		TIMELIB_HAVE_RELATIVE();
1561
1562		s->time->y = timelib_get_nr((char **) &ptr, 4);
1563		w = timelib_get_nr((char **) &ptr, 2);
1564		d = 1;
1565		s->time->m = 1;
1566		s->time->d = 1;
1567		s->time->relative.d = timelib_daynr_from_weeknr(s->time->y, w, d);
1568
1569		TIMELIB_DEINIT;
1570		return TIMELIB_ISO_WEEK;
1571	}
1572
1573	pgtextshort
1574	{
1575		int length = 0;
1576		DEBUG_OUTPUT("pgtextshort");
1577		TIMELIB_INIT;
1578		TIMELIB_HAVE_DATE();
1579		s->time->m = timelib_get_month((char **) &ptr);
1580		s->time->d = timelib_get_nr((char **) &ptr, 2);
1581		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1582		TIMELIB_PROCESS_YEAR(s->time->y, length);
1583		TIMELIB_DEINIT;
1584		return TIMELIB_PG_TEXT;
1585	}
1586
1587	pgtextreverse
1588	{
1589		int length = 0;
1590		DEBUG_OUTPUT("pgtextreverse");
1591		TIMELIB_INIT;
1592		TIMELIB_HAVE_DATE();
1593		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1594		s->time->m = timelib_get_month((char **) &ptr);
1595		s->time->d = timelib_get_nr((char **) &ptr, 2);
1596		TIMELIB_PROCESS_YEAR(s->time->y, length);
1597		TIMELIB_DEINIT;
1598		return TIMELIB_PG_TEXT;
1599	}
1600
1601	clf
1602	{
1603		int tz_not_found;
1604		DEBUG_OUTPUT("clf");
1605		TIMELIB_INIT;
1606		TIMELIB_HAVE_TIME();
1607		TIMELIB_HAVE_DATE();
1608		s->time->d = timelib_get_nr((char **) &ptr, 2);
1609		s->time->m = timelib_get_month((char **) &ptr);
1610		s->time->y = timelib_get_nr((char **) &ptr, 4);
1611		s->time->h = timelib_get_nr((char **) &ptr, 2);
1612		s->time->i = timelib_get_nr((char **) &ptr, 2);
1613		s->time->s = timelib_get_nr((char **) &ptr, 2);
1614		s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1615		if (tz_not_found) {
1616			add_error(s, TIMELIB_ERR_TZID_NOT_FOUND, "The timezone could not be found in the database");
1617		}
1618		TIMELIB_DEINIT;
1619		return TIMELIB_CLF;
1620	}
1621
1622	year4
1623	{
1624		DEBUG_OUTPUT("year4");
1625		TIMELIB_INIT;
1626		s->time->y = timelib_get_nr((char **) &ptr, 4);
1627		TIMELIB_DEINIT;
1628		return TIMELIB_CLF;
1629	}
1630
1631	ago
1632	{
1633		DEBUG_OUTPUT("ago");
1634		TIMELIB_INIT;
1635		s->time->relative.y = 0 - s->time->relative.y;
1636		s->time->relative.m = 0 - s->time->relative.m;
1637		s->time->relative.d = 0 - s->time->relative.d;
1638		s->time->relative.h = 0 - s->time->relative.h;
1639		s->time->relative.i = 0 - s->time->relative.i;
1640		s->time->relative.s = 0 - s->time->relative.s;
1641		s->time->relative.weekday = 0 - s->time->relative.weekday;
1642		if (s->time->relative.weekday == 0) {
1643			s->time->relative.weekday = -7;
1644		}
1645		if (s->time->relative.have_special_relative && s->time->relative.special.type == TIMELIB_SPECIAL_WEEKDAY) {
1646			s->time->relative.special.amount = 0 - s->time->relative.special.amount;
1647		}
1648		TIMELIB_DEINIT;
1649		return TIMELIB_AGO;
1650	}
1651
1652	daytext
1653	{
1654		const timelib_relunit* relunit;
1655		DEBUG_OUTPUT("daytext");
1656		TIMELIB_INIT;
1657		TIMELIB_HAVE_RELATIVE();
1658		TIMELIB_HAVE_WEEKDAY_RELATIVE();
1659		TIMELIB_UNHAVE_TIME();
1660		relunit = timelib_lookup_relunit((char**) &ptr);
1661		s->time->relative.weekday = relunit->multiplier;
1662		if (s->time->relative.weekday_behavior != 2) {
1663			s->time->relative.weekday_behavior = 1;
1664		}
1665
1666		TIMELIB_DEINIT;
1667		return TIMELIB_WEEKDAY;
1668	}
1669
1670	relativetextweek
1671	{
1672		timelib_sll i;
1673		int         behavior = 0;
1674		DEBUG_OUTPUT("relativetextweek");
1675		TIMELIB_INIT;
1676		TIMELIB_HAVE_RELATIVE();
1677
1678		while(*ptr) {
1679			i = timelib_get_relative_text((char **) &ptr, &behavior);
1680			timelib_eat_spaces((char **) &ptr);
1681			timelib_set_relative((char **) &ptr, i, behavior, s);
1682			s->time->relative.weekday_behavior = 2;
1683
1684			/* to handle the format weekday + last/this/next week */
1685			if (s->time->relative.have_weekday_relative == 0) {
1686				TIMELIB_HAVE_WEEKDAY_RELATIVE();
1687				s->time->relative.weekday = 1;
1688			}
1689		}
1690		TIMELIB_DEINIT;
1691		return TIMELIB_RELATIVE;
1692	}
1693
1694	relativetext
1695	{
1696		timelib_sll i;
1697		int         behavior = 0;
1698		DEBUG_OUTPUT("relativetext");
1699		TIMELIB_INIT;
1700		TIMELIB_HAVE_RELATIVE();
1701
1702		while(*ptr) {
1703			i = timelib_get_relative_text((char **) &ptr, &behavior);
1704			timelib_eat_spaces((char **) &ptr);
1705			timelib_set_relative((char **) &ptr, i, behavior, s);
1706		}
1707		TIMELIB_DEINIT;
1708		return TIMELIB_RELATIVE;
1709	}
1710
1711	monthfull | monthabbr
1712	{
1713		DEBUG_OUTPUT("monthtext");
1714		TIMELIB_INIT;
1715		TIMELIB_HAVE_DATE();
1716		s->time->m = timelib_lookup_month((char **) &ptr);
1717		TIMELIB_DEINIT;
1718		return TIMELIB_DATE_TEXT;
1719	}
1720
1721	tzcorrection | tz
1722	{
1723		int tz_not_found;
1724		DEBUG_OUTPUT("tzcorrection | tz");
1725		TIMELIB_INIT;
1726		TIMELIB_HAVE_TZ();
1727		s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1728		if (tz_not_found) {
1729			add_error(s, TIMELIB_ERR_TZID_NOT_FOUND, "The timezone could not be found in the database");
1730		}
1731		TIMELIB_DEINIT;
1732		return TIMELIB_TIMEZONE;
1733	}
1734
1735	dateshortwithtimeshort12 | dateshortwithtimelong12
1736	{
1737		DEBUG_OUTPUT("dateshortwithtimeshort12 | dateshortwithtimelong12");
1738		TIMELIB_INIT;
1739		TIMELIB_HAVE_DATE();
1740		s->time->m = timelib_get_month((char **) &ptr);
1741		s->time->d = timelib_get_nr((char **) &ptr, 2);
1742
1743		TIMELIB_HAVE_TIME();
1744		s->time->h = timelib_get_nr((char **) &ptr, 2);
1745		s->time->i = timelib_get_nr((char **) &ptr, 2);
1746		if (*ptr == ':' || *ptr == '.') {
1747			s->time->s = timelib_get_nr((char **) &ptr, 2);
1748
1749			if (*ptr == '.') {
1750				s->time->us = timelib_get_frac_nr((char **) &ptr, 8);
1751			}
1752		}
1753
1754		s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1755		TIMELIB_DEINIT;
1756		return TIMELIB_SHORTDATE_WITH_TIME;
1757	}
1758
1759	dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz
1760	{
1761		int tz_not_found;
1762		DEBUG_OUTPUT("dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz");
1763		TIMELIB_INIT;
1764		TIMELIB_HAVE_DATE();
1765		s->time->m = timelib_get_month((char **) &ptr);
1766		s->time->d = timelib_get_nr((char **) &ptr, 2);
1767
1768		TIMELIB_HAVE_TIME();
1769		s->time->h = timelib_get_nr((char **) &ptr, 2);
1770		s->time->i = timelib_get_nr((char **) &ptr, 2);
1771		if (*ptr == ':') {
1772			s->time->s = timelib_get_nr((char **) &ptr, 2);
1773
1774			if (*ptr == '.') {
1775				s->time->us = timelib_get_frac_nr((char **) &ptr, 8);
1776			}
1777		}
1778
1779		if (*ptr != '\0') {
1780			s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1781			if (tz_not_found) {
1782				add_error(s, TIMELIB_ERR_TZID_NOT_FOUND, "The timezone could not be found in the database");
1783			}
1784		}
1785		TIMELIB_DEINIT;
1786		return TIMELIB_SHORTDATE_WITH_TIME;
1787	}
1788
1789	relative
1790	{
1791		timelib_ull i;
1792		DEBUG_OUTPUT("relative");
1793		TIMELIB_INIT;
1794		TIMELIB_HAVE_RELATIVE();
1795
1796		while(*ptr) {
1797			i = timelib_get_unsigned_nr((char **) &ptr, 24);
1798			timelib_eat_spaces((char **) &ptr);
1799			timelib_set_relative((char **) &ptr, i, 1, s);
1800		}
1801		TIMELIB_DEINIT;
1802		return TIMELIB_RELATIVE;
1803	}
1804
1805	[ .,\t]
1806	{
1807		goto std;
1808	}
1809
1810	"\000"|"\n"
1811	{
1812		s->pos = cursor; s->line++;
1813		goto std;
1814	}
1815
1816	any
1817	{
1818		add_error(s, TIMELIB_ERR_UNEXPECTED_CHARACTER, "Unexpected character");
1819		goto std;
1820	}
1821*/
1822}
1823
1824/*!max:re2c */
1825
1826timelib_time* timelib_strtotime(char *s, size_t len, timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper)
1827{
1828	Scanner in;
1829	int t;
1830	char *e = s + len - 1;
1831
1832	memset(&in, 0, sizeof(in));
1833	in.errors = timelib_malloc(sizeof(timelib_error_container));
1834	in.errors->warning_count = 0;
1835	in.errors->warning_messages = NULL;
1836	in.errors->error_count = 0;
1837	in.errors->error_messages = NULL;
1838
1839	if (len > 0) {
1840		while (isspace(*s) && s < e) {
1841			s++;
1842		}
1843		while (isspace(*e) && e > s) {
1844			e--;
1845		}
1846	}
1847	if (e - s < 0) {
1848		in.time = timelib_time_ctor();
1849		add_error(&in, TIMELIB_ERR_EMPTY_STRING, "Empty string");
1850		if (errors) {
1851			*errors = in.errors;
1852		} else {
1853			timelib_error_container_dtor(in.errors);
1854		}
1855		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;
1856		in.time->is_localtime = in.time->zone_type = 0;
1857		return in.time;
1858	}
1859	e++;
1860
1861	in.str = timelib_malloc((e - s) + YYMAXFILL);
1862	memset(in.str, 0, (e - s) + YYMAXFILL);
1863	memcpy(in.str, s, (e - s));
1864	in.lim = in.str + (e - s) + YYMAXFILL;
1865	in.cur = in.str;
1866	in.time = timelib_time_ctor();
1867	in.time->y = TIMELIB_UNSET;
1868	in.time->d = TIMELIB_UNSET;
1869	in.time->m = TIMELIB_UNSET;
1870	in.time->h = TIMELIB_UNSET;
1871	in.time->i = TIMELIB_UNSET;
1872	in.time->s = TIMELIB_UNSET;
1873	in.time->us = TIMELIB_UNSET;
1874	in.time->z = TIMELIB_UNSET;
1875	in.time->dst = TIMELIB_UNSET;
1876	in.tzdb = tzdb;
1877	in.time->is_localtime = 0;
1878	in.time->zone_type = 0;
1879	in.time->relative.days = TIMELIB_UNSET;
1880
1881	do {
1882		t = scan(&in, tz_get_wrapper);
1883#ifdef DEBUG_PARSER
1884		printf("%d\n", t);
1885#endif
1886	} while(t != EOI);
1887
1888	/* do funky checking whether the parsed time was valid time */
1889	if (in.time->have_time && !timelib_valid_time( in.time->h, in.time->i, in.time->s)) {
1890		add_warning(&in, TIMELIB_WARN_INVALID_TIME, "The parsed time was invalid");
1891	}
1892	/* do funky checking whether the parsed date was valid date */
1893	if (in.time->have_date && !timelib_valid_date( in.time->y, in.time->m, in.time->d)) {
1894		add_warning(&in, TIMELIB_WARN_INVALID_DATE, "The parsed date was invalid");
1895	}
1896
1897	timelib_free(in.str);
1898	if (errors) {
1899		*errors = in.errors;
1900	} else {
1901		timelib_error_container_dtor(in.errors);
1902	}
1903	return in.time;
1904}
1905
1906#define TIMELIB_CHECK_NUMBER                                           \
1907		if (strchr("0123456789", *ptr) == NULL)                        \
1908		{                                                              \
1909			add_pbf_error(s, TIMELIB_ERR_UNEXPECTED_DATA, "Unexpected data found.", string, begin); \
1910		}
1911#define TIMELIB_CHECK_SIGNED_NUMBER                                    \
1912		if (strchr("-0123456789", *ptr) == NULL)                       \
1913		{                                                              \
1914			add_pbf_error(s, TIMELIB_ERR_UNEXPECTED_DATA, "Unexpected data found.", string, begin); \
1915		}
1916
1917static void timelib_time_reset_fields(timelib_time *time)
1918{
1919	assert(time != NULL);
1920
1921	time->y = 1970;
1922	time->m = 1;
1923	time->d = 1;
1924	time->h = time->i = time->s = 0;
1925	time->us = 0;
1926	time->tz_info = NULL;
1927}
1928
1929static void timelib_time_reset_unset_fields(timelib_time *time)
1930{
1931	assert(time != NULL);
1932
1933	if (time->y == TIMELIB_UNSET ) time->y = 1970;
1934	if (time->m == TIMELIB_UNSET ) time->m = 1;
1935	if (time->d == TIMELIB_UNSET ) time->d = 1;
1936	if (time->h == TIMELIB_UNSET ) time->h = 0;
1937	if (time->i == TIMELIB_UNSET ) time->i = 0;
1938	if (time->s == TIMELIB_UNSET ) time->s = 0;
1939	if (time->us == TIMELIB_UNSET ) time->us = 0;
1940}
1941
1942static const timelib_format_specifier default_format_map[] = {
1943	{'+', TIMELIB_FORMAT_ALLOW_EXTRA_CHARACTERS},
1944	{'#', TIMELIB_FORMAT_ANY_SEPARATOR},
1945	{'j', TIMELIB_FORMAT_DAY_TWO_DIGIT},
1946	{'d', TIMELIB_FORMAT_DAY_TWO_DIGIT_PADDED},
1947	{'z', TIMELIB_FORMAT_DAY_OF_YEAR},
1948	{'S', TIMELIB_FORMAT_DAY_SUFFIX},
1949	{'U', TIMELIB_FORMAT_EPOCH_SECONDS},
1950	{'\\', TIMELIB_FORMAT_ESCAPE},
1951	{'h', TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX},
1952	{'g', TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX_PADDED},
1953	{'H', TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX},
1954	{'G', TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX_PADDED},
1955	{'a', TIMELIB_FORMAT_MERIDIAN},
1956	{'A', TIMELIB_FORMAT_MERIDIAN},
1957	{'u', TIMELIB_FORMAT_MICROSECOND_SIX_DIGIT},
1958	{'v', TIMELIB_FORMAT_MILLISECOND_THREE_DIGIT},
1959	{'i', TIMELIB_FORMAT_MINUTE_TWO_DIGIT},
1960	{'n', TIMELIB_FORMAT_MONTH_TWO_DIGIT},
1961	{'m', TIMELIB_FORMAT_MONTH_TWO_DIGIT_PADDED},
1962	{'?', TIMELIB_FORMAT_RANDOM_CHAR},
1963	{'!', TIMELIB_FORMAT_RESET_ALL},
1964	{'|', TIMELIB_FORMAT_RESET_ALL_WHEN_NOT_SET},
1965	{'s', TIMELIB_FORMAT_SECOND_TWO_DIGIT},
1966	{';', TIMELIB_FORMAT_SEPARATOR},
1967	{':', TIMELIB_FORMAT_SEPARATOR},
1968	{'/', TIMELIB_FORMAT_SEPARATOR},
1969	{'.', TIMELIB_FORMAT_SEPARATOR},
1970	{',', TIMELIB_FORMAT_SEPARATOR},
1971	{'-', TIMELIB_FORMAT_SEPARATOR},
1972	{'(', TIMELIB_FORMAT_SEPARATOR},
1973	{')', TIMELIB_FORMAT_SEPARATOR},
1974	{'*', TIMELIB_FORMAT_SKIP_TO_SEPARATOR},
1975	{'D', TIMELIB_FORMAT_TEXTUAL_DAY_3_LETTER},
1976	{'l', TIMELIB_FORMAT_TEXTUAL_DAY_FULL},
1977	{'M', TIMELIB_FORMAT_TEXTUAL_MONTH_3_LETTER},
1978	{'F', TIMELIB_FORMAT_TEXTUAL_MONTH_FULL},
1979	{'e', TIMELIB_FORMAT_TIMEZONE_OFFSET},
1980	{'P', TIMELIB_FORMAT_TIMEZONE_OFFSET},
1981	{'T', TIMELIB_FORMAT_TIMEZONE_OFFSET},
1982	{'O', TIMELIB_FORMAT_TIMEZONE_OFFSET},
1983	{' ', TIMELIB_FORMAT_WHITESPACE},
1984	{'y', TIMELIB_FORMAT_YEAR_TWO_DIGIT},
1985	{'Y', TIMELIB_FORMAT_YEAR_FOUR_DIGIT},
1986	{'\0', TIMELIB_FORMAT_END}
1987};
1988
1989static const timelib_format_config default_format_config = {
1990	default_format_map,
1991	// No prefix required by default.
1992	'\0'
1993};
1994
1995static timelib_format_specifier_code timelib_lookup_format(char input, const timelib_format_specifier* format_map)
1996{
1997	while (format_map && format_map->specifier != '\0') {
1998		if (format_map->specifier == input) {
1999			return format_map->code;
2000		}
2001		format_map++;
2002	}
2003	return TIMELIB_FORMAT_LITERAL;
2004}
2005
2006timelib_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)
2007{
2008	return timelib_parse_from_format_with_map(format, string, len, errors, tzdb, tz_get_wrapper, &default_format_config);
2009}
2010
2011timelib_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)
2012{
2013	char        *fptr = format;
2014	char        *ptr = string;
2015	char        *begin;
2016	timelib_sll  tmp;
2017	Scanner      in;
2018	Scanner     *s = &in;
2019	bool         allow_extra = false;
2020	bool         prefix_found = false;
2021	int          iso_year = TIMELIB_UNSET;
2022	int          iso_week_of_year = TIMELIB_UNSET;
2023	int          iso_day_of_week = TIMELIB_UNSET;
2024	char         prefix_char = format_config->prefix_char;
2025	const timelib_format_specifier *format_map = format_config->format_map;
2026
2027	memset(&in, 0, sizeof(in));
2028	in.errors = timelib_malloc(sizeof(timelib_error_container));
2029	in.errors->warning_count = 0;
2030	in.errors->warning_messages = NULL;
2031	in.errors->error_count = 0;
2032	in.errors->error_messages = NULL;
2033
2034	in.time = timelib_time_ctor();
2035	in.time->y = TIMELIB_UNSET;
2036	in.time->d = TIMELIB_UNSET;
2037	in.time->m = TIMELIB_UNSET;
2038	in.time->h = TIMELIB_UNSET;
2039	in.time->i = TIMELIB_UNSET;
2040	in.time->s = TIMELIB_UNSET;
2041	in.time->us = TIMELIB_UNSET;
2042	in.time->z = TIMELIB_UNSET;
2043	in.time->dst = TIMELIB_UNSET;
2044	in.tzdb = tzdb;
2045	in.time->is_localtime = 0;
2046	in.time->zone_type = 0;
2047
2048	/* Loop over the format string */
2049	while (*fptr && *ptr) {
2050		begin = ptr;
2051
2052		if (prefix_char) {
2053			/* There are 2 cases where the input string and format string
2054			 * should match the next literal:
2055			 *
2056			 * 1. No prefix has been specified yet in the format, so expect 1:1
2057			 *    match.
2058			 * 2. Sequential prefix characters indicating that the second
2059			 *    prefix is escaped. (e.g. "%%" is expecting literal "%")
2060			 */
2061			if ((!prefix_found && *fptr != prefix_char) ||
2062				(prefix_found && *fptr == prefix_char)) {
2063				if (*fptr != *ptr) {
2064					add_pbf_error(s, TIMELIB_ERR_FORMAT_LITERAL_MISMATCH, "Format literal not found", string, begin);
2065				}
2066				ptr++;
2067				fptr++;
2068				prefix_found = false;
2069				continue;
2070			}
2071
2072			if (*fptr == prefix_char) {
2073				fptr++;
2074				prefix_found = true;
2075				continue;
2076			}
2077
2078			/* Fall through case is that the prefix has been found and the next
2079			 * character is the format specifier. */
2080			prefix_found = false;
2081		}
2082
2083		switch (timelib_lookup_format(*fptr, format_map)) {
2084			case TIMELIB_FORMAT_TEXTUAL_DAY_3_LETTER: /* three letter day */
2085			case TIMELIB_FORMAT_TEXTUAL_DAY_FULL: /* full day */
2086				{
2087					const timelib_relunit* tmprel = 0;
2088
2089					tmprel = timelib_lookup_relunit((char **) &ptr);
2090					if (!tmprel) {
2091						add_pbf_error(s, TIMELIB_ERR_NO_TEXTUAL_DAY, "A textual day could not be found", string, begin);
2092						break;
2093					} else {
2094						in.time->have_relative = 1;
2095						in.time->relative.have_weekday_relative = 1;
2096						in.time->relative.weekday = tmprel->multiplier;
2097						in.time->relative.weekday_behavior = 1;
2098					}
2099				}
2100				break;
2101			case TIMELIB_FORMAT_DAY_TWO_DIGIT: /* two digit day, without leading zero */
2102			case TIMELIB_FORMAT_DAY_TWO_DIGIT_PADDED: /* two digit day, with leading zero */
2103				TIMELIB_CHECK_NUMBER;
2104				if ((s->time->d = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) {
2105					add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_DAY, "A two digit day could not be found", string, begin);
2106				}
2107				break;
2108			case TIMELIB_FORMAT_DAY_SUFFIX: /* day suffix, ignored, nor checked */
2109				timelib_skip_day_suffix((char **) &ptr);
2110				break;
2111			case TIMELIB_FORMAT_DAY_OF_YEAR: /* day of year - resets month (0 based) - also initializes everything else to !TIMELIB_UNSET */
2112				TIMELIB_CHECK_NUMBER;
2113				if ((tmp = timelib_get_nr((char **) &ptr, 3)) == TIMELIB_UNSET) {
2114					add_pbf_error(s, TIMELIB_ERR_NO_THREE_DIGIT_DAY_OF_YEAR, "A three digit day-of-year could not be found", string, begin);
2115				} else {
2116					s->time->m = 1;
2117					s->time->d = tmp + 1;
2118					timelib_do_normalize(s->time);
2119				}
2120				break;
2121
2122			case TIMELIB_FORMAT_MONTH_TWO_DIGIT: /* two digit month, without leading zero */
2123			case TIMELIB_FORMAT_MONTH_TWO_DIGIT_PADDED: /* two digit month, with leading zero */
2124				TIMELIB_CHECK_NUMBER;
2125				if ((s->time->m = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) {
2126					add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_MONTH, "A two digit month could not be found", string, begin);
2127				}
2128				break;
2129			case TIMELIB_FORMAT_TEXTUAL_MONTH_3_LETTER: /* three letter month */
2130			case TIMELIB_FORMAT_TEXTUAL_MONTH_FULL: /* full month */
2131				tmp = timelib_lookup_month((char **) &ptr);
2132				if (!tmp) {
2133					add_pbf_error(s, TIMELIB_ERR_NO_TEXTUAL_MONTH, "A textual month could not be found", string, begin);
2134				} else {
2135					s->time->m = tmp;
2136				}
2137				break;
2138			case TIMELIB_FORMAT_YEAR_TWO_DIGIT: /* two digit year */
2139				{
2140					int length = 0;
2141					TIMELIB_CHECK_NUMBER;
2142					if ((s->time->y = timelib_get_nr_ex((char **) &ptr, 2, &length)) == TIMELIB_UNSET) {
2143						add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_YEAR, "A two digit year could not be found", string, begin);
2144					}
2145					TIMELIB_PROCESS_YEAR(s->time->y, length);
2146				}
2147				break;
2148			case TIMELIB_FORMAT_YEAR_FOUR_DIGIT: /* four digit year */
2149				TIMELIB_CHECK_NUMBER;
2150				if ((s->time->y = timelib_get_nr((char **) &ptr, 4)) == TIMELIB_UNSET) {
2151					add_pbf_error(s, TIMELIB_ERR_NO_FOUR_DIGIT_YEAR, "A four digit year could not be found", string, begin);
2152				}
2153				break;
2154			case TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX: /* two digit hour, without leading zero */
2155			case TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX_PADDED: /* two digit hour, with leading zero */
2156				TIMELIB_CHECK_NUMBER;
2157				if ((s->time->h = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) {
2158					add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_HOUR, "A two digit hour could not be found", string, begin);
2159				}
2160				if (s->time->h > 12) {
2161					add_pbf_error(s, TIMELIB_ERR_HOUR_LARGER_THAN_12, "Hour can not be higher than 12", string, begin);
2162				}
2163				break;
2164			case TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX_PADDED: /* two digit hour, with leading zero */
2165			case TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX: /* two digit hour, without leading zero */
2166				TIMELIB_CHECK_NUMBER;
2167				if ((s->time->h = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) {
2168					add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_HOUR, "A two digit hour could not be found", string, begin);
2169				}
2170				break;
2171			case TIMELIB_FORMAT_MERIDIAN: /* am/pm/a.m./p.m. AM/PM/A.M./P.M. */
2172				if (s->time->h == TIMELIB_UNSET) {
2173					add_pbf_error(s, TIMELIB_ERR_MERIDIAN_BEFORE_HOUR, "Meridian can only come after an hour has been found", string, begin);
2174				} else if ((tmp = timelib_meridian_with_check((char **) &ptr, s->time->h)) == TIMELIB_UNSET) {
2175					add_pbf_error(s, TIMELIB_ERR_NO_MERIDIAN, "A meridian could not be found", string, begin);
2176				} else {
2177					s->time->h += tmp;
2178				}
2179				break;
2180			case TIMELIB_FORMAT_MINUTE_TWO_DIGIT: /* two digit minute, with leading zero */
2181				{
2182					int length;
2183					timelib_sll min;
2184
2185					TIMELIB_CHECK_NUMBER;
2186					min = timelib_get_nr_ex((char **) &ptr, 2, &length);
2187					if (min == TIMELIB_UNSET || length != 2) {
2188						add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_MINUTE, "A two digit minute could not be found", string, begin);
2189					} else {
2190						s->time->i = min;
2191					}
2192				}
2193				break;
2194			case TIMELIB_FORMAT_SECOND_TWO_DIGIT: /* two digit second, with leading zero */
2195				{
2196					int length;
2197					timelib_sll sec;
2198
2199					TIMELIB_CHECK_NUMBER;
2200					sec = timelib_get_nr_ex((char **) &ptr, 2, &length);
2201					if (sec == TIMELIB_UNSET || length != 2) {
2202						add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_SECOND, "A two digit second could not be found", string, begin);
2203					} else {
2204						s->time->s = sec;
2205					}
2206				}
2207				break;
2208			case TIMELIB_FORMAT_MICROSECOND_SIX_DIGIT: /* up to six digit microsecond */
2209				{
2210					double f;
2211					char *tptr;
2212
2213					TIMELIB_CHECK_NUMBER;
2214					tptr = ptr;
2215					if ((f = timelib_get_nr((char **) &ptr, 6)) == TIMELIB_UNSET || (ptr - tptr < 1)) {
2216						add_pbf_error(s, TIMELIB_ERR_NO_SIX_DIGIT_MICROSECOND, "A six digit microsecond could not be found", string, begin);
2217					} else {
2218						s->time->us = (f * pow(10, 6 - (ptr - tptr)));
2219					}
2220				}
2221				break;
2222			case TIMELIB_FORMAT_MILLISECOND_THREE_DIGIT: /* up to three digit millisecond */
2223				{
2224					double f;
2225					char *tptr;
2226
2227					TIMELIB_CHECK_NUMBER;
2228					tptr = ptr;
2229					if ((f = timelib_get_nr((char **) &ptr, 3)) == TIMELIB_UNSET || (ptr - tptr < 1)) {
2230						add_pbf_error(s, TIMELIB_ERR_NO_THREE_DIGIT_MILLISECOND, "A three digit millisecond could not be found", string, begin);
2231					} else {
2232						s->time->us = (f * pow(10, 3 - (ptr - tptr)) * 1000);
2233					}
2234				}
2235				break;
2236			case TIMELIB_FORMAT_WHITESPACE: /* any sort of whitespace (' ' and \t) */
2237				timelib_eat_spaces((char **) &ptr);
2238				break;
2239			case TIMELIB_FORMAT_EPOCH_SECONDS: /* epoch seconds */
2240				TIMELIB_CHECK_SIGNED_NUMBER;
2241				TIMELIB_HAVE_RELATIVE();
2242				tmp = timelib_get_unsigned_nr((char **) &ptr, 24);
2243				s->time->y = 1970;
2244				s->time->m = 1;
2245				s->time->d = 1;
2246				s->time->h = s->time->i = s->time->s = 0;
2247				s->time->relative.s += tmp;
2248				s->time->is_localtime = 1;
2249				s->time->zone_type = TIMELIB_ZONETYPE_OFFSET;
2250				s->time->z = 0;
2251				s->time->dst = 0;
2252				break;
2253			case TIMELIB_FORMAT_ANY_SEPARATOR: /* separation symbol */
2254				if (timelib_lookup_format(*ptr, format_map) == TIMELIB_FORMAT_SEPARATOR) {
2255					++ptr;
2256				} else {
2257					add_pbf_error(s, TIMELIB_ERR_NO_SEP_SYMBOL, "The separation symbol ([;:/.,-]) could not be found", string, begin);
2258				}
2259				break;
2260
2261			case TIMELIB_FORMAT_SEPARATOR:
2262				if (*ptr == *fptr) {
2263					++ptr;
2264				} else {
2265					add_pbf_error(s, TIMELIB_ERR_NO_SEP_SYMBOL, "The separation symbol could not be found", string, begin);
2266				}
2267				break;
2268
2269			case TIMELIB_FORMAT_RESET_ALL: /* reset all fields to default */
2270				timelib_time_reset_fields(s->time);
2271				break; /* break intentionally not missing */
2272
2273			case TIMELIB_FORMAT_RESET_ALL_WHEN_NOT_SET: /* reset all fields to default when not set */
2274				timelib_time_reset_unset_fields(s->time);
2275				break; /* break intentionally not missing */
2276
2277			case TIMELIB_FORMAT_RANDOM_CHAR: /* random char */
2278				++ptr;
2279				break;
2280
2281			case TIMELIB_FORMAT_ESCAPE: /* escaped char */
2282				if(!fptr[1]) {
2283					add_pbf_error(s, TIMELIB_ERR_EXPECTED_ESCAPE_CHAR, "Escaped character expected", string, begin);
2284					break;
2285				}
2286				fptr++;
2287				if (*ptr == *fptr) {
2288					++ptr;
2289				} else {
2290					add_pbf_error(s, TIMELIB_ERR_NO_ESCAPED_CHAR, "The escaped character could not be found", string, begin);
2291				}
2292				break;
2293
2294			case TIMELIB_FORMAT_SKIP_TO_SEPARATOR: /* random chars until a separator or number ([ \t.,:;/-0123456789]) */
2295				timelib_eat_until_separator((char **) &ptr);
2296				break;
2297
2298			case TIMELIB_FORMAT_ALLOW_EXTRA_CHARACTERS: /* allow extra chars in the format */
2299				allow_extra = true;
2300				break;
2301			case TIMELIB_FORMAT_YEAR_ISO:
2302				if ((iso_year = timelib_get_nr((char **) &ptr, 4)) == TIMELIB_UNSET) {
2303					add_pbf_error(s, TIMELIB_ERR_NO_FOUR_DIGIT_YEAR_ISO, "A four digit ISO year could not be found", string, begin);
2304				}
2305				break;
2306			case TIMELIB_FORMAT_WEEK_OF_YEAR_ISO:
2307				if ((iso_week_of_year = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) {
2308					add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_WEEK, "A two digit ISO week could not be found", string, begin);
2309				}
2310				/* Range is 1 - 53  for ISO week of year */
2311				if (iso_week_of_year < 1 || iso_week_of_year > 53) {
2312					add_pbf_error(s, TIMELIB_ERR_INVALID_WEEK, "ISO Week must be between 1 and 53", string, begin);
2313				}
2314				break;
2315			case TIMELIB_FORMAT_DAY_OF_WEEK_ISO:
2316				if ((iso_day_of_week = timelib_get_nr((char **) &ptr, 1)) == TIMELIB_UNSET) {
2317					add_pbf_error(s, TIMELIB_ERR_NO_DAY_OF_WEEK, "A single digit day of week could not be found", string, begin);
2318				}
2319				if (iso_day_of_week < 1 || iso_day_of_week > 7) {
2320					add_pbf_error(s, TIMELIB_ERR_INVALID_DAY_OF_WEEK, "Day of week must be between 1 and 7", string, begin);
2321				}
2322				break;
2323			case TIMELIB_FORMAT_TIMEZONE_OFFSET: /* timezone */
2324				{
2325					int tz_not_found;
2326
2327					s->time->z = timelib_parse_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
2328					if (tz_not_found) {
2329						add_pbf_error(s, TIMELIB_ERR_TZID_NOT_FOUND, "The timezone could not be found in the database", string, begin);
2330					}
2331				}
2332				break;
2333			case TIMELIB_FORMAT_TIMEZONE_OFFSET_MINUTES: /* timezone format +/-mmm */
2334				s->time->z = timelib_parse_tz_minutes((char **) &ptr, s->time);
2335				if (s->time->z == TIMELIB_UNSET) {
2336					add_pbf_error(s, TIMELIB_ERR_INVALID_TZ_OFFSET, "Invalid timezone offset in minutes", string, begin);
2337				}
2338				break;
2339			case TIMELIB_FORMAT_LITERAL:
2340			default:
2341				if (*fptr != *ptr) {
2342					add_pbf_error(s, TIMELIB_ERR_WRONG_FORMAT_SEP, "The format separator does not match", string, begin);
2343				}
2344				ptr++;
2345		}
2346		fptr++;
2347	}
2348	if (*ptr) {
2349		if (allow_extra) {
2350			add_pbf_warning(s, TIMELIB_WARN_TRAILING_DATA, "Trailing data", string, ptr);
2351		} else {
2352			add_pbf_error(s, TIMELIB_ERR_TRAILING_DATA, "Trailing data", string, ptr);
2353		}
2354	}
2355
2356	if (*fptr) {
2357		/* Trailing reset specifiers are valid. */
2358		int done = 0;
2359		while (*fptr && !done) {
2360			switch (timelib_lookup_format(*fptr, format_map)) {
2361				case TIMELIB_FORMAT_RESET_ALL: /* reset all fields to default */
2362					timelib_time_reset_fields(s->time);
2363					break;
2364
2365				case TIMELIB_FORMAT_RESET_ALL_WHEN_NOT_SET: /* reset all fields to default when not set */
2366					timelib_time_reset_unset_fields(s->time);
2367					break;
2368				case TIMELIB_FORMAT_ALLOW_EXTRA_CHARACTERS:
2369					break;
2370
2371				default:
2372					add_pbf_error(s, TIMELIB_ERR_DATA_MISSING, "Data missing", string, ptr);
2373					done = 1;
2374			}
2375			fptr++;
2376		}
2377	}
2378
2379	/* clean up a bit */
2380	if (s->time->h != TIMELIB_UNSET || s->time->i != TIMELIB_UNSET || s->time->s != TIMELIB_UNSET || s->time->us != TIMELIB_UNSET) {
2381		if (s->time->h == TIMELIB_UNSET ) {
2382			s->time->h = 0;
2383		}
2384		if (s->time->i == TIMELIB_UNSET ) {
2385			s->time->i = 0;
2386		}
2387		if (s->time->s == TIMELIB_UNSET ) {
2388			s->time->s = 0;
2389		}
2390		if (s->time->us == TIMELIB_UNSET ) {
2391			s->time->us = 0;
2392		}
2393	}
2394
2395	/* Check for mixing of ISO dates with natural dates. */
2396	if (s->time->y != TIMELIB_UNSET && (iso_week_of_year != TIMELIB_UNSET || iso_year != TIMELIB_UNSET || iso_day_of_week != 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	if (iso_year != TIMELIB_UNSET && (s->time->y != TIMELIB_UNSET || s->time->m != TIMELIB_UNSET || s->time->d != TIMELIB_UNSET)) {
2400		add_pbf_error(s, TIMELIB_ERR_MIX_ISO_WITH_NATURAL, "Mixing of ISO dates with natural dates is not allowed", string, ptr);
2401	}
2402
2403	/* Convert ISO values */
2404	if (iso_year != TIMELIB_UNSET) {
2405		/* Default week of year and day of week to 1. */
2406		if (iso_week_of_year == TIMELIB_UNSET) {
2407			iso_week_of_year = 1;
2408		}
2409		if (iso_day_of_week == TIMELIB_UNSET) {
2410			iso_day_of_week = 1;
2411		}
2412		timelib_date_from_isodate(iso_year, iso_week_of_year, iso_day_of_week, &s->time->y, &s->time->m, &s->time->d);
2413	} else if (iso_week_of_year != TIMELIB_UNSET || iso_day_of_week != TIMELIB_UNSET) {
2414		add_pbf_warning(s, TIMELIB_WARN_INVALID_DATE, "The parsed date was invalid", string, ptr);
2415	}
2416
2417	/* do funky checking whether the parsed time was valid time */
2418	if (s->time->h != TIMELIB_UNSET && s->time->i != TIMELIB_UNSET &&
2419		s->time->s != TIMELIB_UNSET &&
2420		!timelib_valid_time( s->time->h, s->time->i, s->time->s)) {
2421		add_pbf_warning(s, TIMELIB_WARN_INVALID_TIME, "The parsed time was invalid", string, ptr);
2422	}
2423	/* do funky checking whether the parsed date was valid date */
2424	if (s->time->y != TIMELIB_UNSET && s->time->m != TIMELIB_UNSET &&
2425		s->time->d != TIMELIB_UNSET &&
2426		!timelib_valid_date( s->time->y, s->time->m, s->time->d)) {
2427		add_pbf_warning(s, TIMELIB_WARN_INVALID_DATE, "The parsed date was invalid", string, ptr);
2428	}
2429
2430	if (errors) {
2431		*errors = in.errors;
2432	} else {
2433		timelib_error_container_dtor(in.errors);
2434	}
2435	return in.time;
2436}
2437
2438void timelib_fill_holes(timelib_time *parsed, timelib_time *now, int options)
2439{
2440	if (!(options & TIMELIB_OVERRIDE_TIME) && parsed->have_date && !parsed->have_time) {
2441		parsed->h = 0;
2442		parsed->i = 0;
2443		parsed->s = 0;
2444		parsed->us = 0;
2445	}
2446	if (
2447		parsed->y != TIMELIB_UNSET || parsed->m != TIMELIB_UNSET || parsed->d != TIMELIB_UNSET ||
2448		parsed->h != TIMELIB_UNSET || parsed->i != TIMELIB_UNSET || parsed->s != TIMELIB_UNSET
2449	) {
2450		if (parsed->us == TIMELIB_UNSET) parsed->us = 0;
2451	} else {
2452		if (parsed->us == TIMELIB_UNSET) parsed->us = now->us != TIMELIB_UNSET ? now->us : 0;
2453	}
2454	if (parsed->y == TIMELIB_UNSET) parsed->y = now->y != TIMELIB_UNSET ? now->y : 0;
2455	if (parsed->m == TIMELIB_UNSET) parsed->m = now->m != TIMELIB_UNSET ? now->m : 0;
2456	if (parsed->d == TIMELIB_UNSET) parsed->d = now->d != TIMELIB_UNSET ? now->d : 0;
2457	if (parsed->h == TIMELIB_UNSET) parsed->h = now->h != TIMELIB_UNSET ? now->h : 0;
2458	if (parsed->i == TIMELIB_UNSET) parsed->i = now->i != TIMELIB_UNSET ? now->i : 0;
2459	if (parsed->s == TIMELIB_UNSET) parsed->s = now->s != TIMELIB_UNSET ? now->s : 0;
2460	if (parsed->z == TIMELIB_UNSET) parsed->z = now->z != TIMELIB_UNSET ? now->z : 0;
2461	if (parsed->dst == TIMELIB_UNSET) parsed->dst = now->dst != TIMELIB_UNSET ? now->dst : 0;
2462
2463	if (!parsed->tz_abbr) {
2464		parsed->tz_abbr = now->tz_abbr ? timelib_strdup(now->tz_abbr) : NULL;
2465	}
2466	if (!parsed->tz_info) {
2467		parsed->tz_info = now->tz_info ? (!(options & TIMELIB_NO_CLONE) ? timelib_tzinfo_clone(now->tz_info) : now->tz_info) : NULL;
2468	}
2469	if (parsed->zone_type == 0 && now->zone_type != 0) {
2470		parsed->zone_type = now->zone_type;
2471/*		parsed->tz_abbr = now->tz_abbr ? timelib_strdup(now->tz_abbr) : NULL;
2472		parsed->tz_info = now->tz_info ? timelib_tzinfo_clone(now->tz_info) : NULL;
2473*/		parsed->is_localtime = 1;
2474	}
2475/*	timelib_dump_date(parsed, 2);
2476	timelib_dump_date(now, 2);
2477*/
2478}
2479
2480char *timelib_timezone_id_from_abbr(const char *abbr, timelib_long gmtoffset, int isdst)
2481{
2482	const timelib_tz_lookup_table *tp;
2483
2484	tp = abbr_search(abbr, gmtoffset, isdst);
2485	if (tp) {
2486		return (tp->full_tz_name);
2487	} else {
2488		return NULL;
2489	}
2490}
2491
2492const timelib_tz_lookup_table *timelib_timezone_abbreviations_list(void)
2493{
2494	return timelib_timezone_lookup;
2495}
2496
2497#ifdef DEBUG_PARSER_STUB
2498int main(void)
2499{
2500	timelib_time time = timelib_strtotime("May 12");
2501
2502	printf ("%04d-%02d-%02d %02d:%02d:%02d.%-5d %+04d %1d",
2503		time.y, time.m, time.d, time.h, time.i, time.s, time.f, time.z, time.dst);
2504	if (time.have_relative) {
2505		printf ("%3dY %3dM %3dD / %3dH %3dM %3dS",
2506			time.relative.y, time.relative.m, time.relative.d, time.relative.h, time.relative.i, time.relative.s);
2507	}
2508	if (time.have_weekday_relative) {
2509		printf (" / %d", time.relative.weekday);
2510	}
2511	if (time.have_weeknr_day) {
2512		printf(" / %dW%d", time.relative.weeknr_day.weeknr, time.relative.weeknr_day.dayofweek);
2513	}
2514	return 0;
2515}
2516#endif
2517
2518/*
2519 * vim: syntax=c
2520 */
2521