xref: /PHP-5.4/ext/date/lib/parse_date.re (revision 358aedc4)
1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2014 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' && **ptr != ';' && **ptr != ':' &&
656           **ptr != '/' && **ptr != '.' && **ptr != '-' && **ptr != '(' && **ptr != ')' ) {
657		++*ptr;
658	}
659	end = *ptr;
660	word = calloc(1, end - begin + 1);
661	memcpy(word, begin, end - begin);
662
663	for (tp = timelib_relunit_lookup; tp->name; tp++) {
664		if (strcasecmp(word, tp->name) == 0) {
665			value = tp;
666			break;
667		}
668	}
669
670	free(word);
671	return value;
672}
673
674static void timelib_set_relative(char **ptr, timelib_sll amount, int behavior, Scanner *s)
675{
676	const timelib_relunit* relunit;
677
678	if (!(relunit = timelib_lookup_relunit(ptr))) {
679		return;
680	}
681
682	switch (relunit->unit) {
683		case TIMELIB_SECOND: s->time->relative.s += amount * relunit->multiplier; break;
684		case TIMELIB_MINUTE: s->time->relative.i += amount * relunit->multiplier; break;
685		case TIMELIB_HOUR:   s->time->relative.h += amount * relunit->multiplier; break;
686		case TIMELIB_DAY:    s->time->relative.d += amount * relunit->multiplier; break;
687		case TIMELIB_MONTH:  s->time->relative.m += amount * relunit->multiplier; break;
688		case TIMELIB_YEAR:   s->time->relative.y += amount * relunit->multiplier; break;
689
690		case TIMELIB_WEEKDAY:
691			TIMELIB_HAVE_WEEKDAY_RELATIVE();
692			TIMELIB_UNHAVE_TIME();
693			s->time->relative.d += (amount > 0 ? amount - 1 : amount) * 7;
694			s->time->relative.weekday = relunit->multiplier;
695			s->time->relative.weekday_behavior = behavior;
696			break;
697
698		case TIMELIB_SPECIAL:
699			TIMELIB_HAVE_SPECIAL_RELATIVE();
700			TIMELIB_UNHAVE_TIME();
701			s->time->relative.special.type = relunit->multiplier;
702			s->time->relative.special.amount = amount;
703	}
704}
705
706const static timelib_tz_lookup_table* zone_search(const char *word, long gmtoffset, int isdst)
707{
708	int first_found = 0;
709	const timelib_tz_lookup_table  *tp, *first_found_elem = NULL;
710	const timelib_tz_lookup_table  *fmp;
711
712	if (strcasecmp("utc", word) == 0 || strcasecmp("gmt", word) == 0) {
713		return timelib_timezone_utc;
714	}
715
716	for (tp = timelib_timezone_lookup; tp->name; tp++) {
717		if (strcasecmp(word, tp->name) == 0) {
718			if (!first_found) {
719				first_found = 1;
720				first_found_elem = tp;
721				if (gmtoffset == -1) {
722					return tp;
723				}
724			}
725			if (tp->gmtoffset == gmtoffset) {
726				return tp;
727			}
728		}
729	}
730	if (first_found) {
731		return first_found_elem;
732	}
733
734	for (tp = timelib_timezone_lookup; tp->name; tp++) {
735		if (tp->full_tz_name && strcasecmp(word, tp->full_tz_name) == 0) {
736			if (!first_found) {
737				first_found = 1;
738				first_found_elem = tp;
739				if (gmtoffset == -1) {
740					return tp;
741				}
742			}
743			if (tp->gmtoffset == gmtoffset) {
744				return tp;
745			}
746		}
747	}
748	if (first_found) {
749		return first_found_elem;
750	}
751
752
753	/* Still didn't find anything, let's find the zone solely based on
754	 * offset/isdst then */
755	for (fmp = timelib_timezone_fallbackmap; fmp->name; fmp++) {
756		if ((fmp->gmtoffset * 60) == gmtoffset && fmp->type == isdst) {
757			return fmp;
758		}
759	}
760	return NULL;
761}
762
763static long timelib_lookup_zone(char **ptr, int *dst, char **tz_abbr, int *found)
764{
765	char *word;
766	char *begin = *ptr, *end;
767	long  value = 0;
768	const timelib_tz_lookup_table *tp;
769
770	while (**ptr != '\0' && **ptr != ')' && **ptr != ' ') {
771		++*ptr;
772	}
773	end = *ptr;
774	word = calloc(1, end - begin + 1);
775	memcpy(word, begin, end - begin);
776
777	if ((tp = zone_search(word, -1, 0))) {
778		value = -tp->gmtoffset / 60;
779		*dst = tp->type;
780		value += tp->type * 60;
781		*found = 1;
782	} else {
783		*found = 0;
784	}
785
786	*tz_abbr = word;
787	return value;
788}
789
790static 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)
791{
792	timelib_tzinfo *res;
793	long            retval = 0;
794
795	*tz_not_found = 0;
796
797	while (**ptr == ' ' || **ptr == '\t' || **ptr == '(') {
798		++*ptr;
799	}
800	if ((*ptr)[0] == 'G' && (*ptr)[1] == 'M' && (*ptr)[2] == 'T' && ((*ptr)[3] == '+' || (*ptr)[3] == '-')) {
801		*ptr += 3;
802	}
803	if (**ptr == '+') {
804		++*ptr;
805		t->is_localtime = 1;
806		t->zone_type = TIMELIB_ZONETYPE_OFFSET;
807		*tz_not_found = 0;
808		t->dst = 0;
809
810		retval = -1 * timelib_parse_tz_cor(ptr);
811	} else if (**ptr == '-') {
812		++*ptr;
813		t->is_localtime = 1;
814		t->zone_type = TIMELIB_ZONETYPE_OFFSET;
815		*tz_not_found = 0;
816		t->dst = 0;
817
818		retval = timelib_parse_tz_cor(ptr);
819	} else {
820		int found = 0;
821		long offset;
822		char *tz_abbr;
823
824		t->is_localtime = 1;
825
826		offset = timelib_lookup_zone(ptr, dst, &tz_abbr, &found);
827		if (found) {
828			t->zone_type = TIMELIB_ZONETYPE_ABBR;
829		}
830#if 0
831		/* If we found a TimeZone identifier, use it */
832		if (tz_name) {
833			t->tz_info = timelib_parse_tzfile(tz_name);
834			t->zone_type = TIMELIB_ZONETYPE_ID;
835		}
836#endif
837		/* If we have a TimeZone identifier to start with, use it */
838		if (strstr(tz_abbr, "/") || strcmp(tz_abbr, "UTC") == 0) {
839			if ((res = tz_wrapper(tz_abbr, tzdb)) != NULL) {
840				t->tz_info = res;
841				t->zone_type = TIMELIB_ZONETYPE_ID;
842				found++;
843			}
844		}
845		if (found && t->zone_type != TIMELIB_ZONETYPE_ID) {
846			timelib_time_tz_abbr_update(t, tz_abbr);
847		}
848		free(tz_abbr);
849		*tz_not_found = (found == 0);
850		retval = offset;
851	}
852	while (**ptr == ')') {
853		++*ptr;
854	}
855	return retval;
856}
857
858#define timelib_split_free(arg) {       \
859	int i;                         \
860	for (i = 0; i < arg.c; i++) {  \
861		free(arg.v[i]);            \
862	}                              \
863	if (arg.v) {                   \
864		free(arg.v);               \
865	}                              \
866}
867
868static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper)
869{
870	uchar *cursor = s->cur;
871	char *str, *ptr = NULL;
872
873std:
874	s->tok = cursor;
875	s->len = 0;
876/*!re2c
877any = [\000-\377];
878
879space = [ \t]+;
880frac = "."[0-9]+;
881
882ago = 'ago';
883
884hour24 = [01]?[0-9] | "2"[0-4];
885hour24lz = [01][0-9] | "2"[0-4];
886hour12 = "0"?[1-9] | "1"[0-2];
887minute = [0-5]?[0-9];
888minutelz = [0-5][0-9];
889second = minute | "60";
890secondlz = minutelz | "60";
891meridian = ([AaPp] "."? [Mm] "."?) [\000\t ];
892tz = "("? [A-Za-z]{1,6} ")"? | [A-Z][a-z]+([_/-][A-Za-z]+)+;
893tzcorrection = "GMT"? [+-] hour24 ":"? minute?;
894
895daysuf = "st" | "nd" | "rd" | "th";
896
897month = "0"? [0-9] | "1"[0-2];
898day   = (([0-2]?[0-9]) | ("3"[01])) daysuf?;
899year  = [0-9]{1,4};
900year2 = [0-9]{2};
901year4 = [0-9]{4};
902year4withsign = [+-]? [0-9]{4};
903
904dayofyear = "00"[1-9] | "0"[1-9][0-9] | [1-2][0-9][0-9] | "3"[0-5][0-9] | "36"[0-6];
905weekofyear = "0"[1-9] | [1-4][0-9] | "5"[0-3];
906
907monthlz = "0" [0-9] | "1" [0-2];
908daylz   = "0" [0-9] | [1-2][0-9] | "3" [01];
909
910dayfull = 'sunday' | 'monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday';
911dayabbr = 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat' | 'sun';
912dayspecial = 'weekday' | 'weekdays';
913daytext = dayfull | dayabbr | dayspecial;
914
915monthfull = 'january' | 'february' | 'march' | 'april' | 'may' | 'june' | 'july' | 'august' | 'september' | 'october' | 'november' | 'december';
916monthabbr = 'jan' | 'feb' | 'mar' | 'apr' | 'may' | 'jun' | 'jul' | 'aug' | 'sep' | 'sept' | 'oct' | 'nov' | 'dec';
917monthroman = "I" | "II" | "III" | "IV" | "V" | "VI" | "VII" | "VIII" | "IX" | "X" | "XI" | "XII";
918monthtext = monthfull | monthabbr | monthroman;
919
920/* Time formats */
921timetiny12 = hour12 space? meridian;
922timeshort12 = hour12[:.]minutelz space? meridian;
923timelong12 = hour12[:.]minute[:.]secondlz space? meridian;
924
925timeshort24 = 't'? hour24[:.]minute;
926timelong24 =  't'? hour24[:.]minute[:.]second;
927iso8601long =  't'? hour24 [:.] minute [:.] second frac;
928
929/* iso8601shorttz = hour24 [:] minutelz space? (tzcorrection | tz); */
930iso8601normtz =  't'? hour24 [:.] minute [:.] secondlz space? (tzcorrection | tz);
931/* iso8601longtz =  hour24 [:] minute [:] secondlz frac space? (tzcorrection | tz); */
932
933gnunocolon       = 't'? hour24lz minutelz;
934/* gnunocolontz     = hour24lz minutelz space? (tzcorrection | tz); */
935iso8601nocolon   = 't'? hour24lz minutelz secondlz;
936/* iso8601nocolontz = hour24lz minutelz secondlz space? (tzcorrection | tz); */
937
938/* Date formats */
939americanshort    = month "/" day;
940american         = month "/" day "/" year;
941iso8601dateslash = year4 "/" monthlz "/" daylz "/"?;
942dateslash        = year4 "/" month "/" day;
943iso8601date4     = year4withsign "-" monthlz "-" daylz;
944iso8601date2     = year2 "-" monthlz "-" daylz;
945gnudateshorter   = year4 "-" month;
946gnudateshort     = year "-" month "-" day;
947pointeddate4     = day [.\t-] month [.-] year4;
948pointeddate2     = day [.\t] month "." year2;
949datefull         = day ([ \t.-])* monthtext ([ \t.-])* year;
950datenoday        = monthtext ([ .\t-])* year4;
951datenodayrev     = year4 ([ .\t-])* monthtext;
952datetextual      = monthtext ([ .\t-])* day [,.stndrh\t ]+ year;
953datenoyear       = monthtext ([ .\t-])* day [,.stndrh\t ]*;
954datenoyearrev    = day ([ .\t-])* monthtext;
955datenocolon      = year4 monthlz daylz;
956
957/* Special formats */
958soap             = year4 "-" monthlz "-" daylz "T" hour24lz ":" minutelz ":" secondlz frac tzcorrection?;
959xmlrpc           = year4 monthlz daylz "T" hour24 ":" minutelz ":" secondlz;
960xmlrpcnocolon    = year4 monthlz daylz 't' hour24 minutelz secondlz;
961wddx             = year4 "-" month "-" day "T" hour24 ":" minute ":" second;
962pgydotd          = year4 "."? dayofyear;
963pgtextshort      = monthabbr "-" daylz "-" year;
964pgtextreverse    = year "-" monthabbr "-" daylz;
965mssqltime        = hour12 ":" minutelz ":" secondlz [:.] [0-9]+ meridian;
966isoweekday       = year4 "-"? "W" weekofyear "-"? [0-7];
967isoweek          = year4 "-"? "W" weekofyear;
968exif             = year4 ":" monthlz ":" daylz " " hour24lz ":" minutelz ":" secondlz;
969firstdayof       = 'first day of';
970lastdayof        = 'last day of';
971backof           = 'back of ' hour24 space? meridian?;
972frontof          = 'front of ' hour24 space? meridian?;
973
974/* Common Log Format: 10/Oct/2000:13:55:36 -0700 */
975clf              = day "/" monthabbr "/" year4 ":" hour24lz ":" minutelz ":" secondlz space tzcorrection;
976
977/* Timestamp format: @1126396800 */
978timestamp        = "@" "-"? [0-9]+;
979
980/* To fix some ambiguities */
981dateshortwithtimeshort12  = datenoyear timeshort12;
982dateshortwithtimelong12   = datenoyear timelong12;
983dateshortwithtimeshort  = datenoyear timeshort24;
984dateshortwithtimelong   = datenoyear timelong24;
985dateshortwithtimelongtz = datenoyear iso8601normtz;
986
987/*
988 * Relative regexps
989 */
990reltextnumber = 'first'|'second'|'third'|'fourth'|'fifth'|'sixth'|'seventh'|'eight'|'eighth'|'ninth'|'tenth'|'eleventh'|'twelfth';
991reltexttext = 'next'|'last'|'previous'|'this';
992reltextunit = (('sec'|'second'|'min'|'minute'|'hour'|'day'|'fortnight'|'forthnight'|'month'|'year') 's'?) | 'weeks' | daytext;
993
994relnumber = ([+-]*[ \t]*[0-9]+);
995relative = relnumber space? (reltextunit | 'week' );
996relativetext = (reltextnumber|reltexttext) space reltextunit;
997relativetextweek = reltexttext space 'week';
998
999weekdayof        = (reltextnumber|reltexttext) space (dayfull|dayabbr) space 'of';
1000
1001*/
1002
1003/*!re2c
1004	/* so that vim highlights correctly */
1005	'yesterday'
1006	{
1007		DEBUG_OUTPUT("yesterday");
1008		TIMELIB_INIT;
1009		TIMELIB_HAVE_RELATIVE();
1010		TIMELIB_UNHAVE_TIME();
1011
1012		s->time->relative.d = -1;
1013		TIMELIB_DEINIT;
1014		return TIMELIB_RELATIVE;
1015	}
1016
1017	'now'
1018	{
1019		DEBUG_OUTPUT("now");
1020		TIMELIB_INIT;
1021
1022		TIMELIB_DEINIT;
1023		return TIMELIB_RELATIVE;
1024	}
1025
1026	'noon'
1027	{
1028		DEBUG_OUTPUT("noon");
1029		TIMELIB_INIT;
1030		TIMELIB_UNHAVE_TIME();
1031		TIMELIB_HAVE_TIME();
1032		s->time->h = 12;
1033
1034		TIMELIB_DEINIT;
1035		return TIMELIB_RELATIVE;
1036	}
1037
1038	'midnight' | 'today'
1039	{
1040		DEBUG_OUTPUT("midnight | today");
1041		TIMELIB_INIT;
1042		TIMELIB_UNHAVE_TIME();
1043
1044		TIMELIB_DEINIT;
1045		return TIMELIB_RELATIVE;
1046	}
1047
1048	'tomorrow'
1049	{
1050		DEBUG_OUTPUT("tomorrow");
1051		TIMELIB_INIT;
1052		TIMELIB_HAVE_RELATIVE();
1053		TIMELIB_UNHAVE_TIME();
1054
1055		s->time->relative.d = 1;
1056		TIMELIB_DEINIT;
1057		return TIMELIB_RELATIVE;
1058	}
1059
1060	timestamp
1061	{
1062		timelib_ull i;
1063
1064		TIMELIB_INIT;
1065		TIMELIB_HAVE_RELATIVE();
1066		TIMELIB_UNHAVE_DATE();
1067		TIMELIB_UNHAVE_TIME();
1068		TIMELIB_HAVE_TZ();
1069
1070		i = timelib_get_unsigned_nr((char **) &ptr, 24);
1071		s->time->y = 1970;
1072		s->time->m = 1;
1073		s->time->d = 1;
1074		s->time->h = s->time->i = s->time->s = 0;
1075		s->time->f = 0.0;
1076		s->time->relative.s += i;
1077		s->time->is_localtime = 1;
1078		s->time->zone_type = TIMELIB_ZONETYPE_OFFSET;
1079		s->time->z = 0;
1080		s->time->dst = 0;
1081
1082		TIMELIB_DEINIT;
1083		return TIMELIB_RELATIVE;
1084	}
1085
1086	firstdayof | lastdayof
1087	{
1088		DEBUG_OUTPUT("firstdayof | lastdayof");
1089		TIMELIB_INIT;
1090		TIMELIB_HAVE_RELATIVE();
1091
1092		/* skip "last day of" or "first day of" */
1093		if (*ptr == 'l') {
1094			s->time->relative.first_last_day_of = 2;
1095		} else {
1096			s->time->relative.first_last_day_of = 1;
1097		}
1098
1099		TIMELIB_DEINIT;
1100		return TIMELIB_LF_DAY_OF_MONTH;
1101	}
1102
1103	backof | frontof
1104	{
1105		DEBUG_OUTPUT("backof | frontof");
1106		TIMELIB_INIT;
1107		TIMELIB_UNHAVE_TIME();
1108		TIMELIB_HAVE_TIME();
1109
1110		if (*ptr == 'b') {
1111			s->time->h = timelib_get_nr((char **) &ptr, 2);
1112			s->time->i = 15;
1113		} else {
1114			s->time->h = timelib_get_nr((char **) &ptr, 2) - 1;
1115			s->time->i = 45;
1116		}
1117		if (*ptr != '\0' ) {
1118			timelib_eat_spaces((char **) &ptr);
1119			s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1120		}
1121
1122		TIMELIB_DEINIT;
1123		return TIMELIB_LF_DAY_OF_MONTH;
1124	}
1125
1126	weekdayof
1127	{
1128		timelib_sll i;
1129		int         behavior = 0;
1130		DEBUG_OUTPUT("weekdayof");
1131		TIMELIB_INIT;
1132		TIMELIB_HAVE_RELATIVE();
1133		TIMELIB_HAVE_SPECIAL_RELATIVE();
1134
1135		i = timelib_get_relative_text((char **) &ptr, &behavior);
1136		timelib_eat_spaces((char **) &ptr);
1137		if (i > 0) { /* first, second... etc */
1138			s->time->relative.special.type = TIMELIB_SPECIAL_DAY_OF_WEEK_IN_MONTH;
1139			timelib_set_relative((char **) &ptr, i, 1, s);
1140		} else { /* last */
1141			s->time->relative.special.type = TIMELIB_SPECIAL_LAST_DAY_OF_WEEK_IN_MONTH;
1142			timelib_set_relative((char **) &ptr, i, behavior, s);
1143		}
1144		TIMELIB_DEINIT;
1145		return TIMELIB_WEEK_DAY_OF_MONTH;
1146	}
1147
1148	timetiny12 | timeshort12 | timelong12
1149	{
1150		DEBUG_OUTPUT("timetiny12 | timeshort12 | timelong12");
1151		TIMELIB_INIT;
1152		TIMELIB_HAVE_TIME();
1153		s->time->h = timelib_get_nr((char **) &ptr, 2);
1154		if (*ptr == ':' || *ptr == '.') {
1155			s->time->i = timelib_get_nr((char **) &ptr, 2);
1156			if (*ptr == ':' || *ptr == '.') {
1157				s->time->s = timelib_get_nr((char **) &ptr, 2);
1158			}
1159		}
1160		s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1161		TIMELIB_DEINIT;
1162		return TIMELIB_TIME12;
1163	}
1164
1165	mssqltime
1166	{
1167		DEBUG_OUTPUT("mssqltime");
1168		TIMELIB_INIT;
1169		TIMELIB_HAVE_TIME();
1170		s->time->h = timelib_get_nr((char **) &ptr, 2);
1171		s->time->i = timelib_get_nr((char **) &ptr, 2);
1172		if (*ptr == ':' || *ptr == '.') {
1173			s->time->s = timelib_get_nr((char **) &ptr, 2);
1174
1175			if (*ptr == ':' || *ptr == '.') {
1176				s->time->f = timelib_get_frac_nr((char **) &ptr, 8);
1177			}
1178		}
1179		timelib_eat_spaces((char **) &ptr);
1180		s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1181		TIMELIB_DEINIT;
1182		return TIMELIB_TIME24_WITH_ZONE;
1183	}
1184
1185	timeshort24 | timelong24 /* | iso8601short | iso8601norm */ | iso8601long /*| iso8601shorttz | iso8601normtz | iso8601longtz*/
1186	{
1187		int tz_not_found;
1188		DEBUG_OUTPUT("timeshort24 | timelong24 | iso8601long");
1189		TIMELIB_INIT;
1190		TIMELIB_HAVE_TIME();
1191		s->time->h = timelib_get_nr((char **) &ptr, 2);
1192		s->time->i = timelib_get_nr((char **) &ptr, 2);
1193		if (*ptr == ':' || *ptr == '.') {
1194			s->time->s = timelib_get_nr((char **) &ptr, 2);
1195
1196			if (*ptr == '.') {
1197				s->time->f = timelib_get_frac_nr((char **) &ptr, 8);
1198			}
1199		}
1200
1201		if (*ptr != '\0') {
1202			s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1203			if (tz_not_found) {
1204				add_error(s, "The timezone could not be found in the database");
1205			}
1206		}
1207		TIMELIB_DEINIT;
1208		return TIMELIB_TIME24_WITH_ZONE;
1209	}
1210
1211	gnunocolon
1212	{
1213		DEBUG_OUTPUT("gnunocolon");
1214		TIMELIB_INIT;
1215		switch (s->time->have_time) {
1216			case 0:
1217				s->time->h = timelib_get_nr((char **) &ptr, 2);
1218				s->time->i = timelib_get_nr((char **) &ptr, 2);
1219				s->time->s = 0;
1220				break;
1221			case 1:
1222				s->time->y = timelib_get_nr((char **) &ptr, 4);
1223				break;
1224			default:
1225				TIMELIB_DEINIT;
1226				add_error(s, "Double time specification");
1227				return TIMELIB_ERROR;
1228		}
1229		s->time->have_time++;
1230		TIMELIB_DEINIT;
1231		return TIMELIB_GNU_NOCOLON;
1232	}
1233/*
1234	gnunocolontz
1235	{
1236		DEBUG_OUTPUT("gnunocolontz");
1237		TIMELIB_INIT;
1238		switch (s->time->have_time) {
1239			case 0:
1240				s->time->h = timelib_get_nr((char **) &ptr, 2);
1241				s->time->i = timelib_get_nr((char **) &ptr, 2);
1242				s->time->s = 0;
1243				s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, s->tzdb, tz_get_wrapper);
1244				break;
1245			case 1:
1246				s->time->y = timelib_get_nr((char **) &ptr, 4);
1247				break;
1248			default:
1249				TIMELIB_DEINIT;
1250				return TIMELIB_ERROR;
1251		}
1252		s->time->have_time++;
1253		TIMELIB_DEINIT;
1254		return TIMELIB_GNU_NOCOLON_TZ;
1255	}
1256*/
1257	iso8601nocolon /*| iso8601nocolontz*/
1258	{
1259		int tz_not_found;
1260		DEBUG_OUTPUT("iso8601nocolon");
1261		TIMELIB_INIT;
1262		TIMELIB_HAVE_TIME();
1263		s->time->h = timelib_get_nr((char **) &ptr, 2);
1264		s->time->i = timelib_get_nr((char **) &ptr, 2);
1265		s->time->s = timelib_get_nr((char **) &ptr, 2);
1266
1267		if (*ptr != '\0') {
1268			s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1269			if (tz_not_found) {
1270				add_error(s, "The timezone could not be found in the database");
1271			}
1272		}
1273		TIMELIB_DEINIT;
1274		return TIMELIB_ISO_NOCOLON;
1275	}
1276
1277	americanshort | american
1278	{
1279		int length = 0;
1280		DEBUG_OUTPUT("americanshort | american");
1281		TIMELIB_INIT;
1282		TIMELIB_HAVE_DATE();
1283		s->time->m = timelib_get_nr((char **) &ptr, 2);
1284		s->time->d = timelib_get_nr((char **) &ptr, 2);
1285		if (*ptr == '/') {
1286			s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1287			TIMELIB_PROCESS_YEAR(s->time->y, length);
1288		}
1289		TIMELIB_DEINIT;
1290		return TIMELIB_AMERICAN;
1291	}
1292
1293	iso8601date4 | iso8601dateslash | dateslash
1294	{
1295		DEBUG_OUTPUT("iso8601date4 | iso8601date2 | iso8601dateslash | dateslash");
1296		TIMELIB_INIT;
1297		TIMELIB_HAVE_DATE();
1298		s->time->y = timelib_get_unsigned_nr((char **) &ptr, 4);
1299		s->time->m = timelib_get_nr((char **) &ptr, 2);
1300		s->time->d = timelib_get_nr((char **) &ptr, 2);
1301		TIMELIB_DEINIT;
1302		return TIMELIB_ISO_DATE;
1303	}
1304
1305	iso8601date2
1306	{
1307		int length = 0;
1308		DEBUG_OUTPUT("iso8601date2");
1309		TIMELIB_INIT;
1310		TIMELIB_HAVE_DATE();
1311		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1312		s->time->m = timelib_get_nr((char **) &ptr, 2);
1313		s->time->d = timelib_get_nr((char **) &ptr, 2);
1314		TIMELIB_PROCESS_YEAR(s->time->y, length);
1315		TIMELIB_DEINIT;
1316		return TIMELIB_ISO_DATE;
1317	}
1318
1319	gnudateshorter
1320	{
1321		int length = 0;
1322		DEBUG_OUTPUT("gnudateshorter");
1323		TIMELIB_INIT;
1324		TIMELIB_HAVE_DATE();
1325		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1326		s->time->m = timelib_get_nr((char **) &ptr, 2);
1327		s->time->d = 1;
1328		TIMELIB_PROCESS_YEAR(s->time->y, length);
1329		TIMELIB_DEINIT;
1330		return TIMELIB_ISO_DATE;
1331	}
1332
1333	gnudateshort
1334	{
1335		int length = 0;
1336		DEBUG_OUTPUT("gnudateshort");
1337		TIMELIB_INIT;
1338		TIMELIB_HAVE_DATE();
1339		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1340		s->time->m = timelib_get_nr((char **) &ptr, 2);
1341		s->time->d = timelib_get_nr((char **) &ptr, 2);
1342		TIMELIB_PROCESS_YEAR(s->time->y, length);
1343		TIMELIB_DEINIT;
1344		return TIMELIB_ISO_DATE;
1345	}
1346
1347	datefull
1348	{
1349		int length = 0;
1350		DEBUG_OUTPUT("datefull");
1351		TIMELIB_INIT;
1352		TIMELIB_HAVE_DATE();
1353		s->time->d = timelib_get_nr((char **) &ptr, 2);
1354		timelib_skip_day_suffix((char **) &ptr);
1355		s->time->m = timelib_get_month((char **) &ptr);
1356		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1357		TIMELIB_PROCESS_YEAR(s->time->y, length);
1358		TIMELIB_DEINIT;
1359		return TIMELIB_DATE_FULL;
1360	}
1361
1362	pointeddate4
1363	{
1364		DEBUG_OUTPUT("pointed date YYYY");
1365		TIMELIB_INIT;
1366		TIMELIB_HAVE_DATE();
1367		s->time->d = timelib_get_nr((char **) &ptr, 2);
1368		s->time->m = timelib_get_nr((char **) &ptr, 2);
1369		s->time->y = timelib_get_nr((char **) &ptr, 4);
1370		TIMELIB_DEINIT;
1371		return TIMELIB_DATE_FULL_POINTED;
1372	}
1373
1374	pointeddate2
1375	{
1376		int length = 0;
1377		DEBUG_OUTPUT("pointed date YY");
1378		TIMELIB_INIT;
1379		TIMELIB_HAVE_DATE();
1380		s->time->d = timelib_get_nr((char **) &ptr, 2);
1381		s->time->m = timelib_get_nr((char **) &ptr, 2);
1382		s->time->y = timelib_get_nr_ex((char **) &ptr, 2, &length);
1383		TIMELIB_PROCESS_YEAR(s->time->y, length);
1384		TIMELIB_DEINIT;
1385		return TIMELIB_DATE_FULL_POINTED;
1386	}
1387
1388	datenoday
1389	{
1390		int length = 0;
1391		DEBUG_OUTPUT("datenoday");
1392		TIMELIB_INIT;
1393		TIMELIB_HAVE_DATE();
1394		s->time->m = timelib_get_month((char **) &ptr);
1395		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1396		s->time->d = 1;
1397		TIMELIB_PROCESS_YEAR(s->time->y, length);
1398		TIMELIB_DEINIT;
1399		return TIMELIB_DATE_NO_DAY;
1400	}
1401
1402	datenodayrev
1403	{
1404		int length = 0;
1405		DEBUG_OUTPUT("datenodayrev");
1406		TIMELIB_INIT;
1407		TIMELIB_HAVE_DATE();
1408		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1409		s->time->m = timelib_get_month((char **) &ptr);
1410		s->time->d = 1;
1411		TIMELIB_PROCESS_YEAR(s->time->y, length);
1412		TIMELIB_DEINIT;
1413		return TIMELIB_DATE_NO_DAY;
1414	}
1415
1416	datetextual | datenoyear
1417	{
1418		int length = 0;
1419		DEBUG_OUTPUT("datetextual | datenoyear");
1420		TIMELIB_INIT;
1421		TIMELIB_HAVE_DATE();
1422		s->time->m = timelib_get_month((char **) &ptr);
1423		s->time->d = timelib_get_nr((char **) &ptr, 2);
1424		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1425		TIMELIB_PROCESS_YEAR(s->time->y, length);
1426		TIMELIB_DEINIT;
1427		return TIMELIB_DATE_TEXT;
1428	}
1429
1430	datenoyearrev
1431	{
1432		DEBUG_OUTPUT("datenoyearrev");
1433		TIMELIB_INIT;
1434		TIMELIB_HAVE_DATE();
1435		s->time->d = timelib_get_nr((char **) &ptr, 2);
1436		timelib_skip_day_suffix((char **) &ptr);
1437		s->time->m = timelib_get_month((char **) &ptr);
1438		TIMELIB_DEINIT;
1439		return TIMELIB_DATE_TEXT;
1440	}
1441
1442	datenocolon
1443	{
1444		DEBUG_OUTPUT("datenocolon");
1445		TIMELIB_INIT;
1446		TIMELIB_HAVE_DATE();
1447		s->time->y = timelib_get_nr((char **) &ptr, 4);
1448		s->time->m = timelib_get_nr((char **) &ptr, 2);
1449		s->time->d = timelib_get_nr((char **) &ptr, 2);
1450		TIMELIB_DEINIT;
1451		return TIMELIB_DATE_NOCOLON;
1452	}
1453
1454	xmlrpc | xmlrpcnocolon | soap | wddx | exif
1455	{
1456		int tz_not_found;
1457		DEBUG_OUTPUT("xmlrpc | xmlrpcnocolon | soap | wddx | exif");
1458		TIMELIB_INIT;
1459		TIMELIB_HAVE_TIME();
1460		TIMELIB_HAVE_DATE();
1461		s->time->y = timelib_get_nr((char **) &ptr, 4);
1462		s->time->m = timelib_get_nr((char **) &ptr, 2);
1463		s->time->d = timelib_get_nr((char **) &ptr, 2);
1464		s->time->h = timelib_get_nr((char **) &ptr, 2);
1465		s->time->i = timelib_get_nr((char **) &ptr, 2);
1466		s->time->s = timelib_get_nr((char **) &ptr, 2);
1467		if (*ptr == '.') {
1468			s->time->f = timelib_get_frac_nr((char **) &ptr, 9);
1469			if (*ptr) { /* timezone is optional */
1470				s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1471				if (tz_not_found) {
1472					add_error(s, "The timezone could not be found in the database");
1473				}
1474			}
1475		}
1476		TIMELIB_DEINIT;
1477		return TIMELIB_XMLRPC_SOAP;
1478	}
1479
1480	pgydotd
1481	{
1482		int length = 0;
1483		DEBUG_OUTPUT("pgydotd");
1484		TIMELIB_INIT;
1485		TIMELIB_HAVE_DATE();
1486		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1487		s->time->d = timelib_get_nr((char **) &ptr, 3);
1488		s->time->m = 1;
1489		TIMELIB_PROCESS_YEAR(s->time->y, length);
1490		TIMELIB_DEINIT;
1491		return TIMELIB_PG_YEARDAY;
1492	}
1493
1494	isoweekday
1495	{
1496		timelib_sll w, d;
1497		DEBUG_OUTPUT("isoweekday");
1498		TIMELIB_INIT;
1499		TIMELIB_HAVE_DATE();
1500		TIMELIB_HAVE_RELATIVE();
1501
1502		s->time->y = timelib_get_nr((char **) &ptr, 4);
1503		w = timelib_get_nr((char **) &ptr, 2);
1504		d = timelib_get_nr((char **) &ptr, 1);
1505		s->time->m = 1;
1506		s->time->d = 1;
1507		s->time->relative.d = timelib_daynr_from_weeknr(s->time->y, w, d);
1508
1509		TIMELIB_DEINIT;
1510		return TIMELIB_ISO_WEEK;
1511	}
1512
1513	isoweek
1514	{
1515		timelib_sll w, d;
1516		DEBUG_OUTPUT("isoweek");
1517		TIMELIB_INIT;
1518		TIMELIB_HAVE_DATE();
1519		TIMELIB_HAVE_RELATIVE();
1520
1521		s->time->y = timelib_get_nr((char **) &ptr, 4);
1522		w = timelib_get_nr((char **) &ptr, 2);
1523		d = 1;
1524		s->time->m = 1;
1525		s->time->d = 1;
1526		s->time->relative.d = timelib_daynr_from_weeknr(s->time->y, w, d);
1527
1528		TIMELIB_DEINIT;
1529		return TIMELIB_ISO_WEEK;
1530	}
1531
1532	pgtextshort
1533	{
1534		int length = 0;
1535		DEBUG_OUTPUT("pgtextshort");
1536		TIMELIB_INIT;
1537		TIMELIB_HAVE_DATE();
1538		s->time->m = timelib_get_month((char **) &ptr);
1539		s->time->d = timelib_get_nr((char **) &ptr, 2);
1540		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1541		TIMELIB_PROCESS_YEAR(s->time->y, length);
1542		TIMELIB_DEINIT;
1543		return TIMELIB_PG_TEXT;
1544	}
1545
1546	pgtextreverse
1547	{
1548		int length = 0;
1549		DEBUG_OUTPUT("pgtextreverse");
1550		TIMELIB_INIT;
1551		TIMELIB_HAVE_DATE();
1552		s->time->y = timelib_get_nr_ex((char **) &ptr, 4, &length);
1553		s->time->m = timelib_get_month((char **) &ptr);
1554		s->time->d = timelib_get_nr((char **) &ptr, 2);
1555		TIMELIB_PROCESS_YEAR(s->time->y, length);
1556		TIMELIB_DEINIT;
1557		return TIMELIB_PG_TEXT;
1558	}
1559
1560	clf
1561	{
1562		int tz_not_found;
1563		DEBUG_OUTPUT("clf");
1564		TIMELIB_INIT;
1565		TIMELIB_HAVE_TIME();
1566		TIMELIB_HAVE_DATE();
1567		s->time->d = timelib_get_nr((char **) &ptr, 2);
1568		s->time->m = timelib_get_month((char **) &ptr);
1569		s->time->y = timelib_get_nr((char **) &ptr, 4);
1570		s->time->h = timelib_get_nr((char **) &ptr, 2);
1571		s->time->i = timelib_get_nr((char **) &ptr, 2);
1572		s->time->s = timelib_get_nr((char **) &ptr, 2);
1573		s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1574		if (tz_not_found) {
1575			add_error(s, "The timezone could not be found in the database");
1576		}
1577		TIMELIB_DEINIT;
1578		return TIMELIB_CLF;
1579	}
1580
1581	year4
1582	{
1583		DEBUG_OUTPUT("year4");
1584		TIMELIB_INIT;
1585		s->time->y = timelib_get_nr((char **) &ptr, 4);
1586		TIMELIB_DEINIT;
1587		return TIMELIB_CLF;
1588	}
1589
1590	ago
1591	{
1592		DEBUG_OUTPUT("ago");
1593		TIMELIB_INIT;
1594		s->time->relative.y = 0 - s->time->relative.y;
1595		s->time->relative.m = 0 - s->time->relative.m;
1596		s->time->relative.d = 0 - s->time->relative.d;
1597		s->time->relative.h = 0 - s->time->relative.h;
1598		s->time->relative.i = 0 - s->time->relative.i;
1599		s->time->relative.s = 0 - s->time->relative.s;
1600		s->time->relative.weekday = 0 - s->time->relative.weekday;
1601		if (s->time->relative.weekday == 0) {
1602			s->time->relative.weekday = -7;
1603		}
1604		if (s->time->relative.have_special_relative && s->time->relative.special.type == TIMELIB_SPECIAL_WEEKDAY) {
1605			s->time->relative.special.amount = 0 - s->time->relative.special.amount;
1606		}
1607		TIMELIB_DEINIT;
1608		return TIMELIB_AGO;
1609	}
1610
1611	daytext
1612	{
1613		const timelib_relunit* relunit;
1614		DEBUG_OUTPUT("daytext");
1615		TIMELIB_INIT;
1616		TIMELIB_HAVE_RELATIVE();
1617		TIMELIB_HAVE_WEEKDAY_RELATIVE();
1618		TIMELIB_UNHAVE_TIME();
1619		relunit = timelib_lookup_relunit((char**) &ptr);
1620		s->time->relative.weekday = relunit->multiplier;
1621		if (s->time->relative.weekday_behavior != 2) {
1622			s->time->relative.weekday_behavior = 1;
1623		}
1624
1625		TIMELIB_DEINIT;
1626		return TIMELIB_WEEKDAY;
1627	}
1628
1629	relativetextweek
1630	{
1631		timelib_sll i;
1632		int         behavior = 0;
1633		DEBUG_OUTPUT("relativetextweek");
1634		TIMELIB_INIT;
1635		TIMELIB_HAVE_RELATIVE();
1636
1637		while(*ptr) {
1638			i = timelib_get_relative_text((char **) &ptr, &behavior);
1639			timelib_eat_spaces((char **) &ptr);
1640			timelib_set_relative((char **) &ptr, i, behavior, s);
1641			s->time->relative.weekday_behavior = 2;
1642
1643			/* to handle the format weekday + last/this/next week */
1644			if (s->time->relative.have_weekday_relative == 0) {
1645				TIMELIB_HAVE_WEEKDAY_RELATIVE();
1646				s->time->relative.weekday = 1;
1647			}
1648		}
1649		TIMELIB_DEINIT;
1650		return TIMELIB_RELATIVE;
1651	}
1652
1653	relativetext
1654	{
1655		timelib_sll i;
1656		int         behavior = 0;
1657		DEBUG_OUTPUT("relativetext");
1658		TIMELIB_INIT;
1659		TIMELIB_HAVE_RELATIVE();
1660
1661		while(*ptr) {
1662			i = timelib_get_relative_text((char **) &ptr, &behavior);
1663			timelib_eat_spaces((char **) &ptr);
1664			timelib_set_relative((char **) &ptr, i, behavior, s);
1665		}
1666		TIMELIB_DEINIT;
1667		return TIMELIB_RELATIVE;
1668	}
1669
1670	monthfull | monthabbr
1671	{
1672		DEBUG_OUTPUT("monthtext");
1673		TIMELIB_INIT;
1674		TIMELIB_HAVE_DATE();
1675		s->time->m = timelib_lookup_month((char **) &ptr);
1676		TIMELIB_DEINIT;
1677		return TIMELIB_DATE_TEXT;
1678	}
1679
1680	tzcorrection | tz
1681	{
1682		int tz_not_found;
1683		DEBUG_OUTPUT("tzcorrection | tz");
1684		TIMELIB_INIT;
1685		TIMELIB_HAVE_TZ();
1686		s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1687		if (tz_not_found) {
1688			add_error(s, "The timezone could not be found in the database");
1689		}
1690		TIMELIB_DEINIT;
1691		return TIMELIB_TIMEZONE;
1692	}
1693
1694	dateshortwithtimeshort12 | dateshortwithtimelong12
1695	{
1696		DEBUG_OUTPUT("dateshortwithtimeshort12 | dateshortwithtimelong12");
1697		TIMELIB_INIT;
1698		TIMELIB_HAVE_DATE();
1699		s->time->m = timelib_get_month((char **) &ptr);
1700		s->time->d = timelib_get_nr((char **) &ptr, 2);
1701
1702		TIMELIB_HAVE_TIME();
1703		s->time->h = timelib_get_nr((char **) &ptr, 2);
1704		s->time->i = timelib_get_nr((char **) &ptr, 2);
1705		if (*ptr == ':' || *ptr == '.') {
1706			s->time->s = timelib_get_nr((char **) &ptr, 2);
1707
1708			if (*ptr == '.') {
1709				s->time->f = timelib_get_frac_nr((char **) &ptr, 8);
1710			}
1711		}
1712
1713		s->time->h += timelib_meridian((char **) &ptr, s->time->h);
1714		TIMELIB_DEINIT;
1715		return TIMELIB_SHORTDATE_WITH_TIME;
1716	}
1717
1718	dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz
1719	{
1720		int tz_not_found;
1721		DEBUG_OUTPUT("dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz");
1722		TIMELIB_INIT;
1723		TIMELIB_HAVE_DATE();
1724		s->time->m = timelib_get_month((char **) &ptr);
1725		s->time->d = timelib_get_nr((char **) &ptr, 2);
1726
1727		TIMELIB_HAVE_TIME();
1728		s->time->h = timelib_get_nr((char **) &ptr, 2);
1729		s->time->i = timelib_get_nr((char **) &ptr, 2);
1730		if (*ptr == ':') {
1731			s->time->s = timelib_get_nr((char **) &ptr, 2);
1732
1733			if (*ptr == '.') {
1734				s->time->f = timelib_get_frac_nr((char **) &ptr, 8);
1735			}
1736		}
1737
1738		if (*ptr != '\0') {
1739			s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
1740			if (tz_not_found) {
1741				add_error(s, "The timezone could not be found in the database");
1742			}
1743		}
1744		TIMELIB_DEINIT;
1745		return TIMELIB_SHORTDATE_WITH_TIME;
1746	}
1747
1748	relative
1749	{
1750		timelib_ull i;
1751		DEBUG_OUTPUT("relative");
1752		TIMELIB_INIT;
1753		TIMELIB_HAVE_RELATIVE();
1754
1755		while(*ptr) {
1756			i = timelib_get_unsigned_nr((char **) &ptr, 24);
1757			timelib_eat_spaces((char **) &ptr);
1758			timelib_set_relative((char **) &ptr, i, 1, s);
1759		}
1760		TIMELIB_DEINIT;
1761		return TIMELIB_RELATIVE;
1762	}
1763
1764	[ .,\t]
1765	{
1766		goto std;
1767	}
1768
1769	"\000"|"\n"
1770	{
1771		s->pos = cursor; s->line++;
1772		goto std;
1773	}
1774
1775	any
1776	{
1777		add_error(s, "Unexpected character");
1778		goto std;
1779	}
1780*/
1781}
1782
1783/*!max:re2c */
1784
1785timelib_time* timelib_strtotime(char *s, int len, struct timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper)
1786{
1787	Scanner in;
1788	int t;
1789	char *e = s + len - 1;
1790
1791	memset(&in, 0, sizeof(in));
1792	in.errors = malloc(sizeof(struct timelib_error_container));
1793	in.errors->warning_count = 0;
1794	in.errors->warning_messages = NULL;
1795	in.errors->error_count = 0;
1796	in.errors->error_messages = NULL;
1797
1798	if (len > 0) {
1799		while (isspace(*s) && s < e) {
1800			s++;
1801		}
1802		while (isspace(*e) && e > s) {
1803			e--;
1804		}
1805	}
1806	if (e - s < 0) {
1807		in.time = timelib_time_ctor();
1808		add_error(&in, "Empty string");
1809		if (errors) {
1810			*errors = in.errors;
1811		} else {
1812			timelib_error_container_dtor(in.errors);
1813		}
1814		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;
1815		in.time->is_localtime = in.time->zone_type = 0;
1816		return in.time;
1817	}
1818	e++;
1819
1820	in.str = malloc((e - s) + YYMAXFILL);
1821	memset(in.str, 0, (e - s) + YYMAXFILL);
1822	memcpy(in.str, s, (e - s));
1823	in.lim = in.str + (e - s) + YYMAXFILL;
1824	in.cur = in.str;
1825	in.time = timelib_time_ctor();
1826	in.time->y = TIMELIB_UNSET;
1827	in.time->d = TIMELIB_UNSET;
1828	in.time->m = TIMELIB_UNSET;
1829	in.time->h = TIMELIB_UNSET;
1830	in.time->i = TIMELIB_UNSET;
1831	in.time->s = TIMELIB_UNSET;
1832	in.time->f = TIMELIB_UNSET;
1833	in.time->z = TIMELIB_UNSET;
1834	in.time->dst = TIMELIB_UNSET;
1835	in.tzdb = tzdb;
1836	in.time->is_localtime = 0;
1837	in.time->zone_type = 0;
1838
1839	do {
1840		t = scan(&in, tz_get_wrapper);
1841#ifdef DEBUG_PARSER
1842		printf("%d\n", t);
1843#endif
1844	} while(t != EOI);
1845
1846	/* do funky checking whether the parsed time was valid time */
1847	if (in.time->have_time && !timelib_valid_time( in.time->h, in.time->i, in.time->s)) {
1848		add_warning(&in, "The parsed time was invalid");
1849	}
1850	/* do funky checking whether the parsed date was valid date */
1851	if (in.time->have_date && !timelib_valid_date( in.time->y, in.time->m, in.time->d)) {
1852		add_warning(&in, "The parsed date was invalid");
1853	}
1854
1855	free(in.str);
1856	if (errors) {
1857		*errors = in.errors;
1858	} else {
1859		timelib_error_container_dtor(in.errors);
1860	}
1861	return in.time;
1862}
1863
1864#define TIMELIB_CHECK_NUMBER                                           \
1865		if (strchr("0123456789", *ptr) == NULL)                        \
1866		{                                                              \
1867			add_pbf_error(s, "Unexpected data found.", string, begin); \
1868		}
1869
1870static void timelib_time_reset_fields(timelib_time *time)
1871{
1872	assert(time != NULL);
1873
1874	time->y = 1970;
1875	time->m = 1;
1876	time->d = 1;
1877	time->h = time->i = time->s = 0;
1878	time->f = 0.0;
1879	time->tz_info = NULL;
1880}
1881
1882static void timelib_time_reset_unset_fields(timelib_time *time)
1883{
1884	assert(time != NULL);
1885
1886	if (time->y == TIMELIB_UNSET ) time->y = 1970;
1887	if (time->m == TIMELIB_UNSET ) time->m = 1;
1888	if (time->d == TIMELIB_UNSET ) time->d = 1;
1889	if (time->h == TIMELIB_UNSET ) time->h = 0;
1890	if (time->i == TIMELIB_UNSET ) time->i = 0;
1891	if (time->s == TIMELIB_UNSET ) time->s = 0;
1892	if (time->f == TIMELIB_UNSET ) time->f = 0.0;
1893}
1894
1895timelib_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)
1896{
1897	char       *fptr = format;
1898	char       *ptr = string;
1899	char       *begin;
1900	timelib_sll tmp;
1901	Scanner in;
1902	Scanner *s = &in;
1903	int allow_extra = 0;
1904
1905	memset(&in, 0, sizeof(in));
1906	in.errors = malloc(sizeof(struct timelib_error_container));
1907	in.errors->warning_count = 0;
1908	in.errors->warning_messages = NULL;
1909	in.errors->error_count = 0;
1910	in.errors->error_messages = NULL;
1911
1912	in.time = timelib_time_ctor();
1913	in.time->y = TIMELIB_UNSET;
1914	in.time->d = TIMELIB_UNSET;
1915	in.time->m = TIMELIB_UNSET;
1916	in.time->h = TIMELIB_UNSET;
1917	in.time->i = TIMELIB_UNSET;
1918	in.time->s = TIMELIB_UNSET;
1919	in.time->f = TIMELIB_UNSET;
1920	in.time->z = TIMELIB_UNSET;
1921	in.time->dst = TIMELIB_UNSET;
1922	in.tzdb = tzdb;
1923	in.time->is_localtime = 0;
1924	in.time->zone_type = 0;
1925
1926	/* Loop over the format string */
1927	while (*fptr && *ptr) {
1928		begin = ptr;
1929		switch (*fptr) {
1930			case 'D': /* three letter day */
1931			case 'l': /* full day */
1932				{
1933					const timelib_relunit* tmprel = 0;
1934
1935					tmprel = timelib_lookup_relunit((char **) &ptr);
1936					if (!tmprel) {
1937						add_pbf_error(s, "A textual day could not be found", string, begin);
1938						break;
1939					} else {
1940						in.time->have_relative = 1;
1941						in.time->relative.have_weekday_relative = 1;
1942						in.time->relative.weekday = tmprel->multiplier;
1943						in.time->relative.weekday_behavior = 1;
1944					}
1945				}
1946				break;
1947			case 'd': /* two digit day, with leading zero */
1948			case 'j': /* two digit day, without leading zero */
1949				TIMELIB_CHECK_NUMBER;
1950				if ((s->time->d = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) {
1951					add_pbf_error(s, "A two digit day could not be found", string, begin);
1952				}
1953				break;
1954			case 'S': /* day suffix, ignored, nor checked */
1955				timelib_skip_day_suffix((char **) &ptr);
1956				break;
1957			case 'z': /* day of year - resets month (0 based) - also initializes everything else to !TIMELIB_UNSET */
1958				TIMELIB_CHECK_NUMBER;
1959				if ((tmp = timelib_get_nr((char **) &ptr, 3)) == TIMELIB_UNSET) {
1960					add_pbf_error(s, "A three digit day-of-year could not be found", string, begin);
1961				} else {
1962					s->time->m = 1;
1963					s->time->d = tmp + 1;
1964					timelib_do_normalize(s->time);
1965				}
1966				break;
1967
1968			case 'm': /* two digit month, with leading zero */
1969			case 'n': /* two digit month, without leading zero */
1970				TIMELIB_CHECK_NUMBER;
1971				if ((s->time->m = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) {
1972					add_pbf_error(s, "A two digit month could not be found", string, begin);
1973				}
1974				break;
1975			case 'M': /* three letter month */
1976			case 'F': /* full month */
1977				tmp = timelib_lookup_month((char **) &ptr);
1978				if (!tmp) {
1979					add_pbf_error(s, "A textual month could not be found", string, begin);
1980				} else {
1981					s->time->m = tmp;
1982				}
1983				break;
1984			case 'y': /* two digit year */
1985				{
1986					int length = 0;
1987					TIMELIB_CHECK_NUMBER;
1988					if ((s->time->y = timelib_get_nr_ex((char **) &ptr, 2, &length)) == TIMELIB_UNSET) {
1989						add_pbf_error(s, "A two digit year could not be found", string, begin);
1990					}
1991					TIMELIB_PROCESS_YEAR(s->time->y, length);
1992				}
1993				break;
1994			case 'Y': /* four digit year */
1995				TIMELIB_CHECK_NUMBER;
1996				if ((s->time->y = timelib_get_nr((char **) &ptr, 4)) == TIMELIB_UNSET) {
1997					add_pbf_error(s, "A four digit year could not be found", string, begin);
1998				}
1999				break;
2000			case 'g': /* two digit hour, with leading zero */
2001			case 'h': /* two digit hour, without leading zero */
2002				TIMELIB_CHECK_NUMBER;
2003				if ((s->time->h = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) {
2004					add_pbf_error(s, "A two digit hour could not be found", string, begin);
2005				}
2006				if (s->time->h > 12) {
2007					add_pbf_error(s, "Hour can not be higher than 12", string, begin);
2008				}
2009				break;
2010			case 'G': /* two digit hour, with leading zero */
2011			case 'H': /* two digit hour, without leading zero */
2012				TIMELIB_CHECK_NUMBER;
2013				if ((s->time->h = timelib_get_nr((char **) &ptr, 2)) == TIMELIB_UNSET) {
2014					add_pbf_error(s, "A two digit hour could not be found", string, begin);
2015				}
2016				break;
2017			case 'a': /* am/pm/a.m./p.m. */
2018			case 'A': /* AM/PM/A.M./P.M. */
2019				if (s->time->h == TIMELIB_UNSET) {
2020					add_pbf_error(s, "Meridian can only come after an hour has been found", string, begin);
2021				} else if ((tmp = timelib_meridian_with_check((char **) &ptr, s->time->h)) == TIMELIB_UNSET) {
2022					add_pbf_error(s, "A meridian could not be found", string, begin);
2023				} else {
2024					s->time->h += tmp;
2025				}
2026				break;
2027			case 'i': /* two digit minute, with leading zero */
2028				{
2029					int length;
2030					timelib_sll min;
2031
2032					TIMELIB_CHECK_NUMBER;
2033					min = timelib_get_nr_ex((char **) &ptr, 2, &length);
2034					if (min == TIMELIB_UNSET || length != 2) {
2035						add_pbf_error(s, "A two digit minute could not be found", string, begin);
2036					} else {
2037						s->time->i = min;
2038					}
2039				}
2040				break;
2041			case 's': /* two digit second, with leading zero */
2042				{
2043					int length;
2044					timelib_sll sec;
2045
2046					TIMELIB_CHECK_NUMBER;
2047					sec = timelib_get_nr_ex((char **) &ptr, 2, &length);
2048					if (sec == TIMELIB_UNSET || length != 2) {
2049						add_pbf_error(s, "A two digit second could not be found", string, begin);
2050					} else {
2051						s->time->s = sec;
2052					}
2053				}
2054				break;
2055			case 'u': /* up to six digit millisecond */
2056				{
2057					double f;
2058					char *tptr;
2059
2060					TIMELIB_CHECK_NUMBER;
2061					tptr = ptr;
2062					if ((f = timelib_get_nr((char **) &ptr, 6)) == TIMELIB_UNSET || (ptr - tptr < 1)) {
2063						add_pbf_error(s, "A six digit millisecond could not be found", string, begin);
2064					} else {
2065						s->time->f = (f / pow(10, (ptr - tptr)));
2066					}
2067				}
2068				break;
2069			case ' ': /* any sort of whitespace (' ' and \t) */
2070				timelib_eat_spaces((char **) &ptr);
2071				break;
2072			case 'U': /* epoch seconds */
2073				TIMELIB_CHECK_NUMBER;
2074				TIMELIB_HAVE_RELATIVE();
2075				tmp = timelib_get_unsigned_nr((char **) &ptr, 24);
2076				s->time->y = 1970;
2077				s->time->m = 1;
2078				s->time->d = 1;
2079				s->time->h = s->time->i = s->time->s = 0;
2080				s->time->f = 0.0;
2081				s->time->relative.s += tmp;
2082				s->time->is_localtime = 1;
2083				s->time->zone_type = TIMELIB_ZONETYPE_OFFSET;
2084				s->time->z = 0;
2085				s->time->dst = 0;
2086				break;
2087
2088			case 'e': /* timezone */
2089			case 'P': /* timezone */
2090			case 'T': /* timezone */
2091			case 'O': /* timezone */
2092				{
2093					int tz_not_found;
2094					s->time->z = timelib_get_zone((char **) &ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper);
2095					if (tz_not_found) {
2096						add_pbf_error(s, "The timezone could not be found in the database", string, begin);
2097					}
2098				}
2099				break;
2100
2101			case '#': /* separation symbol */
2102				if (*ptr == ';' || *ptr == ':' || *ptr == '/' || *ptr == '.' || *ptr == ',' || *ptr == '-' || *ptr == '(' || *ptr == ')') {
2103					++ptr;
2104				} else {
2105					add_pbf_error(s, "The separation symbol ([;:/.,-]) could not be found", string, begin);
2106				}
2107				break;
2108
2109			case ';':
2110			case ':':
2111			case '/':
2112			case '.':
2113			case ',':
2114			case '-':
2115			case '(':
2116			case ')':
2117				if (*ptr == *fptr) {
2118					++ptr;
2119				} else {
2120					add_pbf_error(s, "The separation symbol could not be found", string, begin);
2121				}
2122				break;
2123
2124			case '!': /* reset all fields to default */
2125				timelib_time_reset_fields(s->time);
2126				break; /* break intentionally not missing */
2127
2128			case '|': /* reset all fields to default when not set */
2129				timelib_time_reset_unset_fields(s->time);
2130				break; /* break intentionally not missing */
2131
2132			case '?': /* random char */
2133				++ptr;
2134				break;
2135
2136			case '\\': /* escaped char */
2137				if(!fptr[1]) {
2138					add_pbf_error(s, "Escaped character expected", string, begin);
2139					break;
2140				}
2141				fptr++;
2142				if (*ptr == *fptr) {
2143					++ptr;
2144				} else {
2145					add_pbf_error(s, "The escaped character could not be found", string, begin);
2146				}
2147				break;
2148
2149			case '*': /* random chars until a separator or number ([ \t.,:;/-0123456789]) */
2150				timelib_eat_until_separator((char **) &ptr);
2151				break;
2152
2153			case '+': /* allow extra chars in the format */
2154				allow_extra = 1;
2155				break;
2156
2157			default:
2158				if (*fptr != *ptr) {
2159					add_pbf_error(s, "The format separator does not match", string, begin);
2160				}
2161				ptr++;
2162		}
2163		fptr++;
2164	}
2165	if (*ptr) {
2166		if (allow_extra) {
2167			add_pbf_warning(s, "Trailing data", string, ptr);
2168		} else {
2169			add_pbf_error(s, "Trailing data", string, ptr);
2170		}
2171	}
2172	/* ignore trailing +'s */
2173	while (*fptr == '+') {
2174		fptr++;
2175	}
2176	if (*fptr) {
2177		/* Trailing | and ! specifiers are valid. */
2178		int done = 0;
2179		while (*fptr && !done) {
2180			switch (*fptr++) {
2181				case '!': /* reset all fields to default */
2182					timelib_time_reset_fields(s->time);
2183					break;
2184
2185				case '|': /* reset all fields to default when not set */
2186					timelib_time_reset_unset_fields(s->time);
2187					break;
2188
2189				default:
2190					add_pbf_error(s, "Data missing", string, ptr);
2191					done = 1;
2192			}
2193		}
2194	}
2195
2196	/* clean up a bit */
2197	if (s->time->h != TIMELIB_UNSET || s->time->i != TIMELIB_UNSET || s->time->s != TIMELIB_UNSET) {
2198		if (s->time->h == TIMELIB_UNSET ) {
2199			s->time->h = 0;
2200		}
2201		if (s->time->i == TIMELIB_UNSET ) {
2202			s->time->i = 0;
2203		}
2204		if (s->time->s == TIMELIB_UNSET ) {
2205			s->time->s = 0;
2206		}
2207	}
2208
2209	/* do funky checking whether the parsed time was valid time */
2210	if (s->time->h != TIMELIB_UNSET && s->time->i != TIMELIB_UNSET &&
2211		s->time->s != TIMELIB_UNSET &&
2212		!timelib_valid_time( s->time->h, s->time->i, s->time->s)) {
2213		add_pbf_warning(s, "The parsed time was invalid", string, ptr);
2214	}
2215	/* do funky checking whether the parsed date was valid date */
2216	if (s->time->y != TIMELIB_UNSET && s->time->m != TIMELIB_UNSET &&
2217		s->time->d != TIMELIB_UNSET &&
2218		!timelib_valid_date( s->time->y, s->time->m, s->time->d)) {
2219		add_pbf_warning(s, "The parsed date was invalid", string, ptr);
2220	}
2221
2222	if (errors) {
2223		*errors = in.errors;
2224	} else {
2225		timelib_error_container_dtor(in.errors);
2226	}
2227	return in.time;
2228}
2229
2230void timelib_fill_holes(timelib_time *parsed, timelib_time *now, int options)
2231{
2232	if (!(options & TIMELIB_OVERRIDE_TIME) && parsed->have_date && !parsed->have_time) {
2233		parsed->h = 0;
2234		parsed->i = 0;
2235		parsed->s = 0;
2236		parsed->f = 0;
2237	}
2238	if (parsed->y == TIMELIB_UNSET) parsed->y = now->y != TIMELIB_UNSET ? now->y : 0;
2239	if (parsed->d == TIMELIB_UNSET) parsed->d = now->d != TIMELIB_UNSET ? now->d : 0;
2240	if (parsed->m == TIMELIB_UNSET) parsed->m = now->m != TIMELIB_UNSET ? now->m : 0;
2241	if (parsed->h == TIMELIB_UNSET) parsed->h = now->h != TIMELIB_UNSET ? now->h : 0;
2242	if (parsed->i == TIMELIB_UNSET) parsed->i = now->i != TIMELIB_UNSET ? now->i : 0;
2243	if (parsed->s == TIMELIB_UNSET) parsed->s = now->s != TIMELIB_UNSET ? now->s : 0;
2244	if (parsed->f == TIMELIB_UNSET) parsed->f = now->f != TIMELIB_UNSET ? now->f : 0;
2245	if (parsed->z == TIMELIB_UNSET) parsed->z = now->z != TIMELIB_UNSET ? now->z : 0;
2246	if (parsed->dst == TIMELIB_UNSET) parsed->dst = now->dst != TIMELIB_UNSET ? now->dst : 0;
2247
2248	if (!parsed->tz_abbr) {
2249		parsed->tz_abbr = now->tz_abbr ? strdup(now->tz_abbr) : NULL;
2250	}
2251	if (!parsed->tz_info) {
2252		parsed->tz_info = now->tz_info ? (!(options & TIMELIB_NO_CLONE) ? timelib_tzinfo_clone(now->tz_info) : now->tz_info) : NULL;
2253	}
2254	if (parsed->zone_type == 0 && now->zone_type != 0) {
2255		parsed->zone_type = now->zone_type;
2256/*		parsed->tz_abbr = now->tz_abbr ? strdup(now->tz_abbr) : NULL;
2257		parsed->tz_info = now->tz_info ? timelib_tzinfo_clone(now->tz_info) : NULL;
2258*/		parsed->is_localtime = 1;
2259	}
2260/*	timelib_dump_date(parsed, 2);
2261	timelib_dump_date(now, 2);
2262*/
2263}
2264
2265char *timelib_timezone_id_from_abbr(const char *abbr, long gmtoffset, int isdst)
2266{
2267	const timelib_tz_lookup_table *tp;
2268
2269	tp = zone_search(abbr, gmtoffset, isdst);
2270	if (tp) {
2271		return (tp->full_tz_name);
2272	} else {
2273		return NULL;
2274	}
2275}
2276
2277const timelib_tz_lookup_table *timelib_timezone_abbreviations_list(void)
2278{
2279	return timelib_timezone_lookup;
2280}
2281
2282#ifdef DEBUG_PARSER_STUB
2283int main(void)
2284{
2285	timelib_time time = timelib_strtotime("May 12");
2286
2287	printf ("%04d-%02d-%02d %02d:%02d:%02d.%-5d %+04d %1d",
2288		time.y, time.m, time.d, time.h, time.i, time.s, time.f, time.z, time.dst);
2289	if (time.have_relative) {
2290		printf ("%3dY %3dM %3dD / %3dH %3dM %3dS",
2291			time.relative.y, time.relative.m, time.relative.d, time.relative.h, time.relative.i, time.relative.s);
2292	}
2293	if (time.have_weekday_relative) {
2294		printf (" / %d", time.relative.weekday);
2295	}
2296	if (time.have_weeknr_day) {
2297		printf(" / %dW%d", time.relative.weeknr_day.weeknr, time.relative.weeknr_day.dayofweek);
2298	}
2299	return 0;
2300}
2301#endif
2302
2303/*
2304 * vim: syntax=c
2305 */
2306