diff options
Diffstat (limited to 'src/third_party/timelib-2021.06/parse_date.re')
-rw-r--r-- | src/third_party/timelib-2021.06/parse_date.re | 2631 |
1 files changed, 2631 insertions, 0 deletions
diff --git a/src/third_party/timelib-2021.06/parse_date.re b/src/third_party/timelib-2021.06/parse_date.re new file mode 100644 index 00000000000..cf238340861 --- /dev/null +++ b/src/third_party/timelib-2021.06/parse_date.re @@ -0,0 +1,2631 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015-2019 Derick Rethans + * Copyright (c) 2018 MongoDB, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "timelib.h" +#include "timelib_private.h" + +#include <ctype.h> +#include <math.h> +#include <assert.h> + +#if defined(_MSC_VER) +# define strtoll(s, f, b) _atoi64(s) +#elif !defined(HAVE_STRTOLL) +# if defined(HAVE_ATOLL) +# define strtoll(s, f, b) atoll(s) +# else +# define strtoll(s, f, b) strtol(s, f, b) +# endif +#endif + +#define EOI 257 +#define TIME 258 +#define DATE 259 + +#define TIMELIB_XMLRPC_SOAP 260 +#define TIMELIB_TIME12 261 +#define TIMELIB_TIME24 262 +#define TIMELIB_GNU_NOCOLON 263 +#define TIMELIB_GNU_NOCOLON_TZ 264 +#define TIMELIB_ISO_NOCOLON 265 + +#define TIMELIB_AMERICAN 266 +#define TIMELIB_ISO_DATE 267 +#define TIMELIB_DATE_FULL 268 +#define TIMELIB_DATE_TEXT 269 +#define TIMELIB_DATE_NOCOLON 270 +#define TIMELIB_PG_YEARDAY 271 +#define TIMELIB_PG_TEXT 272 +#define TIMELIB_PG_REVERSE 273 +#define TIMELIB_CLF 274 +#define TIMELIB_DATE_NO_DAY 275 +#define TIMELIB_SHORTDATE_WITH_TIME 276 +#define TIMELIB_DATE_FULL_POINTED 277 +#define TIMELIB_TIME24_WITH_ZONE 278 +#define TIMELIB_ISO_WEEK 279 +#define TIMELIB_LF_DAY_OF_MONTH 280 +#define TIMELIB_WEEK_DAY_OF_MONTH 281 + +#define TIMELIB_TIMEZONE 300 +#define TIMELIB_AGO 301 + +#define TIMELIB_RELATIVE 310 + +#define TIMELIB_ERROR 999 + +/* Some compilers like AIX, defines uchar in sys/types.h */ +#undef uchar +typedef unsigned char uchar; + +#define BSIZE 8192 + +#define YYCTYPE uchar +#define YYCURSOR cursor +#define YYLIMIT s->lim +#define YYMARKER s->ptr +#define YYFILL(n) return EOI; + +#define RET(i) {s->cur = cursor; return i;} + +#define timelib_string_free timelib_free + +#define TIMELIB_HAVE_TIME() { if (s->time->have_time) { add_error(s, TIMELIB_ERR_DOUBLE_TIME, "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->us = 0; } } +#define TIMELIB_UNHAVE_TIME() { s->time->have_time = 0; s->time->h = 0; s->time->i = 0; s->time->s = 0; s->time->us = 0; } +#define TIMELIB_HAVE_DATE() { if (s->time->have_date) { add_error(s, TIMELIB_ERR_DOUBLE_DATE, "Double date specification"); timelib_string_free(str); return TIMELIB_ERROR; } else { s->time->have_date = 1; } } +#define TIMELIB_UNHAVE_DATE() { s->time->have_date = 0; s->time->d = 0; s->time->m = 0; s->time->y = 0; } +#define TIMELIB_HAVE_RELATIVE() { s->time->have_relative = 1; } +#define TIMELIB_HAVE_WEEKDAY_RELATIVE() { s->time->have_relative = 1; s->time->relative.have_weekday_relative = 1; } +#define TIMELIB_HAVE_SPECIAL_RELATIVE() { s->time->have_relative = 1; s->time->relative.have_special_relative = 1; } +#define TIMELIB_HAVE_TZ() { s->cur = cursor; if (s->time->have_zone) { s->time->have_zone > 1 ? add_error(s, TIMELIB_ERR_DOUBLE_TZ, "Double timezone specification") : add_warning(s, TIMELIB_WARN_DOUBLE_TZ, "Double timezone specification"); timelib_string_free(str); s->time->have_zone++; return TIMELIB_ERROR; } else { s->time->have_zone++; } } + +#define TIMELIB_INIT s->cur = cursor; str = timelib_string(s); ptr = str +#define TIMELIB_DEINIT timelib_string_free(str) +#define TIMELIB_ADJUST_RELATIVE_WEEKDAY() if (in->time.have_weekday_relative && (in.rel.d > 0)) { in.rel.d -= 7; } + +#define TIMELIB_PROCESS_YEAR(x, l) { \ + if (((x) == TIMELIB_UNSET) || ((l) >= 4)) { \ + /* (x) = 0; */ \ + } else if ((x) < 100) { \ + if ((x) < 70) { \ + (x) += 2000; \ + } else { \ + (x) += 1900; \ + } \ + } \ +} + +#ifdef DEBUG_PARSER +#define DEBUG_OUTPUT(s) printf("%s\n", s); +#define YYDEBUG(s,c) { if (s != -1) { printf("state: %d ", s); printf("[%c]\n", c); } } +#else +#define DEBUG_OUTPUT(s) +#define YYDEBUG(s,c) +#endif + +typedef struct _timelib_elems { + unsigned int c; /* Number of elements */ + char **v; /* Values */ +} timelib_elems; + +typedef struct _Scanner { + int fd; + uchar *lim, *str, *ptr, *cur, *tok, *pos; + unsigned int line, len; + timelib_error_container *errors; + + timelib_time *time; + const timelib_tzdb *tzdb; +} Scanner; + +typedef struct _timelib_lookup_table { + const char *name; + int type; + int value; +} timelib_lookup_table; + +typedef struct _timelib_relunit { + const char *name; + int unit; + int multiplier; +} timelib_relunit; + +/* The timezone table. */ +static const timelib_tz_lookup_table timelib_timezone_lookup[] = { +#include "timezonemap.h" + { NULL, 0, 0, NULL }, +}; + +static const timelib_tz_lookup_table timelib_timezone_fallbackmap[] = { +#include "fallbackmap.h" + { NULL, 0, 0, NULL }, +}; + +static const timelib_tz_lookup_table timelib_timezone_utc[] = { + { "utc", 0, 0, "UTC" }, +}; + +static timelib_relunit const timelib_relunit_lookup[] = { + { "ms", TIMELIB_MICROSEC, 1000 }, + { "msec", TIMELIB_MICROSEC, 1000 }, + { "msecs", TIMELIB_MICROSEC, 1000 }, + { "millisecond", TIMELIB_MICROSEC, 1000 }, + { "milliseconds", TIMELIB_MICROSEC, 1000 }, + { "µs", TIMELIB_MICROSEC, 1 }, + { "usec", TIMELIB_MICROSEC, 1 }, + { "usecs", TIMELIB_MICROSEC, 1 }, + { "µsec", TIMELIB_MICROSEC, 1 }, + { "µsecs", TIMELIB_MICROSEC, 1 }, + { "microsecond", TIMELIB_MICROSEC, 1 }, + { "microseconds", TIMELIB_MICROSEC, 1 }, + { "sec", TIMELIB_SECOND, 1 }, + { "secs", TIMELIB_SECOND, 1 }, + { "second", TIMELIB_SECOND, 1 }, + { "seconds", TIMELIB_SECOND, 1 }, + { "min", TIMELIB_MINUTE, 1 }, + { "mins", TIMELIB_MINUTE, 1 }, + { "minute", TIMELIB_MINUTE, 1 }, + { "minutes", TIMELIB_MINUTE, 1 }, + { "hour", TIMELIB_HOUR, 1 }, + { "hours", TIMELIB_HOUR, 1 }, + { "day", TIMELIB_DAY, 1 }, + { "days", TIMELIB_DAY, 1 }, + { "week", TIMELIB_DAY, 7 }, + { "weeks", TIMELIB_DAY, 7 }, + { "fortnight", TIMELIB_DAY, 14 }, + { "fortnights", TIMELIB_DAY, 14 }, + { "forthnight", TIMELIB_DAY, 14 }, + { "forthnights", TIMELIB_DAY, 14 }, + { "month", TIMELIB_MONTH, 1 }, + { "months", TIMELIB_MONTH, 1 }, + { "year", TIMELIB_YEAR, 1 }, + { "years", TIMELIB_YEAR, 1 }, + + { "monday", TIMELIB_WEEKDAY, 1 }, + { "mon", TIMELIB_WEEKDAY, 1 }, + { "tuesday", TIMELIB_WEEKDAY, 2 }, + { "tue", TIMELIB_WEEKDAY, 2 }, + { "wednesday", TIMELIB_WEEKDAY, 3 }, + { "wed", TIMELIB_WEEKDAY, 3 }, + { "thursday", TIMELIB_WEEKDAY, 4 }, + { "thu", TIMELIB_WEEKDAY, 4 }, + { "friday", TIMELIB_WEEKDAY, 5 }, + { "fri", TIMELIB_WEEKDAY, 5 }, + { "saturday", TIMELIB_WEEKDAY, 6 }, + { "sat", TIMELIB_WEEKDAY, 6 }, + { "sunday", TIMELIB_WEEKDAY, 0 }, + { "sun", TIMELIB_WEEKDAY, 0 }, + + { "weekday", TIMELIB_SPECIAL, TIMELIB_SPECIAL_WEEKDAY }, + { "weekdays", TIMELIB_SPECIAL, TIMELIB_SPECIAL_WEEKDAY }, + { NULL, 0, 0 } +}; + +/* The relative text table. */ +static timelib_lookup_table const timelib_reltext_lookup[] = { + { "first", 0, 1 }, + { "next", 0, 1 }, + { "second", 0, 2 }, + { "third", 0, 3 }, + { "fourth", 0, 4 }, + { "fifth", 0, 5 }, + { "sixth", 0, 6 }, + { "seventh", 0, 7 }, + { "eight", 0, 8 }, + { "eighth", 0, 8 }, + { "ninth", 0, 9 }, + { "tenth", 0, 10 }, + { "eleventh", 0, 11 }, + { "twelfth", 0, 12 }, + { "last", 0, -1 }, + { "previous", 0, -1 }, + { "this", 1, 0 }, + { NULL, 1, 0 } +}; + +/* The month table. */ +static timelib_lookup_table const timelib_month_lookup[] = { + { "jan", 0, 1 }, + { "feb", 0, 2 }, + { "mar", 0, 3 }, + { "apr", 0, 4 }, + { "may", 0, 5 }, + { "jun", 0, 6 }, + { "jul", 0, 7 }, + { "aug", 0, 8 }, + { "sep", 0, 9 }, + { "sept", 0, 9 }, + { "oct", 0, 10 }, + { "nov", 0, 11 }, + { "dec", 0, 12 }, + { "i", 0, 1 }, + { "ii", 0, 2 }, + { "iii", 0, 3 }, + { "iv", 0, 4 }, + { "v", 0, 5 }, + { "vi", 0, 6 }, + { "vii", 0, 7 }, + { "viii", 0, 8 }, + { "ix", 0, 9 }, + { "x", 0, 10 }, + { "xi", 0, 11 }, + { "xii", 0, 12 }, + + { "january", 0, 1 }, + { "february", 0, 2 }, + { "march", 0, 3 }, + { "april", 0, 4 }, + { "may", 0, 5 }, + { "june", 0, 6 }, + { "july", 0, 7 }, + { "august", 0, 8 }, + { "september", 0, 9 }, + { "october", 0, 10 }, + { "november", 0, 11 }, + { "december", 0, 12 }, + { NULL, 0, 0 } +}; + +#if 0 +static char* timelib_ltrim(char *s) +{ + char *ptr = s; + while (ptr[0] == ' ' || ptr[0] == '\t') { + ptr++; + } + return ptr; +} +#endif + +#if 0 +uchar *fill(Scanner *s, uchar *cursor){ + if(!s->eof){ + unsigned int cnt = s->tok - s->bot; + if(cnt){ + memcpy(s->bot, s->tok, s->lim - s->tok); + s->tok = s->bot; + s->ptr -= cnt; + cursor -= cnt; + s->pos -= cnt; + s->lim -= cnt; + } + if((s->top - s->lim) < BSIZE){ + uchar *buf = (uchar*) timelib_malloc(((s->lim - s->bot) + BSIZE)*sizeof(uchar)); + memcpy(buf, s->tok, s->lim - s->tok); + s->tok = buf; + s->ptr = &buf[s->ptr - s->bot]; + cursor = &buf[cursor - s->bot]; + s->pos = &buf[s->pos - s->bot]; + s->lim = &buf[s->lim - s->bot]; + s->top = &s->lim[BSIZE]; + timelib_free(s->bot); + s->bot = buf; + } + if((cnt = read(s->fd, (char*) s->lim, BSIZE)) != BSIZE){ + s->eof = &s->lim[cnt]; *(s->eof)++ = '\n'; + } + s->lim += cnt; + } + return cursor; +} +#endif + +static timelib_error_message *alloc_error_message(timelib_error_message **messages, int *count) +{ + /* Realloc in power of two increments */ + int is_pow2 = (*count & (*count - 1)) == 0; + + if (is_pow2) { + size_t alloc_size = *count ? (*count * 2) : 1; + + *messages = timelib_realloc(*messages, alloc_size * sizeof(timelib_error_message)); + } + return *messages + (*count)++; +} + +static void add_warning(Scanner *s, int error_code, char *error) +{ + timelib_error_message *message = alloc_error_message(&s->errors->warning_messages, &s->errors->warning_count); + + message->error_code = error_code; + message->position = s->tok ? s->tok - s->str : 0; + message->character = s->tok ? *s->tok : 0; + message->message = timelib_strdup(error); +} + +static void add_error(Scanner *s, int error_code, char *error) +{ + timelib_error_message *message = alloc_error_message(&s->errors->error_messages, &s->errors->error_count); + + message->error_code = error_code; + message->position = s->tok ? s->tok - s->str : 0; + message->character = s->tok ? *s->tok : 0; + message->message = timelib_strdup(error); +} + +static void add_pbf_warning(Scanner *s, int error_code, char *error, const char *sptr, const char *cptr) +{ + timelib_error_message *message = alloc_error_message(&s->errors->warning_messages, &s->errors->warning_count); + + message->error_code = error_code; + message->position = cptr - sptr; + message->character = *cptr; + message->message = timelib_strdup(error); +} + +static void add_pbf_error(Scanner *s, int error_code, char *error, const char *sptr, const char *cptr) +{ + timelib_error_message *message = alloc_error_message(&s->errors->error_messages, &s->errors->error_count); + + message->error_code = error_code; + message->position = cptr - sptr; + message->character = *cptr; + message->message = timelib_strdup(error); +} + +static timelib_sll timelib_meridian(const char **ptr, timelib_sll h) +{ + timelib_sll retval = 0; + + while (!strchr("AaPp", **ptr)) { + ++*ptr; + } + if (**ptr == 'a' || **ptr == 'A') { + if (h == 12) { + retval = -12; + } + } else if (h != 12) { + retval = 12; + } + ++*ptr; + if (**ptr == '.') { + ++*ptr; + } + if (**ptr == 'M' || **ptr == 'm') { + ++*ptr; + } + if (**ptr == '.') { + ++*ptr; + } + return retval; +} + +static timelib_sll timelib_meridian_with_check(const char **ptr, timelib_sll h) +{ + timelib_sll retval = 0; + + while (**ptr && !strchr("AaPp", **ptr)) { + ++*ptr; + } + if(!**ptr) { + return TIMELIB_UNSET; + } + if (**ptr == 'a' || **ptr == 'A') { + if (h == 12) { + retval = -12; + } + } else if (h != 12) { + retval = 12; + } + ++*ptr; + if (**ptr == '.') { + ++*ptr; + if (**ptr != 'm' && **ptr != 'M') { + return TIMELIB_UNSET; + } + ++*ptr; + if (**ptr != '.' ) { + return TIMELIB_UNSET; + } + ++*ptr; + } else if (**ptr == 'm' || **ptr == 'M') { + ++*ptr; + } else { + return TIMELIB_UNSET; + } + return retval; +} + +static char *timelib_string(Scanner *s) +{ + char *tmp = timelib_calloc(1, s->cur - s->tok + 1); + memcpy(tmp, s->tok, s->cur - s->tok); + + return tmp; +} + +static timelib_sll timelib_get_nr_ex(const char **ptr, int max_length, int *scanned_length) +{ + const char *begin, *end; + char *str; + timelib_sll tmp_nr = TIMELIB_UNSET; + int len = 0; + + while ((**ptr < '0') || (**ptr > '9')) { + if (**ptr == '\0') { + return TIMELIB_UNSET; + } + ++*ptr; + } + begin = *ptr; + while ((**ptr >= '0') && (**ptr <= '9') && len < max_length) { + ++*ptr; + ++len; + } + end = *ptr; + if (scanned_length) { + *scanned_length = end - begin; + } + str = timelib_calloc(1, end - begin + 1); + memcpy(str, begin, end - begin); + tmp_nr = strtoll(str, NULL, 10); + timelib_free(str); + return tmp_nr; +} + +static timelib_sll timelib_get_nr(const char **ptr, int max_length) +{ + return timelib_get_nr_ex(ptr, max_length, NULL); +} + +static void timelib_skip_day_suffix(const char **ptr) +{ + if (isspace(**ptr)) { + return; + } + if (!timelib_strncasecmp(*ptr, "nd", 2) || !timelib_strncasecmp(*ptr, "rd", 2) ||!timelib_strncasecmp(*ptr, "st", 2) || !timelib_strncasecmp(*ptr, "th", 2)) { + *ptr += 2; + } +} + +static timelib_sll timelib_get_frac_nr(const char **ptr) +{ + const char *begin, *end; + char *str; + double tmp_nr = TIMELIB_UNSET; + int len = 0; + + while ((**ptr != '.') && (**ptr != ':') && ((**ptr < '0') || (**ptr > '9'))) { + if (**ptr == '\0') { + return TIMELIB_UNSET; + } + ++*ptr; + } + begin = *ptr; + while ((**ptr == '.') || (**ptr == ':') || ((**ptr >= '0') && (**ptr <= '9'))) { + ++*ptr; + ++len; + } + end = *ptr; + str = timelib_calloc(1, end - begin); + memcpy(str, begin + 1, end - begin - 1); + tmp_nr = strtod(str, NULL) * pow(10, 7 - (end - begin)); + timelib_free(str); + return tmp_nr; +} + +static timelib_ull timelib_get_signed_nr(Scanner *s, const char **ptr, int max_length) +{ + timelib_ull dir = 1; + + while (((**ptr < '0') || (**ptr > '9')) && (**ptr != '+') && (**ptr != '-')) { + if (**ptr == '\0') { + add_error(s, TIMELIB_ERR_UNEXPECTED_DATA, "Found unexpected data"); + return 0; + } + ++*ptr; + } + + while (**ptr == '+' || **ptr == '-') + { + if (**ptr == '-') { + dir *= -1; + } + ++*ptr; + } + return dir * timelib_get_nr(ptr, max_length); +} + +static timelib_sll timelib_lookup_relative_text(const char **ptr, int *behavior) +{ + char *word; + const char *begin = *ptr, *end; + timelib_sll value = 0; + const timelib_lookup_table *tp; + + while ((**ptr >= 'A' && **ptr <= 'Z') || (**ptr >= 'a' && **ptr <= 'z')) { + ++*ptr; + } + end = *ptr; + word = timelib_calloc(1, end - begin + 1); + memcpy(word, begin, end - begin); + + for (tp = timelib_reltext_lookup; tp->name; tp++) { + if (timelib_strcasecmp(word, tp->name) == 0) { + value = tp->value; + *behavior = tp->type; + } + } + + timelib_free(word); + return value; +} + +static timelib_sll timelib_get_relative_text(const char **ptr, int *behavior) +{ + while (**ptr == ' ' || **ptr == '\t' || **ptr == '-' || **ptr == '/') { + ++*ptr; + } + return timelib_lookup_relative_text(ptr, behavior); +} + +static timelib_long timelib_lookup_month(const char **ptr) +{ + char *word; + const char *begin = *ptr, *end; + timelib_long value = 0; + const timelib_lookup_table *tp; + + while ((**ptr >= 'A' && **ptr <= 'Z') || (**ptr >= 'a' && **ptr <= 'z')) { + ++*ptr; + } + end = *ptr; + word = timelib_calloc(1, end - begin + 1); + memcpy(word, begin, end - begin); + + for (tp = timelib_month_lookup; tp->name; tp++) { + if (timelib_strcasecmp(word, tp->name) == 0) { + value = tp->value; + } + } + + timelib_free(word); + return value; +} + +static timelib_long timelib_get_month(const char **ptr) +{ + while (**ptr == ' ' || **ptr == '\t' || **ptr == '-' || **ptr == '.' || **ptr == '/') { + ++*ptr; + } + return timelib_lookup_month(ptr); +} + +static void timelib_eat_spaces(const char **ptr) +{ + while (**ptr == ' ' || **ptr == '\t') { + ++*ptr; + } +} + +static void timelib_eat_until_separator(const char **ptr) +{ + ++*ptr; + while (strchr(" \t.,:;/-0123456789", **ptr) == NULL) { + ++*ptr; + } +} + +static const timelib_relunit* timelib_lookup_relunit(const char **ptr) +{ + char *word; + const char *begin = *ptr, *end; + const timelib_relunit *tp, *value = NULL; + + while (**ptr != '\0' && **ptr != ' ' && **ptr != ',' && **ptr != '\t' && **ptr != ';' && **ptr != ':' && + **ptr != '/' && **ptr != '.' && **ptr != '-' && **ptr != '(' && **ptr != ')' ) { + ++*ptr; + } + end = *ptr; + word = timelib_calloc(1, end - begin + 1); + memcpy(word, begin, end - begin); + + for (tp = timelib_relunit_lookup; tp->name; tp++) { + if (timelib_strcasecmp(word, tp->name) == 0) { + value = tp; + break; + } + } + + timelib_free(word); + return value; +} + +/** + * The time_part parameter is a flag. It can be TIMELIB_TIME_PART_KEEP in case + * the time portion should not be reset to midnight, or + * TIMELIB_TIME_PART_DONT_KEEP in case it does need to be reset. This is used + * for not overwriting the time portion for 'X weekday'. + */ +static void timelib_set_relative(const char **ptr, timelib_sll amount, int behavior, Scanner *s, int time_part) +{ + const timelib_relunit* relunit; + + if (!(relunit = timelib_lookup_relunit(ptr))) { + return; + } + + switch (relunit->unit) { + case TIMELIB_MICROSEC: s->time->relative.us += amount * relunit->multiplier; break; + case TIMELIB_SECOND: s->time->relative.s += amount * relunit->multiplier; break; + case TIMELIB_MINUTE: s->time->relative.i += amount * relunit->multiplier; break; + case TIMELIB_HOUR: s->time->relative.h += amount * relunit->multiplier; break; + case TIMELIB_DAY: s->time->relative.d += amount * relunit->multiplier; break; + case TIMELIB_MONTH: s->time->relative.m += amount * relunit->multiplier; break; + case TIMELIB_YEAR: s->time->relative.y += amount * relunit->multiplier; break; + + case TIMELIB_WEEKDAY: + TIMELIB_HAVE_WEEKDAY_RELATIVE(); + if (time_part != TIMELIB_TIME_PART_KEEP) { + TIMELIB_UNHAVE_TIME(); + } + s->time->relative.d += (amount > 0 ? amount - 1 : amount) * 7; + s->time->relative.weekday = relunit->multiplier; + s->time->relative.weekday_behavior = behavior; + break; + + case TIMELIB_SPECIAL: + TIMELIB_HAVE_SPECIAL_RELATIVE(); + if (time_part != TIMELIB_TIME_PART_KEEP) { + TIMELIB_UNHAVE_TIME(); + } + s->time->relative.special.type = relunit->multiplier; + s->time->relative.special.amount = amount; + } +} + +static const timelib_tz_lookup_table* abbr_search(const char *word, timelib_long gmtoffset, int isdst) +{ + int first_found = 0; + const timelib_tz_lookup_table *tp, *first_found_elem = NULL; + const timelib_tz_lookup_table *fmp; + + if (timelib_strcasecmp("utc", word) == 0 || timelib_strcasecmp("gmt", word) == 0) { + return timelib_timezone_utc; + } + + for (tp = timelib_timezone_lookup; tp->name; tp++) { + if (timelib_strcasecmp(word, tp->name) == 0) { + if (!first_found) { + first_found = 1; + first_found_elem = tp; + if (gmtoffset == -1) { + return tp; + } + } + if (tp->gmtoffset == gmtoffset) { + return tp; + } + } + } + if (first_found) { + return first_found_elem; + } + + /* Still didn't find anything, let's find the zone solely based on + * offset/isdst then */ + for (fmp = timelib_timezone_fallbackmap; fmp->name; fmp++) { + if (fmp->gmtoffset == gmtoffset && fmp->type == isdst) { + return fmp; + } + } + return NULL; +} + +static timelib_long timelib_lookup_abbr(const char **ptr, int *dst, char **tz_abbr, int *found) +{ + char *word; + const char *begin = *ptr, *end; + timelib_long value = 0; + const timelib_tz_lookup_table *tp; + + while (**ptr != '\0' && **ptr != ')' && **ptr != ' ') { + ++*ptr; + } + end = *ptr; + word = timelib_calloc(1, end - begin + 1); + memcpy(word, begin, end - begin); + + if ((tp = abbr_search(word, -1, 0))) { + value = tp->gmtoffset; + *dst = tp->type; + value -= tp->type * 3600; + *found = 1; + } else { + *found = 0; + } + + *tz_abbr = word; + return value; +} + +#define sHOUR(a) (int)(a * 3600) +#define sMIN(a) (int)(a * 60) + +static timelib_long timelib_parse_tz_cor(const char **ptr, int *tz_not_found) +{ + const char *begin = *ptr, *end; + timelib_long tmp; + + *tz_not_found = 1; + + while (isdigit(**ptr) || **ptr == ':') { + ++*ptr; + } + end = *ptr; + switch (end - begin) { + case 1: /* H */ + case 2: /* HH */ + *tz_not_found = 0; + return sHOUR(strtol(begin, NULL, 10)); + + case 3: /* H:M */ + case 4: /* H:MM, HH:M, HHMM */ + if (begin[1] == ':') { + *tz_not_found = 0; + tmp = sHOUR(strtol(begin, NULL, 10)) + sMIN(strtol(begin + 2, NULL, 10)); + return tmp; + } else if (begin[2] == ':') { + *tz_not_found = 0; + tmp = sHOUR(strtol(begin, NULL, 10)) + sMIN(strtol(begin + 3, NULL, 10)); + return tmp; + } else { + *tz_not_found = 0; + tmp = strtol(begin, NULL, 10); + return sHOUR(tmp / 100) + sMIN(tmp % 100); + } + + case 5: /* HH:MM */ + if (begin[2] != ':') { + break; + } + + *tz_not_found = 0; + tmp = sHOUR(strtol(begin, NULL, 10)) + sMIN(strtol(begin + 3, NULL, 10)); + return tmp; + } + return 0; +} + +static timelib_long timelib_parse_tz_minutes(const char **ptr, timelib_time *t) +{ + timelib_long retval = TIMELIB_UNSET; + const char *begin = *ptr; + + /* First character must be +/- */ + if (**ptr != '+' && **ptr != '-') { + return retval; + } + + ++*ptr; + while (isdigit(**ptr)) { + ++*ptr; + } + + if (*begin == '+') { + t->is_localtime = 1; + t->zone_type = TIMELIB_ZONETYPE_OFFSET; + t->dst = 0; + + retval = sMIN(strtol(begin + 1, NULL, 10)); + } else if (*begin == '-') { + t->is_localtime = 1; + t->zone_type = TIMELIB_ZONETYPE_OFFSET; + t->dst = 0; + + retval = -1 * sMIN(strtol(begin + 1, NULL, 10)); + } + return retval; +} + +timelib_long timelib_parse_zone(const char **ptr, int *dst, timelib_time *t, int *tz_not_found, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_wrapper) +{ + timelib_tzinfo *res; + timelib_long retval = 0; + + *tz_not_found = 0; + + while (**ptr == ' ' || **ptr == '\t' || **ptr == '(') { + ++*ptr; + } + if ((*ptr)[0] == 'G' && (*ptr)[1] == 'M' && (*ptr)[2] == 'T' && ((*ptr)[3] == '+' || (*ptr)[3] == '-')) { + *ptr += 3; + } + if (**ptr == '+') { + ++*ptr; + t->is_localtime = 1; + t->zone_type = TIMELIB_ZONETYPE_OFFSET; + t->dst = 0; + + retval = timelib_parse_tz_cor(ptr, tz_not_found); + } else if (**ptr == '-') { + ++*ptr; + t->is_localtime = 1; + t->zone_type = TIMELIB_ZONETYPE_OFFSET; + t->dst = 0; + + retval = -1 * timelib_parse_tz_cor(ptr, tz_not_found); + } else { + int found = 0; + timelib_long offset = 0; + char *tz_abbr; + + t->is_localtime = 1; + + /* First, we lookup by abbreviation only */ + offset = timelib_lookup_abbr(ptr, dst, &tz_abbr, &found); + if (found) { + t->zone_type = TIMELIB_ZONETYPE_ABBR; + timelib_time_tz_abbr_update(t, tz_abbr); + } + + /* Otherwise, we look if we have a TimeZone identifier */ + if (!found || strcmp("UTC", tz_abbr) == 0) { + int dummy_error_code; + + if ((res = tz_wrapper(tz_abbr, tzdb, &dummy_error_code)) != NULL) { + t->tz_info = res; + t->zone_type = TIMELIB_ZONETYPE_ID; + found++; + } + } + timelib_free(tz_abbr); + *tz_not_found = (found == 0); + retval = offset; + } + while (**ptr == ')') { + ++*ptr; + } + return retval; +} + +#define timelib_split_free(arg) { \ + int i; \ + for (i = 0; i < arg.c; i++) { \ + timelib_free(arg.v[i]); \ + } \ + if (arg.v) { \ + timelib_free(arg.v); \ + } \ +} + +static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) +{ + uchar *cursor = s->cur; + char *str; + const char *ptr = NULL; + +std: + s->tok = cursor; + s->len = 0; +/*!re2c +any = [\000-\377]; + +space = [ \t]+; +frac = "."[0-9]+; + +ago = 'ago'; + +hour24 = [01]?[0-9] | "2"[0-4]; +hour24lz = [01][0-9] | "2"[0-4]; +hour12 = "0"?[1-9] | "1"[0-2]; +minute = [0-5]?[0-9]; +minutelz = [0-5][0-9]; +second = minute | "60"; +secondlz = minutelz | "60"; +meridian = ([AaPp] "."? [Mm] "."?) [\000\t ]; +tz = "("? [A-Za-z]{1,6} ")"? | [A-Z][a-z]+([_/-][A-Za-z]+)+; +tzcorrection = "GMT"? [+-] hour24 ":"? minute?; + +daysuf = "st" | "nd" | "rd" | "th"; + +month = "0"? [0-9] | "1"[0-2]; +day = (([0-2]?[0-9]) | ("3"[01])) daysuf?; +year = [0-9]{1,4}; +year2 = [0-9]{2}; +year4 = [0-9]{4}; +year4withsign = [+-]? [0-9]{4}; +yearx = [+-] [0-9]{5,19}; + +dayofyear = "00"[1-9] | "0"[1-9][0-9] | [1-2][0-9][0-9] | "3"[0-5][0-9] | "36"[0-6]; +weekofyear = "0"[1-9] | [1-4][0-9] | "5"[0-3]; + +monthlz = "0" [0-9] | "1" [0-2]; +daylz = "0" [0-9] | [1-2][0-9] | "3" [01]; + +dayfull = 'sunday' | 'monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday'; +dayabbr = 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat' | 'sun'; +dayspecial = 'weekday' | 'weekdays'; +daytext = dayfull | dayabbr | dayspecial; + +monthfull = 'january' | 'february' | 'march' | 'april' | 'may' | 'june' | 'july' | 'august' | 'september' | 'october' | 'november' | 'december'; +monthabbr = 'jan' | 'feb' | 'mar' | 'apr' | 'may' | 'jun' | 'jul' | 'aug' | 'sep' | 'sept' | 'oct' | 'nov' | 'dec'; +monthroman = "I" | "II" | "III" | "IV" | "V" | "VI" | "VII" | "VIII" | "IX" | "X" | "XI" | "XII"; +monthtext = monthfull | monthabbr | monthroman; + +/* Time formats */ +timetiny12 = hour12 space? meridian; +timeshort12 = hour12[:.]minutelz space? meridian; +timelong12 = hour12[:.]minute[:.]secondlz space? meridian; + +timeshort24 = 't'? hour24[:.]minute; +timelong24 = 't'? hour24[:.]minute[:.]second; +iso8601long = 't'? hour24 [:.] minute [:.] second frac; + +/* iso8601shorttz = hour24 [:] minutelz space? (tzcorrection | tz); */ +iso8601normtz = 't'? hour24 [:.] minute [:.] secondlz space? (tzcorrection | tz); +/* iso8601longtz = hour24 [:] minute [:] secondlz frac space? (tzcorrection | tz); */ + +gnunocolon = 't'? hour24lz minutelz; +/* gnunocolontz = hour24lz minutelz space? (tzcorrection | tz); */ +iso8601nocolon = 't'? hour24lz minutelz secondlz; +/* iso8601nocolontz = hour24lz minutelz secondlz space? (tzcorrection | tz); */ + +/* Date formats */ +americanshort = month "/" day; +american = month "/" day "/" year; +iso8601dateslash = year4 "/" monthlz "/" daylz "/"?; +dateslash = year4 "/" month "/" day; +iso8601date4 = year4withsign "-" monthlz "-" daylz; +iso8601date2 = year2 "-" monthlz "-" daylz; +iso8601datex = yearx "-" monthlz "-" daylz; +gnudateshorter = year4 "-" month; +gnudateshort = year "-" month "-" day; +pointeddate4 = day [.\t-] month [.-] year4; +pointeddate2 = day [.\t] month "." year2; +datefull = day ([ \t.-])* monthtext ([ \t.-])* year; +datenoday = monthtext ([ .\t-])* year4; +datenodayrev = year4 ([ .\t-])* monthtext; +datetextual = monthtext ([ .\t-])* day [,.stndrh\t ]+ year; +datenoyear = monthtext ([ .\t-])* day ([,.stndrh\t ]+|[\000]); +datenoyearrev = day ([ .\t-])* monthtext; +datenocolon = year4 monthlz daylz; + +/* Special formats */ +soap = year4 "-" monthlz "-" daylz "T" hour24lz ":" minutelz ":" secondlz frac tzcorrection?; +xmlrpc = year4 monthlz daylz "T" hour24 ":" minutelz ":" secondlz; +xmlrpcnocolon = year4 monthlz daylz 't' hour24 minutelz secondlz; +wddx = year4 "-" month "-" day "T" hour24 ":" minute ":" second; +pgydotd = year4 "."? dayofyear; +pgtextshort = monthabbr "-" daylz "-" year; +pgtextreverse = year "-" monthabbr "-" daylz; +mssqltime = hour12 ":" minutelz ":" secondlz [:.] [0-9]+ meridian; +isoweekday = year4 "-"? "W" weekofyear "-"? [0-7]; +isoweek = year4 "-"? "W" weekofyear; +exif = year4 ":" monthlz ":" daylz " " hour24lz ":" minutelz ":" secondlz; +firstdayof = 'first day of'; +lastdayof = 'last day of'; +backof = 'back of ' hour24 (space? meridian)?; +frontof = 'front of ' hour24 (space? meridian)?; + +/* Common Log Format: 10/Oct/2000:13:55:36 -0700 */ +clf = day "/" monthabbr "/" year4 ":" hour24lz ":" minutelz ":" secondlz space tzcorrection; + +/* Timestamp format: @1126396800 */ +timestamp = "@" "-"? [0-9]+; +timestampms = "@" "-"? [0-9]+ "." [0-9]{0,6}; + +/* To fix some ambiguities */ +dateshortwithtimeshort12 = datenoyear timeshort12; +dateshortwithtimelong12 = datenoyear timelong12; +dateshortwithtimeshort = datenoyear timeshort24; +dateshortwithtimelong = datenoyear timelong24; +dateshortwithtimelongtz = datenoyear iso8601normtz; + +/* + * Relative regexps + */ +reltextnumber = 'first'|'second'|'third'|'fourth'|'fifth'|'sixth'|'seventh'|'eight'|'eighth'|'ninth'|'tenth'|'eleventh'|'twelfth'; +reltexttext = 'next'|'last'|'previous'|'this'; +reltextunit = 'ms' | 'µs' | (('msec'|'millisecond'|'µsec'|'microsecond'|'usec'|'sec'|'second'|'min'|'minute'|'hour'|'day'|'fortnight'|'forthnight'|'month'|'year') 's'?) | 'weeks' | daytext; + +relnumber = ([+-]*[ \t]*[0-9]{1,13}); +relative = relnumber space? (reltextunit | 'week' ); +relativetext = (reltextnumber|reltexttext) space reltextunit; +relativetextweek = reltexttext space 'week'; + +weekdayof = (reltextnumber|reltexttext) space (dayfull|dayabbr) space 'of'; + +*/ + +/*!re2c + /* so that vim highlights correctly */ + 'yesterday' + { + DEBUG_OUTPUT("yesterday"); + TIMELIB_INIT; + TIMELIB_HAVE_RELATIVE(); + TIMELIB_UNHAVE_TIME(); + + s->time->relative.d = -1; + TIMELIB_DEINIT; + return TIMELIB_RELATIVE; + } + + 'now' + { + DEBUG_OUTPUT("now"); + TIMELIB_INIT; + + TIMELIB_DEINIT; + return TIMELIB_RELATIVE; + } + + 'noon' + { + DEBUG_OUTPUT("noon"); + TIMELIB_INIT; + TIMELIB_UNHAVE_TIME(); + TIMELIB_HAVE_TIME(); + s->time->h = 12; + + TIMELIB_DEINIT; + return TIMELIB_RELATIVE; + } + + 'midnight' | 'today' + { + DEBUG_OUTPUT("midnight | today"); + TIMELIB_INIT; + TIMELIB_UNHAVE_TIME(); + + TIMELIB_DEINIT; + return TIMELIB_RELATIVE; + } + + 'tomorrow' + { + DEBUG_OUTPUT("tomorrow"); + TIMELIB_INIT; + TIMELIB_HAVE_RELATIVE(); + TIMELIB_UNHAVE_TIME(); + + s->time->relative.d = 1; + TIMELIB_DEINIT; + return TIMELIB_RELATIVE; + } + + timestamp + { + timelib_ull i; + + TIMELIB_INIT; + TIMELIB_HAVE_RELATIVE(); + TIMELIB_UNHAVE_DATE(); + TIMELIB_UNHAVE_TIME(); + TIMELIB_HAVE_TZ(); + + i = timelib_get_signed_nr(s, &ptr, 24); + s->time->y = 1970; + s->time->m = 1; + s->time->d = 1; + s->time->h = s->time->i = s->time->s = 0; + s->time->us = 0; + s->time->relative.s += i; + s->time->is_localtime = 1; + s->time->zone_type = TIMELIB_ZONETYPE_OFFSET; + s->time->z = 0; + s->time->dst = 0; + + TIMELIB_DEINIT; + return TIMELIB_RELATIVE; + } + + timestampms + { + timelib_ull i, us; + const char *ptr_before; + + TIMELIB_INIT; + TIMELIB_HAVE_RELATIVE(); + TIMELIB_UNHAVE_DATE(); + TIMELIB_UNHAVE_TIME(); + TIMELIB_HAVE_TZ(); + + i = timelib_get_signed_nr(s, &ptr, 24); + + ptr_before = ptr; + us = timelib_get_signed_nr(s, &ptr, 6); + us = us * pow(10, 7 - (ptr - ptr_before)); + + s->time->y = 1970; + s->time->m = 1; + s->time->d = 1; + s->time->h = s->time->i = s->time->s = 0; + s->time->us = 0; + s->time->relative.s += i; + s->time->relative.us = us; + s->time->is_localtime = 1; + s->time->zone_type = TIMELIB_ZONETYPE_OFFSET; + s->time->z = 0; + s->time->dst = 0; + + TIMELIB_DEINIT; + return TIMELIB_RELATIVE; + } + + firstdayof | lastdayof + { + DEBUG_OUTPUT("firstdayof | lastdayof"); + TIMELIB_INIT; + TIMELIB_HAVE_RELATIVE(); + TIMELIB_UNHAVE_TIME(); + + /* skip "last day of" or "first day of" */ + if (*ptr == 'l' || *ptr == 'L') { + s->time->relative.first_last_day_of = TIMELIB_SPECIAL_LAST_DAY_OF_MONTH; + } else { + s->time->relative.first_last_day_of = TIMELIB_SPECIAL_FIRST_DAY_OF_MONTH; + } + + TIMELIB_DEINIT; + return TIMELIB_LF_DAY_OF_MONTH; + } + + backof | frontof + { + DEBUG_OUTPUT("backof | frontof"); + TIMELIB_INIT; + TIMELIB_UNHAVE_TIME(); + TIMELIB_HAVE_TIME(); + + if (*ptr == 'b') { + s->time->h = timelib_get_nr(&ptr, 2); + s->time->i = 15; + } else { + s->time->h = timelib_get_nr(&ptr, 2) - 1; + s->time->i = 45; + } + if (*ptr != '\0' ) { + timelib_eat_spaces(&ptr); + s->time->h += timelib_meridian(&ptr, s->time->h); + } + + TIMELIB_DEINIT; + return TIMELIB_LF_DAY_OF_MONTH; + } + + weekdayof + { + timelib_sll i; + int behavior = 0; + DEBUG_OUTPUT("weekdayof"); + TIMELIB_INIT; + TIMELIB_HAVE_RELATIVE(); + TIMELIB_HAVE_SPECIAL_RELATIVE(); + + i = timelib_get_relative_text(&ptr, &behavior); + timelib_eat_spaces(&ptr); + if (i > 0) { /* first, second... etc */ + s->time->relative.special.type = TIMELIB_SPECIAL_DAY_OF_WEEK_IN_MONTH; + timelib_set_relative(&ptr, i, 1, s, TIMELIB_TIME_PART_DONT_KEEP); + } else { /* last */ + s->time->relative.special.type = TIMELIB_SPECIAL_LAST_DAY_OF_WEEK_IN_MONTH; + timelib_set_relative(&ptr, i, behavior, s, TIMELIB_TIME_PART_DONT_KEEP); + } + TIMELIB_DEINIT; + return TIMELIB_WEEK_DAY_OF_MONTH; + } + + timetiny12 | timeshort12 | timelong12 + { + DEBUG_OUTPUT("timetiny12 | timeshort12 | timelong12"); + TIMELIB_INIT; + TIMELIB_HAVE_TIME(); + s->time->h = timelib_get_nr(&ptr, 2); + if (*ptr == ':' || *ptr == '.') { + s->time->i = timelib_get_nr(&ptr, 2); + if (*ptr == ':' || *ptr == '.') { + s->time->s = timelib_get_nr(&ptr, 2); + } + } + s->time->h += timelib_meridian(&ptr, s->time->h); + TIMELIB_DEINIT; + return TIMELIB_TIME12; + } + + mssqltime + { + DEBUG_OUTPUT("mssqltime"); + TIMELIB_INIT; + TIMELIB_HAVE_TIME(); + s->time->h = timelib_get_nr(&ptr, 2); + s->time->i = timelib_get_nr(&ptr, 2); + if (*ptr == ':' || *ptr == '.') { + s->time->s = timelib_get_nr(&ptr, 2); + + if (*ptr == ':' || *ptr == '.') { + s->time->us = timelib_get_frac_nr(&ptr); + } + } + timelib_eat_spaces(&ptr); + s->time->h += timelib_meridian(&ptr, s->time->h); + TIMELIB_DEINIT; + return TIMELIB_TIME24_WITH_ZONE; + } + + timeshort24 | timelong24 /* | iso8601short | iso8601norm */ | iso8601long /*| iso8601shorttz | iso8601normtz | iso8601longtz*/ + { + int tz_not_found; + DEBUG_OUTPUT("timeshort24 | timelong24 | iso8601long"); + TIMELIB_INIT; + TIMELIB_HAVE_TIME(); + s->time->h = timelib_get_nr(&ptr, 2); + s->time->i = timelib_get_nr(&ptr, 2); + if (*ptr == ':' || *ptr == '.') { + s->time->s = timelib_get_nr(&ptr, 2); + + if (*ptr == '.') { + s->time->us = timelib_get_frac_nr(&ptr); + } + } + + if (*ptr != '\0') { + s->time->z = timelib_parse_zone(&ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper); + if (tz_not_found) { + add_error(s, TIMELIB_ERR_TZID_NOT_FOUND, "The timezone could not be found in the database"); + } + } + TIMELIB_DEINIT; + return TIMELIB_TIME24_WITH_ZONE; + } + + gnunocolon + { + DEBUG_OUTPUT("gnunocolon"); + TIMELIB_INIT; + switch (s->time->have_time) { + case 0: + s->time->h = timelib_get_nr(&ptr, 2); + s->time->i = timelib_get_nr(&ptr, 2); + s->time->s = 0; + break; + case 1: + s->time->y = timelib_get_nr(&ptr, 4); + break; + default: + TIMELIB_DEINIT; + add_error(s, TIMELIB_ERR_DOUBLE_TIME, "Double time specification"); + return TIMELIB_ERROR; + } + s->time->have_time++; + TIMELIB_DEINIT; + return TIMELIB_GNU_NOCOLON; + } +/* + gnunocolontz + { + DEBUG_OUTPUT("gnunocolontz"); + TIMELIB_INIT; + switch (s->time->have_time) { + case 0: + s->time->h = timelib_get_nr(&ptr, 2); + s->time->i = timelib_get_nr(&ptr, 2); + s->time->s = 0; + s->time->z = timelib_parse_zone(&ptr, &s->time->dst, s->time, s->tzdb, tz_get_wrapper); + break; + case 1: + s->time->y = timelib_get_nr(&ptr, 4); + break; + default: + TIMELIB_DEINIT; + return TIMELIB_ERROR; + } + s->time->have_time++; + TIMELIB_DEINIT; + return TIMELIB_GNU_NOCOLON_TZ; + } +*/ + iso8601nocolon /*| iso8601nocolontz*/ + { + int tz_not_found; + DEBUG_OUTPUT("iso8601nocolon"); + TIMELIB_INIT; + TIMELIB_HAVE_TIME(); + s->time->h = timelib_get_nr(&ptr, 2); + s->time->i = timelib_get_nr(&ptr, 2); + s->time->s = timelib_get_nr(&ptr, 2); + + if (*ptr != '\0') { + s->time->z = timelib_parse_zone(&ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper); + if (tz_not_found) { + add_error(s, TIMELIB_ERR_TZID_NOT_FOUND, "The timezone could not be found in the database"); + } + } + TIMELIB_DEINIT; + return TIMELIB_ISO_NOCOLON; + } + + americanshort | american + { + int length = 0; + DEBUG_OUTPUT("americanshort | american"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + s->time->m = timelib_get_nr(&ptr, 2); + s->time->d = timelib_get_nr(&ptr, 2); + if (*ptr == '/') { + s->time->y = timelib_get_nr_ex(&ptr, 4, &length); + TIMELIB_PROCESS_YEAR(s->time->y, length); + } + TIMELIB_DEINIT; + return TIMELIB_AMERICAN; + } + + iso8601date4 | iso8601dateslash | dateslash + { + DEBUG_OUTPUT("iso8601date4 | iso8601date2 | iso8601dateslash | dateslash"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + s->time->y = timelib_get_signed_nr(s, &ptr, 4); + s->time->m = timelib_get_nr(&ptr, 2); + s->time->d = timelib_get_nr(&ptr, 2); + TIMELIB_DEINIT; + return TIMELIB_ISO_DATE; + } + + iso8601date2 + { + int length = 0; + DEBUG_OUTPUT("iso8601date2"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + s->time->y = timelib_get_nr_ex(&ptr, 4, &length); + s->time->m = timelib_get_nr(&ptr, 2); + s->time->d = timelib_get_nr(&ptr, 2); + TIMELIB_PROCESS_YEAR(s->time->y, length); + TIMELIB_DEINIT; + return TIMELIB_ISO_DATE; + } + + iso8601datex + { + DEBUG_OUTPUT("iso8601datex"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + s->time->y = timelib_get_signed_nr(s, &ptr, 19); + s->time->m = timelib_get_nr(&ptr, 2); + s->time->d = timelib_get_nr(&ptr, 2); + TIMELIB_DEINIT; + return TIMELIB_ISO_DATE; + } + + gnudateshorter + { + int length = 0; + DEBUG_OUTPUT("gnudateshorter"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + s->time->y = timelib_get_nr_ex(&ptr, 4, &length); + s->time->m = timelib_get_nr(&ptr, 2); + s->time->d = 1; + TIMELIB_PROCESS_YEAR(s->time->y, length); + TIMELIB_DEINIT; + return TIMELIB_ISO_DATE; + } + + gnudateshort + { + int length = 0; + DEBUG_OUTPUT("gnudateshort"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + s->time->y = timelib_get_nr_ex(&ptr, 4, &length); + s->time->m = timelib_get_nr(&ptr, 2); + s->time->d = timelib_get_nr(&ptr, 2); + TIMELIB_PROCESS_YEAR(s->time->y, length); + TIMELIB_DEINIT; + return TIMELIB_ISO_DATE; + } + + datefull + { + int length = 0; + DEBUG_OUTPUT("datefull"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + s->time->d = timelib_get_nr(&ptr, 2); + timelib_skip_day_suffix(&ptr); + s->time->m = timelib_get_month(&ptr); + s->time->y = timelib_get_nr_ex(&ptr, 4, &length); + TIMELIB_PROCESS_YEAR(s->time->y, length); + TIMELIB_DEINIT; + return TIMELIB_DATE_FULL; + } + + pointeddate4 + { + DEBUG_OUTPUT("pointed date YYYY"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + s->time->d = timelib_get_nr(&ptr, 2); + s->time->m = timelib_get_nr(&ptr, 2); + s->time->y = timelib_get_nr(&ptr, 4); + TIMELIB_DEINIT; + return TIMELIB_DATE_FULL_POINTED; + } + + pointeddate2 + { + int length = 0; + DEBUG_OUTPUT("pointed date YY"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + s->time->d = timelib_get_nr(&ptr, 2); + s->time->m = timelib_get_nr(&ptr, 2); + s->time->y = timelib_get_nr_ex(&ptr, 2, &length); + TIMELIB_PROCESS_YEAR(s->time->y, length); + TIMELIB_DEINIT; + return TIMELIB_DATE_FULL_POINTED; + } + + datenoday + { + int length = 0; + DEBUG_OUTPUT("datenoday"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + s->time->m = timelib_get_month(&ptr); + s->time->y = timelib_get_nr_ex(&ptr, 4, &length); + s->time->d = 1; + TIMELIB_PROCESS_YEAR(s->time->y, length); + TIMELIB_DEINIT; + return TIMELIB_DATE_NO_DAY; + } + + datenodayrev + { + int length = 0; + DEBUG_OUTPUT("datenodayrev"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + s->time->y = timelib_get_nr_ex(&ptr, 4, &length); + s->time->m = timelib_get_month(&ptr); + s->time->d = 1; + TIMELIB_PROCESS_YEAR(s->time->y, length); + TIMELIB_DEINIT; + return TIMELIB_DATE_NO_DAY; + } + + datetextual | datenoyear + { + int length = 0; + DEBUG_OUTPUT("datetextual | datenoyear"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + s->time->m = timelib_get_month(&ptr); + s->time->d = timelib_get_nr(&ptr, 2); + s->time->y = timelib_get_nr_ex(&ptr, 4, &length); + TIMELIB_PROCESS_YEAR(s->time->y, length); + TIMELIB_DEINIT; + return TIMELIB_DATE_TEXT; + } + + datenoyearrev + { + DEBUG_OUTPUT("datenoyearrev"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + s->time->d = timelib_get_nr(&ptr, 2); + timelib_skip_day_suffix(&ptr); + s->time->m = timelib_get_month(&ptr); + TIMELIB_DEINIT; + return TIMELIB_DATE_TEXT; + } + + datenocolon + { + DEBUG_OUTPUT("datenocolon"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + s->time->y = timelib_get_nr(&ptr, 4); + s->time->m = timelib_get_nr(&ptr, 2); + s->time->d = timelib_get_nr(&ptr, 2); + TIMELIB_DEINIT; + return TIMELIB_DATE_NOCOLON; + } + + xmlrpc | xmlrpcnocolon | soap | wddx | exif + { + int tz_not_found; + DEBUG_OUTPUT("xmlrpc | xmlrpcnocolon | soap | wddx | exif"); + TIMELIB_INIT; + TIMELIB_HAVE_TIME(); + TIMELIB_HAVE_DATE(); + s->time->y = timelib_get_nr(&ptr, 4); + s->time->m = timelib_get_nr(&ptr, 2); + s->time->d = timelib_get_nr(&ptr, 2); + s->time->h = timelib_get_nr(&ptr, 2); + s->time->i = timelib_get_nr(&ptr, 2); + s->time->s = timelib_get_nr(&ptr, 2); + if (*ptr == '.') { + s->time->us = timelib_get_frac_nr(&ptr); + if (*ptr) { /* timezone is optional */ + s->time->z = timelib_parse_zone(&ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper); + if (tz_not_found) { + add_error(s, TIMELIB_ERR_TZID_NOT_FOUND, "The timezone could not be found in the database"); + } + } + } + TIMELIB_DEINIT; + return TIMELIB_XMLRPC_SOAP; + } + + pgydotd + { + int length = 0; + DEBUG_OUTPUT("pgydotd"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + s->time->y = timelib_get_nr_ex(&ptr, 4, &length); + s->time->d = timelib_get_nr(&ptr, 3); + s->time->m = 1; + TIMELIB_PROCESS_YEAR(s->time->y, length); + TIMELIB_DEINIT; + return TIMELIB_PG_YEARDAY; + } + + isoweekday + { + timelib_sll w, d; + DEBUG_OUTPUT("isoweekday"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + TIMELIB_HAVE_RELATIVE(); + + s->time->y = timelib_get_nr(&ptr, 4); + w = timelib_get_nr(&ptr, 2); + d = timelib_get_nr(&ptr, 1); + s->time->m = 1; + s->time->d = 1; + s->time->relative.d = timelib_daynr_from_weeknr(s->time->y, w, d); + + TIMELIB_DEINIT; + return TIMELIB_ISO_WEEK; + } + + isoweek + { + timelib_sll w, d; + DEBUG_OUTPUT("isoweek"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + TIMELIB_HAVE_RELATIVE(); + + s->time->y = timelib_get_nr(&ptr, 4); + w = timelib_get_nr(&ptr, 2); + d = 1; + s->time->m = 1; + s->time->d = 1; + s->time->relative.d = timelib_daynr_from_weeknr(s->time->y, w, d); + + TIMELIB_DEINIT; + return TIMELIB_ISO_WEEK; + } + + pgtextshort + { + int length = 0; + DEBUG_OUTPUT("pgtextshort"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + s->time->m = timelib_get_month(&ptr); + s->time->d = timelib_get_nr(&ptr, 2); + s->time->y = timelib_get_nr_ex(&ptr, 4, &length); + TIMELIB_PROCESS_YEAR(s->time->y, length); + TIMELIB_DEINIT; + return TIMELIB_PG_TEXT; + } + + pgtextreverse + { + int length = 0; + DEBUG_OUTPUT("pgtextreverse"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + s->time->y = timelib_get_nr_ex(&ptr, 4, &length); + s->time->m = timelib_get_month(&ptr); + s->time->d = timelib_get_nr(&ptr, 2); + TIMELIB_PROCESS_YEAR(s->time->y, length); + TIMELIB_DEINIT; + return TIMELIB_PG_TEXT; + } + + clf + { + int tz_not_found; + DEBUG_OUTPUT("clf"); + TIMELIB_INIT; + TIMELIB_HAVE_TIME(); + TIMELIB_HAVE_DATE(); + s->time->d = timelib_get_nr(&ptr, 2); + s->time->m = timelib_get_month(&ptr); + s->time->y = timelib_get_nr(&ptr, 4); + s->time->h = timelib_get_nr(&ptr, 2); + s->time->i = timelib_get_nr(&ptr, 2); + s->time->s = timelib_get_nr(&ptr, 2); + s->time->z = timelib_parse_zone(&ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper); + if (tz_not_found) { + add_error(s, TIMELIB_ERR_TZID_NOT_FOUND, "The timezone could not be found in the database"); + } + TIMELIB_DEINIT; + return TIMELIB_CLF; + } + + year4 + { + DEBUG_OUTPUT("year4"); + TIMELIB_INIT; + s->time->y = timelib_get_nr(&ptr, 4); + TIMELIB_DEINIT; + return TIMELIB_CLF; + } + + ago + { + DEBUG_OUTPUT("ago"); + TIMELIB_INIT; + s->time->relative.y = 0 - s->time->relative.y; + s->time->relative.m = 0 - s->time->relative.m; + s->time->relative.d = 0 - s->time->relative.d; + s->time->relative.h = 0 - s->time->relative.h; + s->time->relative.i = 0 - s->time->relative.i; + s->time->relative.s = 0 - s->time->relative.s; + s->time->relative.weekday = 0 - s->time->relative.weekday; + if (s->time->relative.weekday == 0) { + s->time->relative.weekday = -7; + } + if (s->time->relative.have_special_relative && s->time->relative.special.type == TIMELIB_SPECIAL_WEEKDAY) { + s->time->relative.special.amount = 0 - s->time->relative.special.amount; + } + TIMELIB_DEINIT; + return TIMELIB_AGO; + } + + daytext + { + const timelib_relunit* relunit; + DEBUG_OUTPUT("daytext"); + TIMELIB_INIT; + TIMELIB_HAVE_RELATIVE(); + TIMELIB_HAVE_WEEKDAY_RELATIVE(); + TIMELIB_UNHAVE_TIME(); + relunit = timelib_lookup_relunit(&ptr); + s->time->relative.weekday = relunit->multiplier; + if (s->time->relative.weekday_behavior != 2) { + s->time->relative.weekday_behavior = 1; + } + + TIMELIB_DEINIT; + return TIMELIB_WEEKDAY; + } + + relativetextweek + { + timelib_sll i; + int behavior = 0; + DEBUG_OUTPUT("relativetextweek"); + TIMELIB_INIT; + TIMELIB_HAVE_RELATIVE(); + + while(*ptr) { + i = timelib_get_relative_text(&ptr, &behavior); + timelib_eat_spaces(&ptr); + timelib_set_relative(&ptr, i, behavior, s, TIMELIB_TIME_PART_DONT_KEEP); + s->time->relative.weekday_behavior = 2; + + /* to handle the format weekday + last/this/next week */ + if (s->time->relative.have_weekday_relative == 0) { + TIMELIB_HAVE_WEEKDAY_RELATIVE(); + s->time->relative.weekday = 1; + } + } + TIMELIB_DEINIT; + return TIMELIB_RELATIVE; + } + + relativetext + { + timelib_sll i; + int behavior = 0; + DEBUG_OUTPUT("relativetext"); + TIMELIB_INIT; + TIMELIB_HAVE_RELATIVE(); + + while(*ptr) { + i = timelib_get_relative_text(&ptr, &behavior); + timelib_eat_spaces(&ptr); + timelib_set_relative(&ptr, i, behavior, s, TIMELIB_TIME_PART_DONT_KEEP); + } + TIMELIB_DEINIT; + return TIMELIB_RELATIVE; + } + + monthfull | monthabbr + { + DEBUG_OUTPUT("monthtext"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + s->time->m = timelib_lookup_month(&ptr); + TIMELIB_DEINIT; + return TIMELIB_DATE_TEXT; + } + + tzcorrection | tz + { + int tz_not_found; + DEBUG_OUTPUT("tzcorrection | tz"); + TIMELIB_INIT; + TIMELIB_HAVE_TZ(); + s->time->z = timelib_parse_zone(&ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper); + if (tz_not_found) { + add_error(s, TIMELIB_ERR_TZID_NOT_FOUND, "The timezone could not be found in the database"); + } + TIMELIB_DEINIT; + return TIMELIB_TIMEZONE; + } + + dateshortwithtimeshort12 | dateshortwithtimelong12 + { + DEBUG_OUTPUT("dateshortwithtimeshort12 | dateshortwithtimelong12"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + s->time->m = timelib_get_month(&ptr); + s->time->d = timelib_get_nr(&ptr, 2); + + TIMELIB_HAVE_TIME(); + s->time->h = timelib_get_nr(&ptr, 2); + s->time->i = timelib_get_nr(&ptr, 2); + if (*ptr == ':' || *ptr == '.') { + s->time->s = timelib_get_nr(&ptr, 2); + + if (*ptr == '.') { + s->time->us = timelib_get_frac_nr(&ptr); + } + } + + s->time->h += timelib_meridian(&ptr, s->time->h); + TIMELIB_DEINIT; + return TIMELIB_SHORTDATE_WITH_TIME; + } + + dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz + { + int tz_not_found; + DEBUG_OUTPUT("dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz"); + TIMELIB_INIT; + TIMELIB_HAVE_DATE(); + s->time->m = timelib_get_month(&ptr); + s->time->d = timelib_get_nr(&ptr, 2); + + TIMELIB_HAVE_TIME(); + s->time->h = timelib_get_nr(&ptr, 2); + s->time->i = timelib_get_nr(&ptr, 2); + if (*ptr == ':') { + s->time->s = timelib_get_nr(&ptr, 2); + + if (*ptr == '.') { + s->time->us = timelib_get_frac_nr(&ptr); + } + } + + if (*ptr != '\0') { + s->time->z = timelib_parse_zone(&ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper); + if (tz_not_found) { + add_error(s, TIMELIB_ERR_TZID_NOT_FOUND, "The timezone could not be found in the database"); + } + } + TIMELIB_DEINIT; + return TIMELIB_SHORTDATE_WITH_TIME; + } + + relative + { + timelib_ull i; + DEBUG_OUTPUT("relative"); + TIMELIB_INIT; + TIMELIB_HAVE_RELATIVE(); + + while(*ptr) { + i = timelib_get_signed_nr(s, &ptr, 24); + timelib_eat_spaces(&ptr); + timelib_set_relative(&ptr, i, 1, s, TIMELIB_TIME_PART_KEEP); + } + TIMELIB_DEINIT; + return TIMELIB_RELATIVE; + } + + [ .,\t] + { + goto std; + } + + "\000"|"\n" + { + s->pos = cursor; s->line++; + goto std; + } + + any + { + add_error(s, TIMELIB_ERR_UNEXPECTED_CHARACTER, "Unexpected character"); + goto std; + } +*/ +} + +/*!max:re2c */ + +timelib_time *timelib_strtotime(const char *s, size_t len, timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper) +{ + Scanner in; + int t; + const char *e = s + len - 1; + + memset(&in, 0, sizeof(in)); + in.errors = timelib_malloc(sizeof(timelib_error_container)); + in.errors->warning_count = 0; + in.errors->warning_messages = NULL; + in.errors->error_count = 0; + in.errors->error_messages = NULL; + + if (len > 0) { + while (isspace(*s) && s < e) { + s++; + } + while (isspace(*e) && e > s) { + e--; + } + } + if (e - s < 0) { + in.time = timelib_time_ctor(); + add_error(&in, TIMELIB_ERR_EMPTY_STRING, "Empty string"); + if (errors) { + *errors = in.errors; + } else { + timelib_error_container_dtor(in.errors); + } + in.time->y = in.time->d = in.time->m = in.time->h = in.time->i = in.time->s = in.time->us = in.time->dst = in.time->z = TIMELIB_UNSET; + in.time->is_localtime = in.time->zone_type = 0; + return in.time; + } + e++; + + in.str = timelib_malloc((e - s) + YYMAXFILL); + memset(in.str, 0, (e - s) + YYMAXFILL); + memcpy(in.str, s, (e - s)); + in.lim = in.str + (e - s) + YYMAXFILL; + in.cur = in.str; + in.time = timelib_time_ctor(); + in.time->y = TIMELIB_UNSET; + in.time->d = TIMELIB_UNSET; + in.time->m = TIMELIB_UNSET; + in.time->h = TIMELIB_UNSET; + in.time->i = TIMELIB_UNSET; + in.time->s = TIMELIB_UNSET; + in.time->us = TIMELIB_UNSET; + in.time->z = TIMELIB_UNSET; + in.time->dst = TIMELIB_UNSET; + in.tzdb = tzdb; + in.time->is_localtime = 0; + in.time->zone_type = 0; + in.time->relative.days = TIMELIB_UNSET; + + do { + t = scan(&in, tz_get_wrapper); +#ifdef DEBUG_PARSER + printf("%d\n", t); +#endif + } while(t != EOI); + + /* do funky checking whether the parsed time was valid time */ + if (in.time->have_time && !timelib_valid_time( in.time->h, in.time->i, in.time->s)) { + add_warning(&in, TIMELIB_WARN_INVALID_TIME, "The parsed time was invalid"); + } + /* do funky checking whether the parsed date was valid date */ + if (in.time->have_date && !timelib_valid_date( in.time->y, in.time->m, in.time->d)) { + add_warning(&in, TIMELIB_WARN_INVALID_DATE, "The parsed date was invalid"); + } + + timelib_free(in.str); + if (errors) { + *errors = in.errors; + } else { + timelib_error_container_dtor(in.errors); + } + return in.time; +} + +#define TIMELIB_CHECK_NUMBER \ + if (strchr("0123456789", *ptr) == NULL) \ + { \ + add_pbf_error(s, TIMELIB_ERR_UNEXPECTED_DATA, "Unexpected data found.", string, begin); \ + } +#define TIMELIB_CHECK_SIGNED_NUMBER \ + if (strchr("-0123456789", *ptr) == NULL) \ + { \ + add_pbf_error(s, TIMELIB_ERR_UNEXPECTED_DATA, "Unexpected data found.", string, begin); \ + } + +static void timelib_time_reset_fields(timelib_time *time) +{ + assert(time != NULL); + + time->y = 1970; + time->m = 1; + time->d = 1; + time->h = time->i = time->s = 0; + time->us = 0; + time->tz_info = NULL; +} + +static void timelib_time_reset_unset_fields(timelib_time *time) +{ + assert(time != NULL); + + if (time->y == TIMELIB_UNSET ) time->y = 1970; + if (time->m == TIMELIB_UNSET ) time->m = 1; + if (time->d == TIMELIB_UNSET ) time->d = 1; + if (time->h == TIMELIB_UNSET ) time->h = 0; + if (time->i == TIMELIB_UNSET ) time->i = 0; + if (time->s == TIMELIB_UNSET ) time->s = 0; + if (time->us == TIMELIB_UNSET ) time->us = 0; +} + +static const timelib_format_specifier default_format_map[] = { + {'+', TIMELIB_FORMAT_ALLOW_EXTRA_CHARACTERS}, + {'#', TIMELIB_FORMAT_ANY_SEPARATOR}, + {'j', TIMELIB_FORMAT_DAY_TWO_DIGIT}, + {'d', TIMELIB_FORMAT_DAY_TWO_DIGIT_PADDED}, + {'z', TIMELIB_FORMAT_DAY_OF_YEAR}, + {'S', TIMELIB_FORMAT_DAY_SUFFIX}, + {'U', TIMELIB_FORMAT_EPOCH_SECONDS}, + {'\\', TIMELIB_FORMAT_ESCAPE}, + {'h', TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX}, + {'g', TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX_PADDED}, + {'H', TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX}, + {'G', TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX_PADDED}, + {'a', TIMELIB_FORMAT_MERIDIAN}, + {'A', TIMELIB_FORMAT_MERIDIAN}, + {'u', TIMELIB_FORMAT_MICROSECOND_SIX_DIGIT}, + {'v', TIMELIB_FORMAT_MILLISECOND_THREE_DIGIT}, + {'i', TIMELIB_FORMAT_MINUTE_TWO_DIGIT}, + {'n', TIMELIB_FORMAT_MONTH_TWO_DIGIT}, + {'m', TIMELIB_FORMAT_MONTH_TWO_DIGIT_PADDED}, + {'?', TIMELIB_FORMAT_RANDOM_CHAR}, + {'!', TIMELIB_FORMAT_RESET_ALL}, + {'|', TIMELIB_FORMAT_RESET_ALL_WHEN_NOT_SET}, + {'s', TIMELIB_FORMAT_SECOND_TWO_DIGIT}, + {';', TIMELIB_FORMAT_SEPARATOR}, + {':', TIMELIB_FORMAT_SEPARATOR}, + {'/', TIMELIB_FORMAT_SEPARATOR}, + {'.', TIMELIB_FORMAT_SEPARATOR}, + {',', TIMELIB_FORMAT_SEPARATOR}, + {'-', TIMELIB_FORMAT_SEPARATOR}, + {'(', TIMELIB_FORMAT_SEPARATOR}, + {')', TIMELIB_FORMAT_SEPARATOR}, + {'*', TIMELIB_FORMAT_SKIP_TO_SEPARATOR}, + {'D', TIMELIB_FORMAT_TEXTUAL_DAY_3_LETTER}, + {'l', TIMELIB_FORMAT_TEXTUAL_DAY_FULL}, + {'M', TIMELIB_FORMAT_TEXTUAL_MONTH_3_LETTER}, + {'F', TIMELIB_FORMAT_TEXTUAL_MONTH_FULL}, + {'e', TIMELIB_FORMAT_TIMEZONE_OFFSET}, + {'P', TIMELIB_FORMAT_TIMEZONE_OFFSET}, + {'p', TIMELIB_FORMAT_TIMEZONE_OFFSET}, + {'T', TIMELIB_FORMAT_TIMEZONE_OFFSET}, + {'O', TIMELIB_FORMAT_TIMEZONE_OFFSET}, + {' ', TIMELIB_FORMAT_WHITESPACE}, + {'y', TIMELIB_FORMAT_YEAR_TWO_DIGIT}, + {'Y', TIMELIB_FORMAT_YEAR_FOUR_DIGIT}, + {'\0', TIMELIB_FORMAT_END} +}; + +static const timelib_format_config default_format_config = { + default_format_map, + // No prefix required by default. + '\0' +}; + +static timelib_format_specifier_code timelib_lookup_format(char input, const timelib_format_specifier* format_map) +{ + while (format_map && format_map->specifier != '\0') { + if (format_map->specifier == input) { + return format_map->code; + } + format_map++; + } + return TIMELIB_FORMAT_LITERAL; +} + +timelib_time *timelib_parse_from_format(const char *format, const char *string, size_t len, timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper) +{ + return timelib_parse_from_format_with_map(format, string, len, errors, tzdb, tz_get_wrapper, &default_format_config); +} + +timelib_time *timelib_parse_from_format_with_map(const char *format, const char *string, size_t len, timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper, const timelib_format_config* format_config) +{ + const char *fptr = format; + const char *ptr = string; + const char *begin; + timelib_sll tmp; + Scanner in; + Scanner *s = ∈ + bool allow_extra = false; + bool prefix_found = false; + int iso_year = TIMELIB_UNSET; + int iso_week_of_year = TIMELIB_UNSET; + int iso_day_of_week = TIMELIB_UNSET; + char prefix_char = format_config->prefix_char; + const timelib_format_specifier *format_map = format_config->format_map; + + memset(&in, 0, sizeof(in)); + in.errors = timelib_malloc(sizeof(timelib_error_container)); + in.errors->warning_count = 0; + in.errors->warning_messages = NULL; + in.errors->error_count = 0; + in.errors->error_messages = NULL; + + in.time = timelib_time_ctor(); + in.time->y = TIMELIB_UNSET; + in.time->d = TIMELIB_UNSET; + in.time->m = TIMELIB_UNSET; + in.time->h = TIMELIB_UNSET; + in.time->i = TIMELIB_UNSET; + in.time->s = TIMELIB_UNSET; + in.time->us = TIMELIB_UNSET; + in.time->z = TIMELIB_UNSET; + in.time->dst = TIMELIB_UNSET; + in.tzdb = tzdb; + in.time->is_localtime = 0; + in.time->zone_type = 0; + + /* Loop over the format string */ + while (*fptr && *ptr) { + begin = ptr; + + if (prefix_char) { + /* There are 2 cases where the input string and format string + * should match the next literal: + * + * 1. No prefix has been specified yet in the format, so expect 1:1 + * match. + * 2. Sequential prefix characters indicating that the second + * prefix is escaped. (e.g. "%%" is expecting literal "%") + */ + if ((!prefix_found && *fptr != prefix_char) || + (prefix_found && *fptr == prefix_char)) { + if (*fptr != *ptr) { + add_pbf_error(s, TIMELIB_ERR_FORMAT_LITERAL_MISMATCH, "Format literal not found", string, begin); + } + ptr++; + fptr++; + prefix_found = false; + continue; + } + + if (*fptr == prefix_char) { + fptr++; + prefix_found = true; + continue; + } + + /* Fall through case is that the prefix has been found and the next + * character is the format specifier. */ + prefix_found = false; + } + + switch (timelib_lookup_format(*fptr, format_map)) { + case TIMELIB_FORMAT_TEXTUAL_DAY_3_LETTER: /* three letter day */ + case TIMELIB_FORMAT_TEXTUAL_DAY_FULL: /* full day */ + { + const timelib_relunit* tmprel = 0; + + tmprel = timelib_lookup_relunit(&ptr); + if (!tmprel) { + add_pbf_error(s, TIMELIB_ERR_NO_TEXTUAL_DAY, "A textual day could not be found", string, begin); + break; + } else { + in.time->have_relative = 1; + in.time->relative.have_weekday_relative = 1; + in.time->relative.weekday = tmprel->multiplier; + in.time->relative.weekday_behavior = 1; + } + } + break; + case TIMELIB_FORMAT_DAY_TWO_DIGIT: /* two digit day, without leading zero */ + case TIMELIB_FORMAT_DAY_TWO_DIGIT_PADDED: /* two digit day, with leading zero */ + TIMELIB_CHECK_NUMBER; + if ((s->time->d = timelib_get_nr(&ptr, 2)) == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_DAY, "A two digit day could not be found", string, begin); + break; + } + + s->time->have_date = 1; + break; + case TIMELIB_FORMAT_DAY_SUFFIX: /* day suffix, ignored, nor checked */ + timelib_skip_day_suffix(&ptr); + break; + case TIMELIB_FORMAT_DAY_OF_YEAR: /* day of year - resets month (0 based) - also initializes everything else to !TIMELIB_UNSET */ + TIMELIB_CHECK_NUMBER; + if (s->time->y == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_MERIDIAN_BEFORE_HOUR, "A 'day of year' can only come after a year has been found", string, begin); + } + if ((tmp = timelib_get_nr(&ptr, 3)) == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_NO_THREE_DIGIT_DAY_OF_YEAR, "A three digit day-of-year could not be found", string, begin); + break; + } + + if (s->time->y != TIMELIB_UNSET) { + s->time->have_date = 1; + s->time->m = 1; + s->time->d = tmp + 1; + timelib_do_normalize(s->time); + } + break; + + case TIMELIB_FORMAT_MONTH_TWO_DIGIT: /* two digit month, without leading zero */ + case TIMELIB_FORMAT_MONTH_TWO_DIGIT_PADDED: /* two digit month, with leading zero */ + TIMELIB_CHECK_NUMBER; + if ((s->time->m = timelib_get_nr(&ptr, 2)) == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_MONTH, "A two digit month could not be found", string, begin); + break; + } + s->time->have_date = 1; + break; + case TIMELIB_FORMAT_TEXTUAL_MONTH_3_LETTER: /* three letter month */ + case TIMELIB_FORMAT_TEXTUAL_MONTH_FULL: /* full month */ + tmp = timelib_lookup_month(&ptr); + if (!tmp) { + add_pbf_error(s, TIMELIB_ERR_NO_TEXTUAL_MONTH, "A textual month could not be found", string, begin); + break; + } + + s->time->have_date = 1; + s->time->m = tmp; + break; + case TIMELIB_FORMAT_YEAR_TWO_DIGIT: /* two digit year */ + { + int length = 0; + TIMELIB_CHECK_NUMBER; + if ((s->time->y = timelib_get_nr_ex(&ptr, 2, &length)) == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_YEAR, "A two digit year could not be found", string, begin); + break; + } + + s->time->have_date = 1; + TIMELIB_PROCESS_YEAR(s->time->y, length); + } + break; + case TIMELIB_FORMAT_YEAR_FOUR_DIGIT: /* four digit year */ + TIMELIB_CHECK_NUMBER; + if ((s->time->y = timelib_get_nr(&ptr, 4)) == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_NO_FOUR_DIGIT_YEAR, "A four digit year could not be found", string, begin); + break; + } + + s->time->have_date = 1; + break; + case TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX: /* two digit hour, without leading zero */ + case TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX_PADDED: /* two digit hour, with leading zero */ + TIMELIB_CHECK_NUMBER; + if ((s->time->h = timelib_get_nr(&ptr, 2)) == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_HOUR, "A two digit hour could not be found", string, begin); + break; + } + if (s->time->h > 12) { + add_pbf_error(s, TIMELIB_ERR_HOUR_LARGER_THAN_12, "Hour can not be higher than 12", string, begin); + break; + } + + s->time->have_time = 1; + break; + case TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX_PADDED: /* two digit hour, with leading zero */ + case TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX: /* two digit hour, without leading zero */ + TIMELIB_CHECK_NUMBER; + if ((s->time->h = timelib_get_nr(&ptr, 2)) == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_HOUR, "A two digit hour could not be found", string, begin); + break; + } + + s->time->have_time = 1; + break; + case TIMELIB_FORMAT_MERIDIAN: /* am/pm/a.m./p.m. AM/PM/A.M./P.M. */ + if (s->time->h == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_MERIDIAN_BEFORE_HOUR, "Meridian can only come after an hour has been found", string, begin); + } + if ((tmp = timelib_meridian_with_check(&ptr, s->time->h)) == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_NO_MERIDIAN, "A meridian could not be found", string, begin); + break; + } + + s->time->have_time = 1; + if (s->time->h != TIMELIB_UNSET) { + s->time->h += tmp; + } + break; + case TIMELIB_FORMAT_MINUTE_TWO_DIGIT: /* two digit minute, with leading zero */ + { + int length; + timelib_sll min; + + TIMELIB_CHECK_NUMBER; + min = timelib_get_nr_ex(&ptr, 2, &length); + if (min == TIMELIB_UNSET || length != 2) { + add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_MINUTE, "A two digit minute could not be found", string, begin); + break; + } + + s->time->have_time = 1; + s->time->i = min; + } + break; + case TIMELIB_FORMAT_SECOND_TWO_DIGIT: /* two digit second, with leading zero */ + { + int length; + timelib_sll sec; + + TIMELIB_CHECK_NUMBER; + sec = timelib_get_nr_ex(&ptr, 2, &length); + if (sec == TIMELIB_UNSET || length != 2) { + add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_SECOND, "A two digit second could not be found", string, begin); + break; + } + + s->time->have_time = 1; + s->time->s = sec; + } + break; + case TIMELIB_FORMAT_MICROSECOND_SIX_DIGIT: /* up to six digit microsecond */ + { + double f; + const char *tptr; + + TIMELIB_CHECK_NUMBER; + tptr = ptr; + if ((f = timelib_get_nr(&ptr, 6)) == TIMELIB_UNSET || (ptr - tptr < 1)) { + add_pbf_error(s, TIMELIB_ERR_NO_SIX_DIGIT_MICROSECOND, "A six digit microsecond could not be found", string, begin); + break; + } + + s->time->us = (f * pow(10, 6 - (ptr - tptr))); + } + break; + case TIMELIB_FORMAT_MILLISECOND_THREE_DIGIT: /* up to three digit millisecond */ + { + double f; + const char *tptr; + + TIMELIB_CHECK_NUMBER; + tptr = ptr; + if ((f = timelib_get_nr(&ptr, 3)) == TIMELIB_UNSET || (ptr - tptr < 1)) { + add_pbf_error(s, TIMELIB_ERR_NO_THREE_DIGIT_MILLISECOND, "A three digit millisecond could not be found", string, begin); + break; + } + + s->time->us = (f * pow(10, 3 - (ptr - tptr)) * 1000); + } + break; + case TIMELIB_FORMAT_WHITESPACE: /* any sort of whitespace (' ' and \t) */ + timelib_eat_spaces(&ptr); + break; + case TIMELIB_FORMAT_EPOCH_SECONDS: /* epoch seconds */ + TIMELIB_CHECK_SIGNED_NUMBER; + tmp = timelib_get_signed_nr(s, &ptr, 24); + s->time->have_zone = 1; + s->time->sse = tmp; + s->time->is_localtime = 1; + s->time->zone_type = TIMELIB_ZONETYPE_OFFSET; + s->time->z = 0; + s->time->dst = 0; + timelib_update_from_sse(s->time); + break; + + case TIMELIB_FORMAT_ANY_SEPARATOR: /* separation symbol */ + if (timelib_lookup_format(*ptr, format_map) != TIMELIB_FORMAT_SEPARATOR) { + add_pbf_error(s, TIMELIB_ERR_NO_SEP_SYMBOL, "The separation symbol ([;:/.,-]) could not be found", string, begin); + break; + } + + ++ptr; + break; + + case TIMELIB_FORMAT_SEPARATOR: + if (*ptr != *fptr) { + add_pbf_error(s, TIMELIB_ERR_NO_SEP_SYMBOL, "The separation symbol could not be found", string, begin); + break; + } + + ++ptr; + break; + + case TIMELIB_FORMAT_RESET_ALL: /* reset all fields to default */ + timelib_time_reset_fields(s->time); + break; /* break intentionally not missing */ + + case TIMELIB_FORMAT_RESET_ALL_WHEN_NOT_SET: /* reset all fields to default when not set */ + timelib_time_reset_unset_fields(s->time); + break; /* break intentionally not missing */ + + case TIMELIB_FORMAT_RANDOM_CHAR: /* random char */ + ++ptr; + break; + + case TIMELIB_FORMAT_ESCAPE: /* escaped char */ + if (!fptr[1]) { + add_pbf_error(s, TIMELIB_ERR_EXPECTED_ESCAPE_CHAR, "Escaped character expected", string, begin); + break; + } + fptr++; + if (*ptr != *fptr) { + add_pbf_error(s, TIMELIB_ERR_NO_ESCAPED_CHAR, "The escaped character could not be found", string, begin); + break; + } + + ++ptr; + break; + + case TIMELIB_FORMAT_SKIP_TO_SEPARATOR: /* random chars until a separator or number ([ \t.,:;/-0123456789]) */ + timelib_eat_until_separator(&ptr); + break; + + case TIMELIB_FORMAT_ALLOW_EXTRA_CHARACTERS: /* allow extra chars in the format */ + allow_extra = true; + break; + case TIMELIB_FORMAT_YEAR_ISO: + if ((iso_year = timelib_get_nr(&ptr, 4)) == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_NO_FOUR_DIGIT_YEAR_ISO, "A four digit ISO year could not be found", string, begin); + break; + } + + s->time->have_date = 1; + break; + case TIMELIB_FORMAT_WEEK_OF_YEAR_ISO: + if ((iso_week_of_year = timelib_get_nr(&ptr, 2)) == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_NO_TWO_DIGIT_WEEK, "A two digit ISO week could not be found", string, begin); + break; + } + /* Range is 1 - 53 for ISO week of year */ + if (iso_week_of_year < 1 || iso_week_of_year > 53) { + add_pbf_error(s, TIMELIB_ERR_INVALID_WEEK, "ISO Week must be between 1 and 53", string, begin); + break; + } + + s->time->have_date = 1; + break; + case TIMELIB_FORMAT_DAY_OF_WEEK_ISO: + if ((iso_day_of_week = timelib_get_nr(&ptr, 1)) == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_NO_DAY_OF_WEEK, "A single digit day of week could not be found", string, begin); + break; + } + if (iso_day_of_week < 1 || iso_day_of_week > 7) { + add_pbf_error(s, TIMELIB_ERR_INVALID_DAY_OF_WEEK, "Day of week must be between 1 and 7", string, begin); + break; + } + + s->time->have_date = 1; + break; + case TIMELIB_FORMAT_TIMEZONE_OFFSET: /* timezone */ + { + int tz_not_found; + + s->time->z = timelib_parse_zone(&ptr, &s->time->dst, s->time, &tz_not_found, s->tzdb, tz_get_wrapper); + if (tz_not_found) { + add_pbf_error(s, TIMELIB_ERR_TZID_NOT_FOUND, "The timezone could not be found in the database", string, begin); + break; + } + + s->time->have_zone = 1; + } + break; + case TIMELIB_FORMAT_TIMEZONE_OFFSET_MINUTES: /* timezone format +/-mmm */ + s->time->z = timelib_parse_tz_minutes(&ptr, s->time); + if (s->time->z == TIMELIB_UNSET) { + add_pbf_error(s, TIMELIB_ERR_INVALID_TZ_OFFSET, "Invalid timezone offset in minutes", string, begin); + break; + } + + s->time->have_zone = 1; + break; + case TIMELIB_FORMAT_LITERAL: + default: + if (*fptr != *ptr) { + add_pbf_error(s, TIMELIB_ERR_WRONG_FORMAT_SEP, "The format separator does not match", string, begin); + } + ptr++; + } + fptr++; + } + if (*ptr) { + if (allow_extra) { + add_pbf_warning(s, TIMELIB_WARN_TRAILING_DATA, "Trailing data", string, ptr); + } else { + add_pbf_error(s, TIMELIB_ERR_TRAILING_DATA, "Trailing data", string, ptr); + } + } + + if (*fptr) { + /* Trailing reset specifiers are valid. */ + int done = 0; + while (*fptr && !done) { + switch (timelib_lookup_format(*fptr, format_map)) { + case TIMELIB_FORMAT_RESET_ALL: /* reset all fields to default */ + timelib_time_reset_fields(s->time); + break; + + case TIMELIB_FORMAT_RESET_ALL_WHEN_NOT_SET: /* reset all fields to default when not set */ + timelib_time_reset_unset_fields(s->time); + break; + case TIMELIB_FORMAT_ALLOW_EXTRA_CHARACTERS: + break; + + default: + add_pbf_error(s, TIMELIB_ERR_DATA_MISSING, "Data missing", string, ptr); + done = 1; + } + fptr++; + } + } + + /* clean up a bit */ + if (s->time->h != TIMELIB_UNSET || s->time->i != TIMELIB_UNSET || s->time->s != TIMELIB_UNSET || s->time->us != TIMELIB_UNSET) { + if (s->time->h == TIMELIB_UNSET ) { + s->time->h = 0; + } + if (s->time->i == TIMELIB_UNSET ) { + s->time->i = 0; + } + if (s->time->s == TIMELIB_UNSET ) { + s->time->s = 0; + } + if (s->time->us == TIMELIB_UNSET ) { + s->time->us = 0; + } + } + + /* Check for mixing of ISO dates with natural dates. */ + if (s->time->y != TIMELIB_UNSET && (iso_week_of_year != TIMELIB_UNSET || iso_year != TIMELIB_UNSET || iso_day_of_week != TIMELIB_UNSET)) { + add_pbf_error(s, TIMELIB_ERR_MIX_ISO_WITH_NATURAL, "Mixing of ISO dates with natural dates is not allowed", string, ptr); + } + if (iso_year != TIMELIB_UNSET && (s->time->y != TIMELIB_UNSET || s->time->m != TIMELIB_UNSET || s->time->d != TIMELIB_UNSET)) { + add_pbf_error(s, TIMELIB_ERR_MIX_ISO_WITH_NATURAL, "Mixing of ISO dates with natural dates is not allowed", string, ptr); + } + + /* Convert ISO values */ + if (iso_year != TIMELIB_UNSET) { + /* Default week of year and day of week to 1. */ + if (iso_week_of_year == TIMELIB_UNSET) { + iso_week_of_year = 1; + } + if (iso_day_of_week == TIMELIB_UNSET) { + iso_day_of_week = 1; + } + timelib_date_from_isodate(iso_year, iso_week_of_year, iso_day_of_week, &s->time->y, &s->time->m, &s->time->d); + } else if (iso_week_of_year != TIMELIB_UNSET || iso_day_of_week != TIMELIB_UNSET) { + add_pbf_warning(s, TIMELIB_WARN_INVALID_DATE, "The parsed date was invalid", string, ptr); + } + + /* do funky checking whether the parsed time was valid time */ + if (s->time->h != TIMELIB_UNSET && s->time->i != TIMELIB_UNSET && + s->time->s != TIMELIB_UNSET && + !timelib_valid_time( s->time->h, s->time->i, s->time->s)) { + add_pbf_warning(s, TIMELIB_WARN_INVALID_TIME, "The parsed time was invalid", string, ptr); + } + /* do funky checking whether the parsed date was valid date */ + if (s->time->y != TIMELIB_UNSET && s->time->m != TIMELIB_UNSET && + s->time->d != TIMELIB_UNSET && + !timelib_valid_date( s->time->y, s->time->m, s->time->d)) { + add_pbf_warning(s, TIMELIB_WARN_INVALID_DATE, "The parsed date was invalid", string, ptr); + } + + if (errors) { + *errors = in.errors; + } else { + timelib_error_container_dtor(in.errors); + } + return in.time; +} + +void timelib_fill_holes(timelib_time *parsed, timelib_time *now, int options) +{ + if (!(options & TIMELIB_OVERRIDE_TIME) && parsed->have_date && !parsed->have_time) { + parsed->h = 0; + parsed->i = 0; + parsed->s = 0; + parsed->us = 0; + } + if ( + parsed->y != TIMELIB_UNSET || parsed->m != TIMELIB_UNSET || parsed->d != TIMELIB_UNSET || + parsed->h != TIMELIB_UNSET || parsed->i != TIMELIB_UNSET || parsed->s != TIMELIB_UNSET + ) { + if (parsed->us == TIMELIB_UNSET) parsed->us = 0; + } else { + if (parsed->us == TIMELIB_UNSET) parsed->us = now->us != TIMELIB_UNSET ? now->us : 0; + } + if (parsed->y == TIMELIB_UNSET) parsed->y = now->y != TIMELIB_UNSET ? now->y : 0; + if (parsed->m == TIMELIB_UNSET) parsed->m = now->m != TIMELIB_UNSET ? now->m : 0; + if (parsed->d == TIMELIB_UNSET) parsed->d = now->d != TIMELIB_UNSET ? now->d : 0; + if (parsed->h == TIMELIB_UNSET) parsed->h = now->h != TIMELIB_UNSET ? now->h : 0; + if (parsed->i == TIMELIB_UNSET) parsed->i = now->i != TIMELIB_UNSET ? now->i : 0; + if (parsed->s == TIMELIB_UNSET) parsed->s = now->s != TIMELIB_UNSET ? now->s : 0; + if (parsed->z == TIMELIB_UNSET) parsed->z = now->z != TIMELIB_UNSET ? now->z : 0; + if (parsed->dst == TIMELIB_UNSET) parsed->dst = now->dst != TIMELIB_UNSET ? now->dst : 0; + + if (!parsed->tz_abbr) { + parsed->tz_abbr = now->tz_abbr ? timelib_strdup(now->tz_abbr) : NULL; + } + if (!parsed->tz_info) { + parsed->tz_info = now->tz_info ? (!(options & TIMELIB_NO_CLONE) ? timelib_tzinfo_clone(now->tz_info) : now->tz_info) : NULL; + } + if (parsed->zone_type == 0 && now->zone_type != 0) { + parsed->zone_type = now->zone_type; +/* parsed->tz_abbr = now->tz_abbr ? timelib_strdup(now->tz_abbr) : NULL; + parsed->tz_info = now->tz_info ? timelib_tzinfo_clone(now->tz_info) : NULL; +*/ parsed->is_localtime = 1; + } +/* timelib_dump_date(parsed, 2); + timelib_dump_date(now, 2); +*/ +} + +char *timelib_timezone_id_from_abbr(const char *abbr, timelib_long gmtoffset, int isdst) +{ + const timelib_tz_lookup_table *tp; + + tp = abbr_search(abbr, gmtoffset, isdst); + if (tp) { + return (tp->full_tz_name); + } else { + return NULL; + } +} + +const timelib_tz_lookup_table *timelib_timezone_abbreviations_list(void) +{ + return timelib_timezone_lookup; +} + +#ifdef DEBUG_PARSER_STUB +int main(void) +{ + timelib_time time = timelib_strtotime("May 12"); + + printf ("%04d-%02d-%02d %02d:%02d:%02d.%-5d %+04d %1d", + time.y, time.m, time.d, time.h, time.i, time.s, time.f, time.z, time.dst); + if (time.have_relative) { + printf ("%3dY %3dM %3dD / %3dH %3dM %3dS", + time.relative.y, time.relative.m, time.relative.d, time.relative.h, time.relative.i, time.relative.s); + } + if (time.have_weekday_relative) { + printf (" / %d", time.relative.weekday); + } + if (time.have_weeknr_day) { + printf(" / %dW%d", time.relative.weeknr_day.weeknr, time.relative.weeknr_day.dayofweek); + } + return 0; +} +#endif + +/* + * vim: syntax=c + */ |