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