xref: /PHP-5.3/ext/date/lib/parse_date.re (revision 2326401f)
1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2013 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#define HOUR(a) (int)(a * 60)
172
173/* The timezone table. */
174const static timelib_tz_lookup_table timelib_timezone_lookup[] = {
175#include "timezonemap.h"
176	{ NULL, 0, 0, NULL },
177};
178
179const static timelib_tz_lookup_table timelib_timezone_fallbackmap[] = {
180#include "fallbackmap.h"
181	{ NULL, 0, 0, NULL },
182};
183
184const static timelib_tz_lookup_table timelib_timezone_utc[] = {
185	{ "utc", 0, 0, "UTC" },
186};
187
188static timelib_relunit const timelib_relunit_lookup[] = {
189	{ "sec",         TIMELIB_SECOND,  1 },
190	{ "secs",        TIMELIB_SECOND,  1 },
191	{ "second",      TIMELIB_SECOND,  1 },
192	{ "seconds",     TIMELIB_SECOND,  1 },
193	{ "min",         TIMELIB_MINUTE,  1 },
194	{ "mins",        TIMELIB_MINUTE,  1 },
195	{ "minute",      TIMELIB_MINUTE,  1 },
196	{ "minutes",     TIMELIB_MINUTE,  1 },
197	{ "hour",        TIMELIB_HOUR,    1 },
198	{ "hours",       TIMELIB_HOUR,    1 },
199	{ "day",         TIMELIB_DAY,     1 },
200	{ "days",        TIMELIB_DAY,     1 },
201	{ "week",        TIMELIB_DAY,     7 },
202	{ "weeks",       TIMELIB_DAY,     7 },
203	{ "fortnight",   TIMELIB_DAY,    14 },
204	{ "fortnights",  TIMELIB_DAY,    14 },
205	{ "forthnight",  TIMELIB_DAY,    14 },
206	{ "forthnights", TIMELIB_DAY,    14 },
207	{ "month",       TIMELIB_MONTH,   1 },
208	{ "months",      TIMELIB_MONTH,   1 },
209	{ "year",        TIMELIB_YEAR,    1 },
210	{ "years",       TIMELIB_YEAR,    1 },
211
212	{ "monday",      TIMELIB_WEEKDAY, 1 },
213	{ "mon",         TIMELIB_WEEKDAY, 1 },
214	{ "tuesday",     TIMELIB_WEEKDAY, 2 },
215	{ "tue",         TIMELIB_WEEKDAY, 2 },
216	{ "wednesday",   TIMELIB_WEEKDAY, 3 },
217	{ "wed",         TIMELIB_WEEKDAY, 3 },
218	{ "thursday",    TIMELIB_WEEKDAY, 4 },
219	{ "thu",         TIMELIB_WEEKDAY, 4 },
220	{ "friday",      TIMELIB_WEEKDAY, 5 },
221	{ "fri",         TIMELIB_WEEKDAY, 5 },
222	{ "saturday",    TIMELIB_WEEKDAY, 6 },
223	{ "sat",         TIMELIB_WEEKDAY, 6 },
224	{ "sunday",      TIMELIB_WEEKDAY, 0 },
225	{ "sun",         TIMELIB_WEEKDAY, 0 },
226
227	{ "weekday",     TIMELIB_SPECIAL, TIMELIB_SPECIAL_WEEKDAY },
228	{ "weekdays",    TIMELIB_SPECIAL, TIMELIB_SPECIAL_WEEKDAY },
229	{ NULL,          0,          0 }
230};
231
232/* The relative text table. */
233static timelib_lookup_table const timelib_reltext_lookup[] = {
234	{ "first",    0,  1 },
235	{ "next",     0,  1 },
236	{ "second",   0,  2 },
237	{ "third",    0,  3 },
238	{ "fourth",   0,  4 },
239	{ "fifth",    0,  5 },
240	{ "sixth",    0,  6 },
241	{ "seventh",  0,  7 },
242	{ "eight",    0,  8 },
243	{ "eighth",   0,  8 },
244	{ "ninth",    0,  9 },
245	{ "tenth",    0, 10 },
246	{ "eleventh", 0, 11 },
247	{ "twelfth",  0, 12 },
248	{ "last",     0, -1 },
249	{ "previous", 0, -1 },
250	{ "this",     1,  0 },
251	{ NULL,       1,  0 }
252};
253
254/* The month table. */
255static timelib_lookup_table const timelib_month_lookup[] = {
256	{ "jan",  0,  1 },
257	{ "feb",  0,  2 },
258	{ "mar",  0,  3 },
259	{ "apr",  0,  4 },
260	{ "may",  0,  5 },
261	{ "jun",  0,  6 },
262	{ "jul",  0,  7 },
263	{ "aug",  0,  8 },
264	{ "sep",  0,  9 },
265	{ "sept", 0,  9 },
266	{ "oct",  0, 10 },
267	{ "nov",  0, 11 },
268	{ "dec",  0, 12 },
269	{ "i",    0,  1 },
270	{ "ii",   0,  2 },
271	{ "iii",  0,  3 },
272	{ "iv",   0,  4 },
273	{ "v",    0,  5 },
274	{ "vi",   0,  6 },
275	{ "vii",  0,  7 },
276	{ "viii", 0,  8 },
277	{ "ix",   0,  9 },
278	{ "x",    0, 10 },
279	{ "xi",   0, 11 },
280	{ "xii",  0, 12 },
281
282	{ "january",   0,  1 },
283	{ "february",  0,  2 },
284	{ "march",     0,  3 },
285	{ "april",     0,  4 },
286	{ "may",       0,  5 },
287	{ "june",      0,  6 },
288	{ "july",      0,  7 },
289	{ "august",    0,  8 },
290	{ "september", 0,  9 },
291	{ "october",   0, 10 },
292	{ "november",  0, 11 },
293	{ "december",  0, 12 },
294	{  NULL,       0,  0 }
295};
296
297#if 0
298static char* timelib_ltrim(char *s)
299{
300	char *ptr = s;
301	while (ptr[0] == ' ' || ptr[0] == '\t') {
302		ptr++;
303	}
304	return ptr;
305}
306#endif
307
308#if 0
309uchar *fill(Scanner *s, uchar *cursor){
310	if(!s->eof){
311		unsigned int cnt = s->tok - s->bot;
312		if(cnt){
313			memcpy(s->bot, s->tok, s->lim - s->tok);
314			s->tok = s->bot;
315			s->ptr -= cnt;
316			cursor -= cnt;
317			s->pos -= cnt;
318			s->lim -= cnt;
319		}
320		if((s->top - s->lim) < BSIZE){
321			uchar *buf = (uchar*) malloc(((s->lim - s->bot) + BSIZE)*sizeof(uchar));
322			memcpy(buf, s->tok, s->lim - s->tok);
323			s->tok = buf;
324			s->ptr = &buf[s->ptr - s->bot];
325			cursor = &buf[cursor - s->bot];
326			s->pos = &buf[s->pos - s->bot];
327			s->lim = &buf[s->lim - s->bot];
328			s->top = &s->lim[BSIZE];
329			free(s->bot);
330			s->bot = buf;
331		}
332		if((cnt = read(s->fd, (char*) s->lim, BSIZE)) != BSIZE){
333			s->eof = &s->lim[cnt]; *(s->eof)++ = '\n';
334		}
335		s->lim += cnt;
336	}
337	return cursor;
338}
339#endif
340
341static void add_warning(Scanner *s, char *error)
342{
343	s->errors->warning_count++;
344	s->errors->warning_messages = realloc(s->errors->warning_messages, s->errors->warning_count * sizeof(timelib_error_message));
345	s->errors->warning_messages[s->errors->warning_count - 1].position = s->tok ? s->tok - s->str : 0;
346	s->errors->warning_messages[s->errors->warning_count - 1].character = s->tok ? *s->tok : 0;
347	s->errors->warning_messages[s->errors->warning_count - 1].message = strdup(error);
348}
349
350static void add_error(Scanner *s, char *error)
351{
352	s->errors->error_count++;
353	s->errors->error_messages = realloc(s->errors->error_messages, s->errors->error_count * sizeof(timelib_error_message));
354	s->errors->error_messages[s->errors->error_count - 1].position = s->tok ? s->tok - s->str : 0;
355	s->errors->error_messages[s->errors->error_count - 1].character = s->tok ? *s->tok : 0;
356	s->errors->error_messages[s->errors->error_count - 1].message = strdup(error);
357}
358
359static void add_pbf_warning(Scanner *s, char *error, char *sptr, char *cptr)
360{
361	s->errors->warning_count++;
362	s->errors->warning_messages = realloc(s->errors->warning_messages, s->errors->warning_count * sizeof(timelib_error_message));
363	s->errors->warning_messages[s->errors->warning_count - 1].position = cptr - sptr;
364	s->errors->warning_messages[s->errors->warning_count - 1].character = *cptr;
365	s->errors->warning_messages[s->errors->warning_count - 1].message = strdup(error);
366}
367
368static void add_pbf_error(Scanner *s, char *error, char *sptr, char *cptr)
369{
370	s->errors->error_count++;
371	s->errors->error_messages = realloc(s->errors->error_messages, s->errors->error_count * sizeof(timelib_error_message));
372	s->errors->error_messages[s->errors->error_count - 1].position = cptr - sptr;
373	s->errors->error_messages[s->errors->error_count - 1].character = *cptr;
374	s->errors->error_messages[s->errors->error_count - 1].message = strdup(error);
375}
376
377static timelib_sll timelib_meridian(char **ptr, timelib_sll h)
378{
379	timelib_sll retval = 0;
380
381	while (!strchr("AaPp", **ptr)) {
382		++*ptr;
383	}
384	if (**ptr == 'a' || **ptr == 'A') {
385		if (h == 12) {
386			retval = -12;
387		}
388	} else if (h != 12) {
389		retval = 12;
390	}
391	++*ptr;
392	if (**ptr == '.') {
393		*ptr += 3;
394	} else {
395		++*ptr;
396	}
397	return retval;
398}
399
400static timelib_sll timelib_meridian_with_check(char **ptr, timelib_sll h)
401{
402	timelib_sll retval = 0;
403
404	while (**ptr && !strchr("AaPp", **ptr)) {
405		++*ptr;
406	}
407    if(!**ptr) {
408        return TIMELIB_UNSET;
409    }
410	if (**ptr == 'a' || **ptr == 'A') {
411		if (h == 12) {
412			retval = -12;
413		}
414	} else if (h != 12) {
415		retval = 12;
416	}
417	++*ptr;
418	if (**ptr == '.') {
419		++*ptr;
420		if (**ptr != 'm' && **ptr != 'M') {
421			return TIMELIB_UNSET;
422		}
423		++*ptr;
424		if (**ptr != '.' ) {
425			return TIMELIB_UNSET;
426		}
427		++*ptr;
428	} else if (**ptr == 'm' || **ptr == 'M') {
429		++*ptr;
430	} else {
431		return TIMELIB_UNSET;
432	}
433	return retval;
434}
435
436static char *timelib_string(Scanner *s)
437{
438	char *tmp = calloc(1, s->cur - s->tok + 1);
439	memcpy(tmp, s->tok, s->cur - s->tok);
440
441	return tmp;
442}
443
444static timelib_sll timelib_get_nr_ex(char **ptr, int max_length, int *scanned_length)
445{
446	char *begin, *end, *str;
447	timelib_sll tmp_nr = TIMELIB_UNSET;
448	int len = 0;
449
450	while ((**ptr < '0') || (**ptr > '9')) {
451		if (**ptr == '\0') {
452			return TIMELIB_UNSET;
453		}
454		++*ptr;
455	}
456	begin = *ptr;
457	while ((**ptr >= '0') && (**ptr <= '9') && len < max_length) {
458		++*ptr;
459		++len;
460	}
461	end = *ptr;
462	if (scanned_length) {
463		*scanned_length = end - begin;
464	}
465	str = calloc(1, end - begin + 1);
466	memcpy(str, begin, end - begin);
467	tmp_nr = strtoll(str, NULL, 10);
468	free(str);
469	return tmp_nr;
470}
471
472static timelib_sll timelib_get_nr(char **ptr, int max_length)
473{
474	return timelib_get_nr_ex(ptr, max_length, NULL);
475}
476
477static void timelib_skip_day_suffix(char **ptr)
478{
479	if (isspace(**ptr)) {
480		return;
481	}
482	if (!strncasecmp(*ptr, "nd", 2) || !strncasecmp(*ptr, "rd", 2) ||!strncasecmp(*ptr, "st", 2) || !strncasecmp(*ptr, "th", 2)) {
483		*ptr += 2;
484	}
485}
486
487static double timelib_get_frac_nr(char **ptr, int max_length)
488{
489	char *begin, *end, *str;
490	double tmp_nr = TIMELIB_UNSET;
491	int len = 0;
492
493	while ((**ptr != '.') && (**ptr != ':') && ((**ptr < '0') || (**ptr > '9'))) {
494		if (**ptr == '\0') {
495			return TIMELIB_UNSET;
496		}
497		++*ptr;
498	}
499	begin = *ptr;
500	while (((**ptr == '.') || (**ptr == ':') || ((**ptr >= '0') && (**ptr <= '9'))) && len < max_length) {
501		++*ptr;
502		++len;
503	}
504	end = *ptr;
505	str = calloc(1, end - begin + 1);
506	memcpy(str, begin, end - begin);
507	if (str[0] == ':') {
508		str[0] = '.';
509	}
510	tmp_nr = strtod(str, NULL);
511	free(str);
512	return tmp_nr;
513}
514
515static timelib_ull timelib_get_unsigned_nr(char **ptr, int max_length)
516{
517	timelib_ull dir = 1;
518
519	while (((**ptr < '0') || (**ptr > '9')) && (**ptr != '+') && (**ptr != '-')) {
520		if (**ptr == '\0') {
521			return TIMELIB_UNSET;
522		}
523		++*ptr;
524	}
525
526	while (**ptr == '+' || **ptr == '-')
527	{
528		if (**ptr == '-') {
529			dir *= -1;
530		}
531		++*ptr;
532	}
533	return dir * timelib_get_nr(ptr, max_length);
534}
535
536static long timelib_parse_tz_cor(char **ptr)
537{
538	char *begin = *ptr, *end;
539	long  tmp;
540
541	while (isdigit(**ptr) || **ptr == ':') {
542		++*ptr;
543	}
544	end = *ptr;
545	switch (end - begin) {
546		case 1:
547		case 2:
548			return HOUR(strtol(begin, NULL, 10));
549			break;
550		case 3:
551		case 4:
552			if (begin[1] == ':') {
553				tmp = HOUR(strtol(begin, NULL, 10)) + strtol(begin + 2, NULL, 10);
554				return tmp;
555			} else if (begin[2] == ':') {
556				tmp = HOUR(strtol(begin, NULL, 10)) + strtol(begin + 3, NULL, 10);
557				return tmp;
558			} else {
559				tmp = strtol(begin, NULL, 10);
560				return HOUR(tmp / 100) + tmp % 100;
561			}
562		case 5:
563			tmp = HOUR(strtol(begin, NULL, 10)) + strtol(begin + 3, NULL, 10);
564			return tmp;
565	}
566	return 0;
567}
568
569static timelib_sll timelib_lookup_relative_text(char **ptr, int *behavior)
570{
571	char *word;
572	char *begin = *ptr, *end;
573	timelib_sll  value = 0;
574	const timelib_lookup_table *tp;
575
576	while ((**ptr >= 'A' && **ptr <= 'Z') || (**ptr >= 'a' && **ptr <= 'z')) {
577		++*ptr;
578	}
579	end = *ptr;
580	word = calloc(1, end - begin + 1);
581	memcpy(word, begin, end - begin);
582
583	for (tp = timelib_reltext_lookup; tp->name; tp++) {
584		if (strcasecmp(word, tp->name) == 0) {
585			value = tp->value;
586			*behavior = tp->type;
587		}
588	}
589
590	free(word);
591	return value;
592}
593
594static timelib_sll timelib_get_relative_text(char **ptr, int *behavior)
595{
596	while (**ptr == ' ' || **ptr == '\t' || **ptr == '-' || **ptr == '/') {
597		++*ptr;
598	}
599	return timelib_lookup_relative_text(ptr, behavior);
600}
601
602static long timelib_lookup_month(char **ptr)
603{
604	char *word;
605	char *begin = *ptr, *end;
606	long  value = 0;
607	const timelib_lookup_table *tp;
608
609	while ((**ptr >= 'A' && **ptr <= 'Z') || (**ptr >= 'a' && **ptr <= 'z')) {
610		++*ptr;
611	}
612	end = *ptr;
613	word = calloc(1, end - begin + 1);
614	memcpy(word, begin, end - begin);
615
616	for (tp = timelib_month_lookup; tp->name; tp++) {
617		if (strcasecmp(word, tp->name) == 0) {
618			value = tp->value;
619		}
620	}
621
622	free(word);
623	return value;
624}
625
626static long timelib_get_month(char **ptr)
627{
628	while (**ptr == ' ' || **ptr == '\t' || **ptr == '-' || **ptr == '.' || **ptr == '/') {
629		++*ptr;
630	}
631	return timelib_lookup_month(ptr);
632}
633
634static void timelib_eat_spaces(char **ptr)
635{
636	while (**ptr == ' ' || **ptr == '\t') {
637		++*ptr;
638	}
639}
640
641static void timelib_eat_until_separator(char **ptr)
642{
643	++*ptr;
644	while (strchr(" \t.,:;/-0123456789", **ptr) == NULL) {
645		++*ptr;
646	}
647}
648
649static const timelib_relunit* timelib_lookup_relunit(char **ptr)
650{
651	char *word;
652	char *begin = *ptr, *end;
653	const timelib_relunit *tp, *value = NULL;
654
655	while (**ptr != '\0' && **ptr != ' ' && **ptr != ',' && **ptr != '\t') {
656		++*ptr;
657	}
658	end = *ptr;
659	word = calloc(1, end - begin + 1);
660	memcpy(word, begin, end - begin);
661
662	for (tp = timelib_relunit_lookup; tp->name; tp++) {
663		if (strcasecmp(word, tp->name) == 0) {
664			value = tp;
665			break;
666		}
667	}
668
669	free(word);
670	return value;
671}
672
673static void timelib_set_relative(char **ptr, timelib_sll amount, int behavior, Scanner *s)
674{
675	const timelib_relunit* relunit;
676
677	if (!(relunit = timelib_lookup_relunit(ptr))) {
678		return;
679	}
680
681	switch (relunit->unit) {
682		case TIMELIB_SECOND: s->time->relative.s += amount * relunit->multiplier; break;
683		case TIMELIB_MINUTE: s->time->relative.i += amount * relunit->multiplier; break;
684		case TIMELIB_HOUR:   s->time->relative.h += amount * relunit->multiplier; break;
685		case TIMELIB_DAY:    s->time->relative.d += amount * relunit->multiplier; break;
686		case TIMELIB_MONTH:  s->time->relative.m += amount * relunit->multiplier; break;
687		case TIMELIB_YEAR:   s->time->relative.y += amount * relunit->multiplier; break;
688
689		case TIMELIB_WEEKDAY:
690			TIMELIB_HAVE_WEEKDAY_RELATIVE();
691			TIMELIB_UNHAVE_TIME();
692			s->time->relative.d += (amount > 0 ? amount - 1 : amount) * 7;
693			s->time->relative.weekday = relunit->multiplier;
694			s->time->relative.weekday_behavior = behavior;
695			break;
696
697		case TIMELIB_SPECIAL:
698			TIMELIB_HAVE_SPECIAL_RELATIVE();
699			TIMELIB_UNHAVE_TIME();
700			s->time->relative.special.type = relunit->multiplier;
701			s->time->relative.special.amount = amount;
702	}
703}
704
705const static timelib_tz_lookup_table* zone_search(const char *word, long gmtoffset, int isdst)
706{
707	int first_found = 0;
708	const timelib_tz_lookup_table  *tp, *first_found_elem = NULL;
709	const timelib_tz_lookup_table  *fmp;
710
711	if (strcasecmp("utc", word) == 0 || strcasecmp("gmt", word) == 0) {
712		return timelib_timezone_utc;
713	}
714
715	for (tp = timelib_timezone_lookup; tp->name; tp++) {
716		if (strcasecmp(word, tp->name) == 0) {
717			if (!first_found) {
718				first_found = 1;
719				first_found_elem = tp;
720				if (gmtoffset == -1) {
721					return tp;
722				}
723			}
724			if (tp->gmtoffset == gmtoffset) {
725				return tp;
726			}
727		}
728	}
729	if (first_found) {
730		return first_found_elem;
731	}
732
733	for (tp = timelib_timezone_lookup; tp->name; tp++) {
734		if (tp->full_tz_name && strcasecmp(word, tp->full_tz_name) == 0) {
735			if (!first_found) {
736				first_found = 1;
737				first_found_elem = tp;
738				if (gmtoffset == -1) {
739					return tp;
740				}
741			}
742			if (tp->gmtoffset == gmtoffset) {
743				return tp;
744			}
745		}
746	}
747	if (first_found) {
748		return first_found_elem;
749	}
750
751
752	/* Still didn't find anything, let's find the zone solely based on
753	 * offset/isdst then */
754	for (fmp = timelib_timezone_fallbackmap; fmp->name; fmp++) {
755		if ((fmp->gmtoffset * 3600) == gmtoffset && fmp->type == isdst) {
756			return fmp;
757		}
758	}
759	return NULL;
760}
761
762static long timelib_lookup_zone(char **ptr, int *dst, char **tz_abbr, int *found)
763{
764	char *word;
765	char *begin = *ptr, *end;
766	long  value = 0;
767	const timelib_tz_lookup_table *tp;
768
769	while (**ptr != '\0' && **ptr != ')' && **ptr != ' ') {
770		++*ptr;
771	}
772	end = *ptr;
773	word = calloc(1, end - begin + 1);
774	memcpy(word, begin, end - begin);
775
776	if ((tp = zone_search(word, -1, 0))) {
777		value = -tp->gmtoffset / 60;
778		*dst = tp->type;
779		value += tp->type * 60;
780		*found = 1;
781	} else {
782		*found = 0;
783	}
784
785	*tz_abbr = word;
786	return value;
787}
788
789static long timelib_get_zone(char **ptr, int *dst, timelib_time *t, int *tz_not_found, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_wrapper)
790{
791	timelib_tzinfo *res;
792	long            retval = 0;
793
794	*tz_not_found = 0;
795
796	while (**ptr == ' ' || **ptr == '\t' || **ptr == '(') {
797		++*ptr;
798	}
799	if ((*ptr)[0] == 'G' && (*ptr)[1] == 'M' && (*ptr)[2] == 'T' && ((*ptr)[3] == '+' || (*ptr)[3] == '-')) {
800		*ptr += 3;
801	}
802	if (**ptr == '+') {
803		++*ptr;
804		t->is_localtime = 1;
805		t->zone_type = TIMELIB_ZONETYPE_OFFSET;
806		*tz_not_found = 0;
807		t->dst = 0;
808
809		retval = -1 * timelib_parse_tz_cor(ptr);
810	} else if (**ptr == '-') {
811		++*ptr;
812		t->is_localtime = 1;
813		t->zone_type = TIMELIB_ZONETYPE_OFFSET;
814		*tz_not_found = 0;
815		t->dst = 0;
816
817		retval = timelib_parse_tz_cor(ptr);
818	} else {
819		int found = 0;
820		long offset;
821		char *tz_abbr;
822
823		t->is_localtime = 1;
824
825		offset = timelib_lookup_zone(ptr, dst, &tz_abbr, &found);
826		if (found) {
827			t->zone_type = TIMELIB_ZONETYPE_ABBR;
828		}
829#if 0
830		/* If we found a TimeZone identifier, use it */
831		if (tz_name) {
832			t->tz_info = timelib_parse_tzfile(tz_name);
833			t->zone_type = TIMELIB_ZONETYPE_ID;
834		}
835#endif
836		/* If we have a TimeZone identifier to start with, use it */
837		if (strstr(tz_abbr, "/") || strcmp(tz_abbr, "UTC") == 0) {
838			if ((res = tz_wrapper(tz_abbr, tzdb)) != NULL) {
839				t->tz_info = res;
840				t->zone_type = TIMELIB_ZONETYPE_ID;
841				found++;
842			}
843		}
844		if (found && t->zone_type != TIMELIB_ZONETYPE_ID) {
845			timelib_time_tz_abbr_update(t, tz_abbr);
846		}
847		free(tz_abbr);
848		*tz_not_found = (found == 0);
849		retval = offset;
850	}
851	while (**ptr == ')') {
852		++*ptr;
853	}
854	return retval;
855}
856
857#define timelib_split_free(arg) {       \
858	int i;                         \
859	for (i = 0; i < arg.c; i++) {  \
860		free(arg.v[i]);            \
861	}                              \
862	if (arg.v) {                   \
863		free(arg.v);               \
864	}                              \
865}
866
867static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
868{
869	uchar *cursor = s->cur;
870	char *str, *ptr = NULL;
871
872std:
873	s->tok = cursor;
874	s->len = 0;
875/*!re2c
876any = [\000-\377];
877
878space = [ \t]+;
879frac = "."[0-9]+;
880
881ago = 'ago';
882
883hour24 = [01]?[0-9] | "2"[0-4];
884hour24lz = [01][0-9] | "2"[0-4];
885hour12 = "0"?[1-9] | "1"[0-2];
886minute = [0-5]?[0-9];
887minutelz = [0-5][0-9];
888second = minute | "60";
889secondlz = minutelz | "60";
890meridian = ([AaPp] "."? [Mm] "."?) [\000\t ];
891tz = "("? [A-Za-z]{1,6} ")"? | [A-Z][a-z]+([_/-][A-Za-z]+)+;
892tzcorrection = "GMT"? [+-] hour24 ":"? minute?;
893
894daysuf = "st" | "nd" | "rd" | "th";
895
896month = "0"? [0-9] | "1"[0-2];
897day   = (([0-2]?[0-9]) | ("3"[01])) daysuf?;
898year  = [0-9]{1,4};
899year2 = [0-9]{2};
900year4 = [0-9]{4};
901year4withsign = [+-]? [0-9]{4};
902
903dayofyear = "00"[1-9] | "0"[1-9][0-9] | [1-2][0-9][0-9] | "3"[0-5][0-9] | "36"[0-6];
904weekofyear = "0"[1-9] | [1-4][0-9] | "5"[0-3];
905
906monthlz = "0" [0-9] | "1" [0-2];
907daylz   = "0" [0-9] | [1-2][0-9] | "3" [01];
908
909dayfull = 'sunday' | 'monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday';
910dayabbr = 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat' | 'sun';
911dayspecial = 'weekday' | 'weekdays';
912daytext = dayfull | dayabbr | dayspecial;
913
914monthfull = 'january' | 'february' | 'march' | 'april' | 'may' | 'june' | 'july' | 'august' | 'september' | 'october' | 'november' | 'december';
915monthabbr = 'jan' | 'feb' | 'mar' | 'apr' | 'may' | 'jun' | 'jul' | 'aug' | 'sep' | 'sept' | 'oct' | 'nov' | 'dec';
916monthroman = "I" | "II" | "III" | "IV" | "V" | "VI" | "VII" | "VIII" | "IX" | "X" | "XI" | "XII";
917monthtext = monthfull | monthabbr | monthroman;
918
919/* Time formats */
920timetiny12 = hour12 space? meridian;
921timeshort12 = hour12[:.]minutelz space? meridian;
922timelong12 = hour12[:.]minute[:.]secondlz space? meridian;
923
924timeshort24 = 't'? hour24[:.]minute;
925timelong24 =  't'? hour24[:.]minute[:.]second;
926iso8601long =  't'? hour24 [:.] minute [:.] second frac;
927
928/* iso8601shorttz = hour24 [:] minutelz space? (tzcorrection | tz); */
929iso8601normtz =  't'? hour24 [:.] minute [:.] secondlz space? (tzcorrection | tz);
930/* iso8601longtz =  hour24 [:] minute [:] secondlz frac space? (tzcorrection | tz); */
931
932gnunocolon       = 't'? hour24lz minutelz;
933/* gnunocolontz     = hour24lz minutelz space? (tzcorrection | tz); */
934iso8601nocolon   = 't'? hour24lz minutelz secondlz;
935/* iso8601nocolontz = hour24lz minutelz secondlz space? (tzcorrection | tz); */
936
937/* Date formats */
938americanshort    = month "/" day;
939american         = month "/" day "/" year;
940iso8601dateslash = year4 "/" monthlz "/" daylz "/"?;
941dateslash        = year4 "/" month "/" day;
942iso8601date4     = year4withsign "-" monthlz "-" daylz;
943iso8601date2     = year2 "-" monthlz "-" daylz;
944gnudateshorter   = year4 "-" month;
945gnudateshort     = year "-" month "-" day;
946pointeddate4     = day [.\t-] month [.-] year4;
947pointeddate2     = day [.\t] month "." year2;
948datefull         = day ([ \t.-])* monthtext ([ \t.-])* year;
949datenoday        = monthtext ([ .\t-])* year4;
950datenodayrev     = year4 ([ .\t-])* monthtext;
951datetextual      = monthtext ([ .\t-])* day [,.stndrh\t ]+ year;
952datenoyear       = monthtext ([ .\t-])* day [,.stndrh\t ]*;
953datenoyearrev    = day ([ .\t-])* monthtext;
954datenocolon      = year4 monthlz daylz;
955
956/* Special formats */
957soap             = year4 "-" monthlz "-" daylz "T" hour24lz ":" minutelz ":" secondlz frac tzcorrection?;
958xmlrpc           = year4 monthlz daylz "T" hour24 ":" minutelz ":" secondlz;
959xmlrpcnocolon    = year4 monthlz daylz 't' hour24 minutelz secondlz;
960wddx             = year4 "-" month "-" day "T" hour24 ":" minute ":" second;
961pgydotd          = year4 "."? dayofyear;
962pgtextshort      = monthabbr "-" daylz "-" year;
963pgtextreverse    = year "-" monthabbr "-" daylz;
964mssqltime        = hour12 ":" minutelz ":" secondlz [:.] [0-9]+ meridian;
965isoweekday       = year4 "-"? "W" weekofyear "-"? [0-7];
966isoweek          = year4 "-"? "W" weekofyear;
967exif             = year4 ":" monthlz ":" daylz " " hour24lz ":" minutelz ":" secondlz;
968firstdayof       = 'first day of'?;
969lastdayof        = 'last day of'?;
970backof           = 'back of ' hour24 space? meridian?;
971frontof          = 'front of ' hour24 space? meridian?;
972
973/* Common Log Format: 10/Oct/2000:13:55:36 -0700 */
974clf              = day "/" monthabbr "/" year4 ":" hour24lz ":" minutelz ":" secondlz space tzcorrection;
975
976/* Timestamp format: @1126396800 */
977timestamp        = "@" "-"? [0-9]+;
978
979/* To fix some ambiguities */
980dateshortwithtimeshort12  = datenoyear timeshort12;
981dateshortwithtimelong12   = datenoyear timelong12;
982dateshortwithtimeshort  = datenoyear timeshort24;
983dateshortwithtimelong   = datenoyear timelong24;
984dateshortwithtimelongtz = datenoyear iso8601normtz;
985
986/*
987 * Relative regexps
988 */
989reltextnumber = 'first'|'second'|'third'|'fourth'|'fifth'|'sixth'|'seventh'|'eight'|'eighth'|'ninth'|'tenth'|'eleventh'|'twelfth';
990reltexttext = 'next'|'last'|'previous'|'this';
991reltextunit = (('sec'|'second'|'min'|'minute'|'hour'|'day'|'fortnight'|'forthnight'|'month'|'year') 's'?) | 'weeks' | daytext;
992
993relnumber = ([+-]*[ \t]*[0-9]+);
994relative = relnumber space? (reltextunit | 'week' );
995relativetext = (reltextnumber|reltexttext) space reltextunit;
996relativetextweek = reltexttext space 'week';
997
998weekdayof        = (reltextnumber|reltexttext) space (dayfull|dayabbr) space 'of';
999
1000*/
1001
1002/*!re2c
1003	/* so that vim highlights correctly */
1004	'yesterday'
1005	{
1006		DEBUG_OUTPUT("yesterday");
1007		TIMELIB_INIT;
1008		TIMELIB_HAVE_RELATIVE();
1009		TIMELIB_UNHAVE_TIME();
1010
1011		s->time->relative.d = -1;
1012		TIMELIB_DEINIT;
1013		return TIMELIB_RELATIVE;
1014	}
1015
1016	'now'
1017	{
1018		DEBUG_OUTPUT("now");
1019		TIMELIB_INIT;
1020
1021		TIMELIB_DEINIT;
1022		return TIMELIB_RELATIVE;
1023	}
1024
1025	'noon'
1026	{
1027		DEBUG_OUTPUT("noon");
1028		TIMELIB_INIT;
1029		TIMELIB_UNHAVE_TIME();
1030		TIMELIB_HAVE_TIME();
1031		s->time->h = 12;
1032
1033		TIMELIB_DEINIT;
1034		return TIMELIB_RELATIVE;
1035	}
1036
1037	'midnight' | 'today'
1038	{
1039		DEBUG_OUTPUT("midnight | today");
1040		TIMELIB_INIT;
1041		TIMELIB_UNHAVE_TIME();
1042
1043		TIMELIB_DEINIT;
1044		return TIMELIB_RELATIVE;
1045	}
1046
1047	'tomorrow'
1048	{
1049		DEBUG_OUTPUT("tomorrow");
1050		TIMELIB_INIT;
1051		TIMELIB_HAVE_RELATIVE();
1052		TIMELIB_UNHAVE_TIME();
1053
1054		s->time->relative.d = 1;
1055		TIMELIB_DEINIT;
1056		return TIMELIB_RELATIVE;
1057	}
1058
1059	timestamp
1060	{
1061		timelib_ull i;
1062
1063		TIMELIB_INIT;
1064		TIMELIB_HAVE_RELATIVE();
1065		TIMELIB_UNHAVE_DATE();
1066		TIMELIB_UNHAVE_TIME();
1067		TIMELIB_HAVE_TZ();
1068
1069		i = timelib_get_unsigned_nr((char **) &ptr, 24);
1070		s->time->y = 1970;
1071		s->time->m = 1;
1072		s->time->d = 1;
1073		s->time->h = s->time->i = s->time->s = 0;
1074		s->time->f = 0.0;
1075		s->time->relative.s += i;
1076		s->time->is_localtime = 1;
1077		s->time->zone_type = TIMELIB_ZONETYPE_OFFSET;
1078		s->time->z = 0;
1079
1080		TIMELIB_DEINIT;
1081		return TIMELIB_RELATIVE;
1082	}
1083
1084	firstdayof | lastdayof
1085	{
1086		DEBUG_OUTPUT("firstdayof | lastdayof");
1087		TIMELIB_INIT;
1088		TIMELIB_HAVE_RELATIVE();
1089
1090		/* skip "last day of" or "first day of" */
1091		if (*ptr == 'l') {
1092			s->time->relative.first_last_day_of = 2;
1093		} else {
1094			s->time->relative.first_last_day_of = 1;
1095		}
1096
1097		TIMELIB_DEINIT;
1098		return TIMELIB_LF_DAY_OF_MONTH;
1099	}
1100
1101	backof | frontof
1102	{
1103		DEBUG_OUTPUT("backof | frontof");
1104		TIMELIB_INIT;
1105		TIMELIB_UNHAVE_TIME();
1106		TIMELIB_HAVE_TIME();
1107
1108		if (*ptr == 'b') {
1109			s->time->h = timelib_get_nr((char **) &ptr, 2);
1110			s->time->i = 15;
1111		} else {
1112			s->time->h = timelib_get_nr((char **) &ptr, 2) - 1;
1113			s->time->i = 45;
1114		}
1115		if (*ptr != '\0' ) {
1116			timelib_eat_spaces((char **) &ptr);
1117			s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1118		}
1119
1120		TIMELIB_DEINIT;
1121		return TIMELIB_LF_DAY_OF_MONTH;
1122	}
1123
1124	weekdayof
1125	{
1126		timelib_sll i;
1127		int         behavior = 0;
1128		DEBUG_OUTPUT("weekdayof");
1129		TIMELIB_INIT;
1130		TIMELIB_HAVE_RELATIVE();
1131		TIMELIB_HAVE_SPECIAL_RELATIVE();
1132
1133		i = timelib_get_relative_text((char **) &ptr, &behavior);
1134		timelib_eat_spaces((char **) &ptr);
1135		if (i > 0) { /* first, second... etc */
1136			s->time->relative.special.type = TIMELIB_SPECIAL_DAY_OF_WEEK_IN_MONTH;
1137			timelib_set_relative((char **) &ptr, i, 1, s);
1138		} else { /* last */
1139			s->time->relative.special.type = TIMELIB_SPECIAL_LAST_DAY_OF_WEEK_IN_MONTH;
1140			timelib_set_relative((char **) &ptr, i, behavior, s);
1141		}
1142		TIMELIB_DEINIT;
1143		return TIMELIB_WEEK_DAY_OF_MONTH;
1144	}
1145
1146	timetiny12 | timeshort12 | timelong12
1147	{
1148		DEBUG_OUTPUT("timetiny12 | timeshort12 | timelong12");
1149		TIMELIB_INIT;
1150		TIMELIB_HAVE_TIME();
1151		s->time->h = timelib_get_nr((char **) &ptr, 2);
1152		if (*ptr == ':' || *ptr == '.') {
1153			s->time->i = timelib_get_nr((char **) &ptr, 2);
1154			if (*ptr == ':' || *ptr == '.') {
1155				s->time->s = timelib_get_nr((char **) &ptr, 2);
1156			}
1157		}
1158		s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1159		TIMELIB_DEINIT;
1160		return TIMELIB_TIME12;
1161	}
1162
1163	mssqltime
1164	{
1165		DEBUG_OUTPUT("mssqltime");
1166		TIMELIB_INIT;
1167		TIMELIB_HAVE_TIME();
1168		s->time->h = timelib_get_nr((char **) &ptr, 2);
1169		s->time->i = timelib_get_nr((char **) &ptr, 2);
1170		if (*ptr == ':' || *ptr == '.') {
1171			s->time->s = timelib_get_nr((char **) &ptr, 2);
1172
1173			if (*ptr == ':' || *ptr == '.') {
1174				s->time->f = timelib_get_frac_nr((char **) &ptr, 8);
1175			}
1176		}
1177		timelib_eat_spaces((char **) &ptr);
1178		s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1179		TIMELIB_DEINIT;
1180		return TIMELIB_TIME24_WITH_ZONE;
1181	}
1182
1183	timeshort24 | timelong24 /* | iso8601short | iso8601norm */ | iso8601long /*| iso8601shorttz | iso8601normtz | iso8601longtz*/
1184	{
1185		int tz_not_found;
1186		DEBUG_OUTPUT("timeshort24 | timelong24 | iso8601long");
1187		TIMELIB_INIT;
1188		TIMELIB_HAVE_TIME();
1189		s->time->h = timelib_get_nr((char **) &ptr, 2);
1190		s->time->i = timelib_get_nr((char **) &ptr, 2);
1191		if (*ptr == ':' || *ptr == '.') {
1192			s->time->s = timelib_get_nr((char **) &ptr, 2);
1193
1194			if (*ptr == '.') {
1195				s->time->f = timelib_get_frac_nr((char **) &ptr, 8);
1196			}
1197		}
1198
1199		if (*ptr != '\0') {
1200			s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1201			if (tz_not_found) {
1202				add_error(s, "The timezone could not be found in the database");
1203			}
1204		}
1205		TIMELIB_DEINIT;
1206		return TIMELIB_TIME24_WITH_ZONE;
1207	}
1208
1209	gnunocolon
1210	{
1211		DEBUG_OUTPUT("gnunocolon");
1212		TIMELIB_INIT;
1213		switch (s->time->have_time) {
1214			case 0:
1215				s->time->h = timelib_get_nr((char **) &ptr, 2);
1216				s->time->i = timelib_get_nr((char **) &ptr, 2);
1217				s->time->s = 0;
1218				break;
1219			case 1:
1220				s->time->y = timelib_get_nr((char **) &ptr, 4);
1221				break;
1222			default:
1223				TIMELIB_DEINIT;
1224				add_error(s, "Double time specification");
1225				return TIMELIB_ERROR;
1226		}
1227		s->time->have_time++;
1228		TIMELIB_DEINIT;
1229		return TIMELIB_GNU_NOCOLON;
1230	}
1231/*
1232	gnunocolontz
1233	{
1234		DEBUG_OUTPUT("gnunocolontz");
1235		TIMELIB_INIT;
1236		switch (s->time->have_time) {
1237			case 0:
1238				s->time->h = timelib_get_nr((char **) &ptr, 2);
1239				s->time->i = timelib_get_nr((char **) &ptr, 2);
1240				s->time->s = 0;
1241				s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, s->tzdb, tz_get_wrapper);
1242				break;
1243			case 1:
1244				s->time->y = timelib_get_nr((char **) &ptr, 4);
1245				break;
1246			default:
1247				TIMELIB_DEINIT;
1248				return TIMELIB_ERROR;
1249		}
1250		s->time->have_time++;
1251		TIMELIB_DEINIT;
1252		return TIMELIB_GNU_NOCOLON_TZ;
1253	}
1254*/
1255	iso8601nocolon /*| iso8601nocolontz*/
1256	{
1257		int tz_not_found;
1258		DEBUG_OUTPUT("iso8601nocolon");
1259		TIMELIB_INIT;
1260		TIMELIB_HAVE_TIME();
1261		s->time->h = timelib_get_nr((char **) &ptr, 2);
1262		s->time->i = timelib_get_nr((char **) &ptr, 2);
1263		s->time->s = timelib_get_nr((char **) &ptr, 2);
1264
1265		if (*ptr != '\0') {
1266			s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1267			if (tz_not_found) {
1268				add_error(s, "The timezone could not be found in the database");
1269			}
1270		}
1271		TIMELIB_DEINIT;
1272		return TIMELIB_ISO_NOCOLON;
1273	}
1274
1275	americanshort | american
1276	{
1277		int length = 0;
1278		DEBUG_OUTPUT("americanshort | american");
1279		TIMELIB_INIT;
1280		TIMELIB_HAVE_DATE();
1281		s->time->m = timelib_get_nr((char **) &ptr, 2);
1282		s->time->d = timelib_get_nr((char **) &ptr, 2);
1283		if (*ptr == '/') {
1284			s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1285			TIMELIB_PROCESS_YEAR(s->time->y, length);
1286		}
1287		TIMELIB_DEINIT;
1288		return TIMELIB_AMERICAN;
1289	}
1290
1291	iso8601date4 | iso8601dateslash | dateslash
1292	{
1293		DEBUG_OUTPUT("iso8601date4 | iso8601date2 | iso8601dateslash | dateslash");
1294		TIMELIB_INIT;
1295		TIMELIB_HAVE_DATE();
1296		s->time->y = timelib_get_unsigned_nr((char **) &ptr, 4);
1297		s->time->m = timelib_get_nr((char **) &ptr, 2);
1298		s->time->d = timelib_get_nr((char **) &ptr, 2);
1299		TIMELIB_DEINIT;
1300		return TIMELIB_ISO_DATE;
1301	}
1302
1303	iso8601date2
1304	{
1305		int length = 0;
1306		DEBUG_OUTPUT("iso8601date2");
1307		TIMELIB_INIT;
1308		TIMELIB_HAVE_DATE();
1309		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1310		s->time->m = timelib_get_nr((char **) &ptr, 2);
1311		s->time->d = timelib_get_nr((char **) &ptr, 2);
1312		TIMELIB_PROCESS_YEAR(s->time->y, length);
1313		TIMELIB_DEINIT;
1314		return TIMELIB_ISO_DATE;
1315	}
1316
1317	gnudateshorter
1318	{
1319		int length = 0;
1320		DEBUG_OUTPUT("gnudateshorter");
1321		TIMELIB_INIT;
1322		TIMELIB_HAVE_DATE();
1323		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1324		s->time->m = timelib_get_nr((char **) &ptr, 2);
1325		s->time->d = 1;
1326		TIMELIB_PROCESS_YEAR(s->time->y, length);
1327		TIMELIB_DEINIT;
1328		return TIMELIB_ISO_DATE;
1329	}
1330
1331	gnudateshort
1332	{
1333		int length = 0;
1334		DEBUG_OUTPUT("gnudateshort");
1335		TIMELIB_INIT;
1336		TIMELIB_HAVE_DATE();
1337		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1338		s->time->m = timelib_get_nr((char **) &ptr, 2);
1339		s->time->d = timelib_get_nr((char **) &ptr, 2);
1340		TIMELIB_PROCESS_YEAR(s->time->y, length);
1341		TIMELIB_DEINIT;
1342		return TIMELIB_ISO_DATE;
1343	}
1344
1345	datefull
1346	{
1347		int length = 0;
1348		DEBUG_OUTPUT("datefull");
1349		TIMELIB_INIT;
1350		TIMELIB_HAVE_DATE();
1351		s->time->d = timelib_get_nr((char **) &ptr, 2);
1352		timelib_skip_day_suffix((char **) &ptr);
1353		s->time->m = timelib_get_month((char **) &ptr);
1354		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1355		TIMELIB_PROCESS_YEAR(s->time->y, length);
1356		TIMELIB_DEINIT;
1357		return TIMELIB_DATE_FULL;
1358	}
1359
1360	pointeddate4
1361	{
1362		DEBUG_OUTPUT("pointed date YYYY");
1363		TIMELIB_INIT;
1364		TIMELIB_HAVE_DATE();
1365		s->time->d = timelib_get_nr((char **) &ptr, 2);
1366		s->time->m = timelib_get_nr((char **) &ptr, 2);
1367		s->time->y = timelib_get_nr((char **) &ptr, 4);
1368		TIMELIB_DEINIT;
1369		return TIMELIB_DATE_FULL_POINTED;
1370	}
1371
1372	pointeddate2
1373	{
1374		int length = 0;
1375		DEBUG_OUTPUT("pointed date YY");
1376		TIMELIB_INIT;
1377		TIMELIB_HAVE_DATE();
1378		s->time->d = timelib_get_nr((char **) &ptr, 2);
1379		s->time->m = timelib_get_nr((char **) &ptr, 2);
1380		s->time->y = timelib_get_nr_ex((char **) &ptr, 2, &length);
1381		TIMELIB_PROCESS_YEAR(s->time->y, length);
1382		TIMELIB_DEINIT;
1383		return TIMELIB_DATE_FULL_POINTED;
1384	}
1385
1386	datenoday
1387	{
1388		int length = 0;
1389		DEBUG_OUTPUT("datenoday");
1390		TIMELIB_INIT;
1391		TIMELIB_HAVE_DATE();
1392		s->time->m = timelib_get_month((char **) &ptr);
1393		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1394		s->time->d = 1;
1395		TIMELIB_PROCESS_YEAR(s->time->y, length);
1396		TIMELIB_DEINIT;
1397		return TIMELIB_DATE_NO_DAY;
1398	}
1399
1400	datenodayrev
1401	{
1402		int length = 0;
1403		DEBUG_OUTPUT("datenodayrev");
1404		TIMELIB_INIT;
1405		TIMELIB_HAVE_DATE();
1406		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1407		s->time->m = timelib_get_month((char **) &ptr);
1408		s->time->d = 1;
1409		TIMELIB_PROCESS_YEAR(s->time->y, length);
1410		TIMELIB_DEINIT;
1411		return TIMELIB_DATE_NO_DAY;
1412	}
1413
1414	datetextual | datenoyear
1415	{
1416		int length = 0;
1417		DEBUG_OUTPUT("datetextual | datenoyear");
1418		TIMELIB_INIT;
1419		TIMELIB_HAVE_DATE();
1420		s->time->m = timelib_get_month((char **) &ptr);
1421		s->time->d = timelib_get_nr((char **) &ptr, 2);
1422		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1423		TIMELIB_PROCESS_YEAR(s->time->y, length);
1424		TIMELIB_DEINIT;
1425		return TIMELIB_DATE_TEXT;
1426	}
1427
1428	datenoyearrev
1429	{
1430		DEBUG_OUTPUT("datenoyearrev");
1431		TIMELIB_INIT;
1432		TIMELIB_HAVE_DATE();
1433		s->time->d = timelib_get_nr((char **) &ptr, 2);
1434		timelib_skip_day_suffix((char **) &ptr);
1435		s->time->m = timelib_get_month((char **) &ptr);
1436		TIMELIB_DEINIT;
1437		return TIMELIB_DATE_TEXT;
1438	}
1439
1440	datenocolon
1441	{
1442		DEBUG_OUTPUT("datenocolon");
1443		TIMELIB_INIT;
1444		TIMELIB_HAVE_DATE();
1445		s->time->y = timelib_get_nr((char **) &ptr, 4);
1446		s->time->m = timelib_get_nr((char **) &ptr, 2);
1447		s->time->d = timelib_get_nr((char **) &ptr, 2);
1448		TIMELIB_DEINIT;
1449		return TIMELIB_DATE_NOCOLON;
1450	}
1451
1452	xmlrpc | xmlrpcnocolon | soap | wddx | exif
1453	{
1454		int tz_not_found;
1455		DEBUG_OUTPUT("xmlrpc | xmlrpcnocolon | soap | wddx | exif");
1456		TIMELIB_INIT;
1457		TIMELIB_HAVE_TIME();
1458		TIMELIB_HAVE_DATE();
1459		s->time->y = timelib_get_nr((char **) &ptr, 4);
1460		s->time->m = timelib_get_nr((char **) &ptr, 2);
1461		s->time->d = timelib_get_nr((char **) &ptr, 2);
1462		s->time->h = timelib_get_nr((char **) &ptr, 2);
1463		s->time->i = timelib_get_nr((char **) &ptr, 2);
1464		s->time->s = timelib_get_nr((char **) &ptr, 2);
1465		if (*ptr == '.') {
1466			s->time->f = timelib_get_frac_nr((char **) &ptr, 9);
1467			if (*ptr) { /* timezone is optional */
1468				s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1469				if (tz_not_found) {
1470					add_error(s, "The timezone could not be found in the database");
1471				}
1472			}
1473		}
1474		TIMELIB_DEINIT;
1475		return TIMELIB_XMLRPC_SOAP;
1476	}
1477
1478	pgydotd
1479	{
1480		int length = 0;
1481		DEBUG_OUTPUT("pgydotd");
1482		TIMELIB_INIT;
1483		TIMELIB_HAVE_DATE();
1484		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1485		s->time->d = timelib_get_nr((char **) &ptr, 3);
1486		s->time->m = 1;
1487		TIMELIB_PROCESS_YEAR(s->time->y, length);
1488		TIMELIB_DEINIT;
1489		return TIMELIB_PG_YEARDAY;
1490	}
1491
1492	isoweekday
1493	{
1494		timelib_sll w, d;
1495		DEBUG_OUTPUT("isoweekday");
1496		TIMELIB_INIT;
1497		TIMELIB_HAVE_DATE();
1498		TIMELIB_HAVE_RELATIVE();
1499
1500		s->time->y = timelib_get_nr((char **) &ptr, 4);
1501		w = timelib_get_nr((char **) &ptr, 2);
1502		d = timelib_get_nr((char **) &ptr, 1);
1503		s->time->m = 1;
1504		s->time->d = 1;
1505		s->time->relative.d = timelib_daynr_from_weeknr(s->time->y, w, d);
1506
1507		TIMELIB_DEINIT;
1508		return TIMELIB_ISO_WEEK;
1509	}
1510
1511	isoweek
1512	{
1513		timelib_sll w, d;
1514		DEBUG_OUTPUT("isoweek");
1515		TIMELIB_INIT;
1516		TIMELIB_HAVE_DATE();
1517		TIMELIB_HAVE_RELATIVE();
1518
1519		s->time->y = timelib_get_nr((char **) &ptr, 4);
1520		w = timelib_get_nr((char **) &ptr, 2);
1521		d = 1;
1522		s->time->m = 1;
1523		s->time->d = 1;
1524		s->time->relative.d = timelib_daynr_from_weeknr(s->time->y, w, d);
1525
1526		TIMELIB_DEINIT;
1527		return TIMELIB_ISO_WEEK;
1528	}
1529
1530	pgtextshort
1531	{
1532		int length = 0;
1533		DEBUG_OUTPUT("pgtextshort");
1534		TIMELIB_INIT;
1535		TIMELIB_HAVE_DATE();
1536		s->time->m = timelib_get_month((char **) &ptr);
1537		s->time->d = timelib_get_nr((char **) &ptr, 2);
1538		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1539		TIMELIB_PROCESS_YEAR(s->time->y, length);
1540		TIMELIB_DEINIT;
1541		return TIMELIB_PG_TEXT;
1542	}
1543
1544	pgtextreverse
1545	{
1546		int length = 0;
1547		DEBUG_OUTPUT("pgtextreverse");
1548		TIMELIB_INIT;
1549		TIMELIB_HAVE_DATE();
1550		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1551		s->time->m = timelib_get_month((char **) &ptr);
1552		s->time->d = timelib_get_nr((char **) &ptr, 2);
1553		TIMELIB_PROCESS_YEAR(s->time->y, length);
1554		TIMELIB_DEINIT;
1555		return TIMELIB_PG_TEXT;
1556	}
1557
1558	clf
1559	{
1560		int tz_not_found;
1561		DEBUG_OUTPUT("clf");
1562		TIMELIB_INIT;
1563		TIMELIB_HAVE_TIME();
1564		TIMELIB_HAVE_DATE();
1565		s->time->d = timelib_get_nr((char **) &ptr, 2);
1566		s->time->m = timelib_get_month((char **) &ptr);
1567		s->time->y = timelib_get_nr((char **) &ptr, 4);
1568		s->time->h = timelib_get_nr((char **) &ptr, 2);
1569		s->time->i = timelib_get_nr((char **) &ptr, 2);
1570		s->time->s = timelib_get_nr((char **) &ptr, 2);
1571		s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1572		if (tz_not_found) {
1573			add_error(s, "The timezone could not be found in the database");
1574		}
1575		TIMELIB_DEINIT;
1576		return TIMELIB_CLF;
1577	}
1578
1579	year4
1580	{
1581		DEBUG_OUTPUT("year4");
1582		TIMELIB_INIT;
1583		s->time->y = timelib_get_nr((char **) &ptr, 4);
1584		TIMELIB_DEINIT;
1585		return TIMELIB_CLF;
1586	}
1587
1588	ago
1589	{
1590		DEBUG_OUTPUT("ago");
1591		TIMELIB_INIT;
1592		s->time->relative.y = 0 - s->time->relative.y;
1593		s->time->relative.m = 0 - s->time->relative.m;
1594		s->time->relative.d = 0 - s->time->relative.d;
1595		s->time->relative.h = 0 - s->time->relative.h;
1596		s->time->relative.i = 0 - s->time->relative.i;
1597		s->time->relative.s = 0 - s->time->relative.s;
1598		s->time->relative.weekday = 0 - s->time->relative.weekday;
1599		if (s->time->relative.weekday == 0) {
1600			s->time->relative.weekday = -7;
1601		}
1602		if (s->time->relative.have_special_relative && s->time->relative.special.type == TIMELIB_SPECIAL_WEEKDAY) {
1603			s->time->relative.special.amount = 0 - s->time->relative.special.amount;
1604		}
1605		TIMELIB_DEINIT;
1606		return TIMELIB_AGO;
1607	}
1608
1609	daytext
1610	{
1611		const timelib_relunit* relunit;
1612		DEBUG_OUTPUT("daytext");
1613		TIMELIB_INIT;
1614		TIMELIB_HAVE_RELATIVE();
1615		TIMELIB_HAVE_WEEKDAY_RELATIVE();
1616		TIMELIB_UNHAVE_TIME();
1617		relunit = timelib_lookup_relunit((char**) &ptr);
1618		s->time->relative.weekday = relunit->multiplier;
1619		if (s->time->relative.weekday_behavior != 2) {
1620			s->time->relative.weekday_behavior = 1;
1621		}
1622
1623		TIMELIB_DEINIT;
1624		return TIMELIB_WEEKDAY;
1625	}
1626
1627	relativetextweek
1628	{
1629		timelib_sll i;
1630		int         behavior = 0;
1631		DEBUG_OUTPUT("relativetextweek");
1632		TIMELIB_INIT;
1633		TIMELIB_HAVE_RELATIVE();
1634
1635		while(*ptr) {
1636			i = timelib_get_relative_text((char **) &ptr, &behavior);
1637			timelib_eat_spaces((char **) &ptr);
1638			timelib_set_relative((char **) &ptr, i, behavior, s);
1639			s->time->relative.weekday_behavior = 2;
1640
1641			/* to handle the format weekday + last/this/next week */
1642			if (s->time->relative.have_weekday_relative == 0) {
1643				TIMELIB_HAVE_WEEKDAY_RELATIVE();
1644				s->time->relative.weekday = 1;
1645			}
1646		}
1647		TIMELIB_DEINIT;
1648		return TIMELIB_RELATIVE;
1649	}
1650
1651	relativetext
1652	{
1653		timelib_sll i;
1654		int         behavior = 0;
1655		DEBUG_OUTPUT("relativetext");
1656		TIMELIB_INIT;
1657		TIMELIB_HAVE_RELATIVE();
1658
1659		while(*ptr) {
1660			i = timelib_get_relative_text((char **) &ptr, &behavior);
1661			timelib_eat_spaces((char **) &ptr);
1662			timelib_set_relative((char **) &ptr, i, behavior, s);
1663		}
1664		TIMELIB_DEINIT;
1665		return TIMELIB_RELATIVE;
1666	}
1667
1668	monthfull | monthabbr
1669	{
1670		DEBUG_OUTPUT("monthtext");
1671		TIMELIB_INIT;
1672		TIMELIB_HAVE_DATE();
1673		s->time->m = timelib_lookup_month((char **) &ptr);
1674		TIMELIB_DEINIT;
1675		return TIMELIB_DATE_TEXT;
1676	}
1677
1678	tzcorrection | tz
1679	{
1680		int tz_not_found;
1681		DEBUG_OUTPUT("tzcorrection | tz");
1682		TIMELIB_INIT;
1683		TIMELIB_HAVE_TZ();
1684		s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1685		if (tz_not_found) {
1686			add_error(s, "The timezone could not be found in the database");
1687		}
1688		TIMELIB_DEINIT;
1689		return TIMELIB_TIMEZONE;
1690	}
1691
1692	dateshortwithtimeshort12 | dateshortwithtimelong12
1693	{
1694		DEBUG_OUTPUT("dateshortwithtimeshort12 | dateshortwithtimelong12");
1695		TIMELIB_INIT;
1696		TIMELIB_HAVE_DATE();
1697		s->time->m = timelib_get_month((char **) &ptr);
1698		s->time->d = timelib_get_nr((char **) &ptr, 2);
1699
1700		TIMELIB_HAVE_TIME();
1701		s->time->h = timelib_get_nr((char **) &ptr, 2);
1702		s->time->i = timelib_get_nr((char **) &ptr, 2);
1703		if (*ptr == ':' || *ptr == '.') {
1704			s->time->s = timelib_get_nr((char **) &ptr, 2);
1705
1706			if (*ptr == '.') {
1707				s->time->f = timelib_get_frac_nr((char **) &ptr, 8);
1708			}
1709		}
1710
1711		s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1712		TIMELIB_DEINIT;
1713		return TIMELIB_SHORTDATE_WITH_TIME;
1714	}
1715
1716	dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz
1717	{
1718		int tz_not_found;
1719		DEBUG_OUTPUT("dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz");
1720		TIMELIB_INIT;
1721		TIMELIB_HAVE_DATE();
1722		s->time->m = timelib_get_month((char **) &ptr);
1723		s->time->d = timelib_get_nr((char **) &ptr, 2);
1724
1725		TIMELIB_HAVE_TIME();
1726		s->time->h = timelib_get_nr((char **) &ptr, 2);
1727		s->time->i = timelib_get_nr((char **) &ptr, 2);
1728		if (*ptr == ':') {
1729			s->time->s = timelib_get_nr((char **) &ptr, 2);
1730
1731			if (*ptr == '.') {
1732				s->time->f = timelib_get_frac_nr((char **) &ptr, 8);
1733			}
1734		}
1735
1736		if (*ptr != '\0') {
1737			s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1738			if (tz_not_found) {
1739				add_error(s, "The timezone could not be found in the database");
1740			}
1741		}
1742		TIMELIB_DEINIT;
1743		return TIMELIB_SHORTDATE_WITH_TIME;
1744	}
1745
1746	relative
1747	{
1748		timelib_ull i;
1749		DEBUG_OUTPUT("relative");
1750		TIMELIB_INIT;
1751		TIMELIB_HAVE_RELATIVE();
1752
1753		while(*ptr) {
1754			i = timelib_get_unsigned_nr((char **) &ptr, 24);
1755			timelib_eat_spaces((char **) &ptr);
1756			timelib_set_relative((char **) &ptr, i, 1, s);
1757		}
1758		TIMELIB_DEINIT;
1759		return TIMELIB_RELATIVE;
1760	}
1761
1762	[ .,\t]
1763	{
1764		goto std;
1765	}
1766
1767	"\000"|"\n"
1768	{
1769		s->pos = cursor; s->line++;
1770		goto std;
1771	}
1772
1773	any
1774	{
1775		add_error(s, "Unexpected character");
1776		goto std;
1777	}
1778*/
1779}
1780
1781/*!max:re2c */
1782
1783timelib_time* timelib_strtotime(char *s, int len, struct timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper)
1784{
1785	Scanner in;
1786	int t;
1787	char *e = s + len - 1;
1788
1789	memset(&in, 0, sizeof(in));
1790	in.errors = malloc(sizeof(struct timelib_error_container));
1791	in.errors->warning_count = 0;
1792	in.errors->warning_messages = NULL;
1793	in.errors->error_count = 0;
1794	in.errors->error_messages = NULL;
1795
1796	if (len > 0) {
1797		while (isspace(*s) && s < e) {
1798			s++;
1799		}
1800		while (isspace(*e) && e > s) {
1801			e--;
1802		}
1803	}
1804	if (e - s < 0) {
1805		in.time = timelib_time_ctor();
1806		add_error(&in, "Empty string");
1807		if (errors) {
1808			*errors = in.errors;
1809		} else {
1810			timelib_error_container_dtor(in.errors);
1811		}
1812		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;
1813		in.time->is_localtime = in.time->zone_type = 0;
1814		return in.time;
1815	}
1816	e++;
1817
1818	in.str = malloc((e - s) + YYMAXFILL);
1819	memset(in.str, 0, (e - s) + YYMAXFILL);
1820	memcpy(in.str, s, (e - s));
1821	in.lim = in.str + (e - s) + YYMAXFILL;
1822	in.cur = in.str;
1823	in.time = timelib_time_ctor();
1824	in.time->y = TIMELIB_UNSET;
1825	in.time->d = TIMELIB_UNSET;
1826	in.time->m = TIMELIB_UNSET;
1827	in.time->h = TIMELIB_UNSET;
1828	in.time->i = TIMELIB_UNSET;
1829	in.time->s = TIMELIB_UNSET;
1830	in.time->f = TIMELIB_UNSET;
1831	in.time->z = TIMELIB_UNSET;
1832	in.time->dst = TIMELIB_UNSET;
1833	in.tzdb = tzdb;
1834	in.time->is_localtime = 0;
1835	in.time->zone_type = 0;
1836
1837	do {
1838		t = scan(&in, tz_get_wrapper);
1839#ifdef DEBUG_PARSER
1840		printf("%d\n", t);
1841#endif
1842	} while(t != EOI);
1843
1844	/* do funky checking whether the parsed time was valid time */
1845	if (in.time->have_time && !timelib_valid_time( in.time->h, in.time->i, in.time->s)) {
1846		add_warning(&in, "The parsed time was invalid");
1847	}
1848	/* do funky checking whether the parsed date was valid date */
1849	if (in.time->have_date && !timelib_valid_date( in.time->y, in.time->m, in.time->d)) {
1850		add_warning(&in, "The parsed date was invalid");
1851	}
1852
1853	free(in.str);
1854	if (errors) {
1855		*errors = in.errors;
1856	} else {
1857		timelib_error_container_dtor(in.errors);
1858	}
1859	return in.time;
1860}
1861
1862#define TIMELIB_CHECK_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, int 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 = 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 second minute 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_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->f = 0.0;
2079				s->time->relative.s += tmp;
2080				s->time->is_localtime = 1;
2081				s->time->zone_type = TIMELIB_ZONETYPE_OFFSET;
2082				s->time->z = 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_get_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 (parsed->y == TIMELIB_UNSET) parsed->y = now->y != TIMELIB_UNSET ? now->y : 0;
2236	if (parsed->d == TIMELIB_UNSET) parsed->d = now->d != TIMELIB_UNSET ? now->d : 0;
2237	if (parsed->m == TIMELIB_UNSET) parsed->m = now->m != TIMELIB_UNSET ? now->m : 0;
2238	if (parsed->h == TIMELIB_UNSET) parsed->h = now->h != TIMELIB_UNSET ? now->h : 0;
2239	if (parsed->i == TIMELIB_UNSET) parsed->i = now->i != TIMELIB_UNSET ? now->i : 0;
2240	if (parsed->s == TIMELIB_UNSET) parsed->s = now->s != TIMELIB_UNSET ? now->s : 0;
2241	if (parsed->f == TIMELIB_UNSET) parsed->f = now->f != TIMELIB_UNSET ? now->f : 0;
2242	if (parsed->z == TIMELIB_UNSET) parsed->z = now->z != TIMELIB_UNSET ? now->z : 0;
2243	if (parsed->dst == TIMELIB_UNSET) parsed->dst = now->dst != TIMELIB_UNSET ? now->dst : 0;
2244
2245	if (!parsed->tz_abbr) {
2246		parsed->tz_abbr = now->tz_abbr ? strdup(now->tz_abbr) : NULL;
2247	}
2248	if (!parsed->tz_info) {
2249		parsed->tz_info = now->tz_info ? (!(options & TIMELIB_NO_CLONE) ? timelib_tzinfo_clone(now->tz_info) : now->tz_info) : NULL;
2250	}
2251	if (parsed->zone_type == 0 && now->zone_type != 0) {
2252		parsed->zone_type = now->zone_type;
2253/*		parsed->tz_abbr = now->tz_abbr ? strdup(now->tz_abbr) : NULL;
2254		parsed->tz_info = now->tz_info ? timelib_tzinfo_clone(now->tz_info) : NULL;
2255*/		parsed->is_localtime = 1;
2256	}
2257/*	timelib_dump_date(parsed, 2);
2258	timelib_dump_date(now, 2);
2259*/
2260}
2261
2262char *timelib_timezone_id_from_abbr(const char *abbr, long gmtoffset, int isdst)
2263{
2264	const timelib_tz_lookup_table *tp;
2265
2266	tp = zone_search(abbr, gmtoffset, isdst);
2267	if (tp) {
2268		return (tp->full_tz_name);
2269	} else {
2270		return NULL;
2271	}
2272}
2273
2274const timelib_tz_lookup_table *timelib_timezone_abbreviations_list(void)
2275{
2276	return timelib_timezone_lookup;
2277}
2278
2279#ifdef DEBUG_PARSER_STUB
2280int main(void)
2281{
2282	timelib_time time = timelib_strtotime("May 12");
2283
2284	printf ("%04d-%02d-%02d %02d:%02d:%02d.%-5d %+04d %1d",
2285		time.y, time.m, time.d, time.h, time.i, time.s, time.f, time.z, time.dst);
2286	if (time.have_relative) {
2287		printf ("%3dY %3dM %3dD / %3dH %3dM %3dS",
2288			time.relative.y, time.relative.m, time.relative.d, time.relative.h, time.relative.i, time.relative.s);
2289	}
2290	if (time.have_weekday_relative) {
2291		printf (" / %d", time.relative.weekday);
2292	}
2293	if (time.have_weeknr_day) {
2294		printf(" / %dW%d", time.relative.weeknr_day.weeknr, time.relative.weeknr_day.dayofweek);
2295	}
2296	return 0;
2297}
2298#endif
2299
2300/*
2301 * vim: syntax=c
2302 */
2303