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