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