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