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