summaryrefslogtreecommitdiff
path: root/src/third_party/timelib-2021.06/parse_date.re
diff options
context:
space:
mode:
Diffstat (limited to 'src/third_party/timelib-2021.06/parse_date.re')
-rw-r--r--src/third_party/timelib-2021.06/parse_date.re2631
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 = &in;
+ 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
+ */