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