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