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