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