1%{ 2/* Parse a string into an internal time stamp. 3 4 Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005 Free Software 5 Foundation, Inc. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2, or (at your option) 10 any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software Foundation, 19 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 20 21/* Originally written by Steven M. Bellovin <smb@research.att.com> while 22 at the University of North Carolina at Chapel Hill. Later tweaked by 23 a couple of people on Usenet. Completely overhauled by Rich $alz 24 <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990. 25 26 Modified by Paul Eggert <eggert@twinsun.com> in August 1999 to do 27 the right thing about local DST. Also modified by Paul Eggert 28 <eggert@cs.ucla.edu> in February 2004 to support 29 nanosecond-resolution time stamps, and in October 2004 to support 30 TZ strings in dates. */ 31 32/* FIXME: Check for arithmetic overflow in all cases, not just 33 some of them. */ 34 35#ifdef HAVE_CONFIG_H 36# include <config.h> 37#endif 38 39#include "getdate.h" 40 41/* There's no need to extend the stack, so there's no need to involve 42 alloca. */ 43#define YYSTACK_USE_ALLOCA 0 44 45/* Tell Bison how much stack space is needed. 20 should be plenty for 46 this grammar, which is not right recursive. Beware setting it too 47 high, since that might cause problems on machines whose 48 implementations have lame stack-overflow checking. */ 49#define YYMAXDEPTH 20 50#define YYINITDEPTH YYMAXDEPTH 51 52/* Since the code of getdate.y is not included in the Emacs executable 53 itself, there is no need to #define static in this file. Even if 54 the code were included in the Emacs executable, it probably 55 wouldn't do any harm to #undef it here; this will only cause 56 problems if we try to write to a static variable, which I don't 57 think this code needs to do. */ 58#ifdef emacs 59# undef static 60#endif 61 62#include <ctype.h> 63#include <limits.h> 64#include <stdio.h> 65#include <stdlib.h> 66#include <string.h> 67 68#include "setenv.h" 69#include "xalloc.h" 70 71#if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII) 72# define IN_CTYPE_DOMAIN(c) 1 73#else 74# define IN_CTYPE_DOMAIN(c) isascii (c) 75#endif 76 77#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c)) 78#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c)) 79#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c)) 80 81/* ISDIGIT differs from isdigit, as follows: 82 - Its arg may be any int or unsigned int; it need not be an unsigned char. 83 - It's guaranteed to evaluate its argument exactly once. 84 - It's typically faster. 85 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to 86 isdigit unless it's important to use the locale's definition 87 of `digit' even when the host does not conform to POSIX. */ 88#define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9) 89 90#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__ 91# define __attribute__(x) 92#endif 93 94#ifndef ATTRIBUTE_UNUSED 95# define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) 96#endif 97 98/* Shift A right by B bits portably, by dividing A by 2**B and 99 truncating towards minus infinity. A and B should be free of side 100 effects, and B should be in the range 0 <= B <= INT_BITS - 2, where 101 INT_BITS is the number of useful bits in an int. GNU code can 102 assume that INT_BITS is at least 32. 103 104 ISO C99 says that A >> B is implementation-defined if A < 0. Some 105 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift 106 right in the usual way when A < 0, so SHR falls back on division if 107 ordinary A >> B doesn't seem to be the usual signed shift. */ 108#define SHR(a, b) \ 109 (-1 >> 1 == -1 \ 110 ? (a) >> (b) \ 111 : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0)) 112 113#define EPOCH_YEAR 1970 114#define TM_YEAR_BASE 1900 115 116#define HOUR(x) ((x) * 60) 117 118/* An integer value, and the number of digits in its textual 119 representation. */ 120typedef struct 121{ 122 bool negative; 123 long int value; 124 size_t digits; 125} textint; 126 127/* An entry in the lexical lookup table. */ 128typedef struct 129{ 130 char const *name; 131 int type; 132 int value; 133} table; 134 135/* Meridian: am, pm, or 24-hour style. */ 136enum { MERam, MERpm, MER24 }; 137 138enum { BILLION = 1000000000, LOG10_BILLION = 9 }; 139 140/* Information passed to and from the parser. */ 141typedef struct 142{ 143 /* The input string remaining to be parsed. */ 144 const char *input; 145 146 /* N, if this is the Nth Tuesday. */ 147 long int day_ordinal; 148 149 /* Day of week; Sunday is 0. */ 150 int day_number; 151 152 /* tm_isdst flag for the local zone. */ 153 int local_isdst; 154 155 /* Time zone, in minutes east of UTC. */ 156 long int time_zone; 157 158 /* Style used for time. */ 159 int meridian; 160 161 /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds. */ 162 textint year; 163 long int month; 164 long int day; 165 long int hour; 166 long int minutes; 167 struct timespec seconds; /* includes nanoseconds */ 168 169 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */ 170 long int rel_year; 171 long int rel_month; 172 long int rel_day; 173 long int rel_hour; 174 long int rel_minutes; 175 long int rel_seconds; 176 long int rel_ns; 177 178 /* Presence or counts of nonterminals of various flavors parsed so far. */ 179 bool timespec_seen; 180 bool rels_seen; 181 size_t dates_seen; 182 size_t days_seen; 183 size_t local_zones_seen; 184 size_t dsts_seen; 185 size_t times_seen; 186 size_t zones_seen; 187 188 /* Table of local time zone abbrevations, terminated by a null entry. */ 189 table local_time_zone_table[3]; 190} parser_control; 191 192union YYSTYPE; 193static int yylex (union YYSTYPE *, parser_control *); 194static int yyerror (parser_control *, char *); 195static long int time_zone_hhmm (textint, long int); 196 197%} 198 199/* We want a reentrant parser, even if the TZ manipulation and the calls to 200 localtime and gmtime are not reentrant. */ 201%pure-parser 202%parse-param { parser_control *pc } 203%lex-param { parser_control *pc } 204 205/* This grammar has 20 shift/reduce conflicts. */ 206%expect 20 207 208%union 209{ 210 long int intval; 211 textint textintval; 212 struct timespec timespec; 213} 214 215%token tAGO tDST 216 217%token <intval> tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tLOCAL_ZONE tMERIDIAN 218%token <intval> tMINUTE_UNIT tMONTH tMONTH_UNIT tORDINAL 219%token <intval> tSEC_UNIT tYEAR_UNIT tZONE 220 221%token <textintval> tSNUMBER tUNUMBER 222%token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER 223 224%type <intval> o_colon_minutes o_merid 225%type <timespec> seconds signed_seconds unsigned_seconds 226 227%% 228 229spec: 230 timespec 231 | items 232 ; 233 234timespec: 235 '@' seconds 236 { 237 pc->seconds = $2; 238 pc->timespec_seen = true; 239 } 240 ; 241 242items: 243 /* empty */ 244 | items item 245 ; 246 247item: 248 time 249 { pc->times_seen++; } 250 | local_zone 251 { pc->local_zones_seen++; } 252 | zone 253 { pc->zones_seen++; } 254 | date 255 { pc->dates_seen++; } 256 | day 257 { pc->days_seen++; } 258 | rel 259 { pc->rels_seen = true; } 260 | cvsstamp 261 { 262 pc->dates_seen++; 263 pc->zones_seen++; 264 pc->times_seen++; 265 } 266 | number 267 ; 268 269cvsstamp: tUDECIMAL_NUMBER '.' tUDECIMAL_NUMBER '.' tUDECIMAL_NUMBER 270 { 271 int i; 272 pc->year.negative = 0; 273 pc->year.value = $1.tv_sec; 274 275 if (pc->year.value < 70) 276 pc->year.value += 2000; 277 else if (pc->year.value < 100) 278 pc->year.value += 1900; 279 280 for (i = pc->year.value, pc->year.digits = 0; i; i /= 10, pc->year.digits++) 281 continue; 282 if (pc->year.digits == 0) 283 pc->year.digits++; 284 285 pc->month = $1.tv_nsec / 10000000; 286 pc->day = $3.tv_sec; 287 pc->hour = $3.tv_nsec / 10000000; 288 pc->minutes = $5.tv_sec; 289 pc->seconds.tv_sec = $5.tv_nsec / 10000000; 290 pc->seconds.tv_nsec = 0; 291 pc->meridian = MER24; 292 pc->time_zone = 0; 293 } 294 295time: 296 tUNUMBER tMERIDIAN 297 { 298 pc->hour = $1.value; 299 pc->minutes = 0; 300 pc->seconds.tv_sec = 0; 301 pc->seconds.tv_nsec = 0; 302 pc->meridian = $2; 303 } 304 | tUNUMBER ':' tUNUMBER o_merid 305 { 306 pc->hour = $1.value; 307 pc->minutes = $3.value; 308 pc->seconds.tv_sec = 0; 309 pc->seconds.tv_nsec = 0; 310 pc->meridian = $4; 311 } 312 | tUNUMBER ':' tUNUMBER tSNUMBER o_colon_minutes 313 { 314 pc->hour = $1.value; 315 pc->minutes = $3.value; 316 pc->seconds.tv_sec = 0; 317 pc->seconds.tv_nsec = 0; 318 pc->meridian = MER24; 319 pc->zones_seen++; 320 pc->time_zone = time_zone_hhmm ($4, $5); 321 } 322 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_merid 323 { 324 pc->hour = $1.value; 325 pc->minutes = $3.value; 326 pc->seconds = $5; 327 pc->meridian = $6; 328 } 329 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER o_colon_minutes 330 { 331 pc->hour = $1.value; 332 pc->minutes = $3.value; 333 pc->seconds = $5; 334 pc->meridian = MER24; 335 pc->zones_seen++; 336 pc->time_zone = time_zone_hhmm ($6, $7); 337 } 338 ; 339 340local_zone: 341 tLOCAL_ZONE 342 { 343 pc->local_isdst = $1; 344 pc->dsts_seen += (0 < $1); 345 } 346 | tLOCAL_ZONE tDST 347 { 348 pc->local_isdst = 1; 349 pc->dsts_seen += (0 < $1) + 1; 350 } 351 ; 352 353zone: 354 tZONE 355 { pc->time_zone = $1; } 356 | tZONE relunit_snumber 357 { pc->time_zone = $1; pc->rels_seen = true; } 358 | tZONE tSNUMBER o_colon_minutes 359 { pc->time_zone = $1 + time_zone_hhmm ($2, $3); } 360 | tDAYZONE 361 { pc->time_zone = $1 + 60; } 362 | tZONE tDST 363 { pc->time_zone = $1 + 60; } 364 ; 365 366day: 367 tDAY 368 { 369 pc->day_ordinal = 1; 370 pc->day_number = $1; 371 } 372 | tDAY ',' 373 { 374 pc->day_ordinal = 1; 375 pc->day_number = $1; 376 } 377 | tORDINAL tDAY 378 { 379 pc->day_ordinal = $1; 380 pc->day_number = $2; 381 } 382 | tUNUMBER tDAY 383 { 384 pc->day_ordinal = $1.value; 385 pc->day_number = $2; 386 } 387 ; 388 389date: 390 tUNUMBER '/' tUNUMBER 391 { 392 pc->month = $1.value; 393 pc->day = $3.value; 394 } 395 | tUNUMBER '/' tUNUMBER '/' tUNUMBER 396 { 397 /* Interpret as YYYY/MM/DD if the first value has 4 or more digits, 398 otherwise as MM/DD/YY. 399 The goal in recognizing YYYY/MM/DD is solely to support legacy 400 machine-generated dates like those in an RCS log listing. If 401 you want portability, use the ISO 8601 format. */ 402 if (4 <= $1.digits) 403 { 404 pc->year = $1; 405 pc->month = $3.value; 406 pc->day = $5.value; 407 } 408 else 409 { 410 pc->month = $1.value; 411 pc->day = $3.value; 412 pc->year = $5; 413 } 414 } 415 | tUNUMBER tSNUMBER tSNUMBER 416 { 417 /* ISO 8601 format. YYYY-MM-DD. */ 418 pc->year = $1; 419 pc->month = -$2.value; 420 pc->day = -$3.value; 421 } 422 | tUNUMBER tMONTH tSNUMBER 423 { 424 /* e.g. 17-JUN-1992. */ 425 pc->day = $1.value; 426 pc->month = $2; 427 pc->year.value = -$3.value; 428 pc->year.digits = $3.digits; 429 } 430 | tMONTH tSNUMBER tSNUMBER 431 { 432 /* e.g. JUN-17-1992. */ 433 pc->month = $1; 434 pc->day = -$2.value; 435 pc->year.value = -$3.value; 436 pc->year.digits = $3.digits; 437 } 438 | tMONTH tUNUMBER 439 { 440 pc->month = $1; 441 pc->day = $2.value; 442 } 443 | tMONTH tUNUMBER ',' tUNUMBER 444 { 445 pc->month = $1; 446 pc->day = $2.value; 447 pc->year = $4; 448 } 449 | tUNUMBER tMONTH 450 { 451 pc->day = $1.value; 452 pc->month = $2; 453 } 454 | tUNUMBER tMONTH tUNUMBER 455 { 456 pc->day = $1.value; 457 pc->month = $2; 458 pc->year = $3; 459 } 460 ; 461 462rel: 463 relunit tAGO 464 { 465 pc->rel_ns = -pc->rel_ns; 466 pc->rel_seconds = -pc->rel_seconds; 467 pc->rel_minutes = -pc->rel_minutes; 468 pc->rel_hour = -pc->rel_hour; 469 pc->rel_day = -pc->rel_day; 470 pc->rel_month = -pc->rel_month; 471 pc->rel_year = -pc->rel_year; 472 } 473 | relunit 474 ; 475 476relunit: 477 tORDINAL tYEAR_UNIT 478 { pc->rel_year += $1 * $2; } 479 | tUNUMBER tYEAR_UNIT 480 { pc->rel_year += $1.value * $2; } 481 | tYEAR_UNIT 482 { pc->rel_year += $1; } 483 | tORDINAL tMONTH_UNIT 484 { pc->rel_month += $1 * $2; } 485 | tUNUMBER tMONTH_UNIT 486 { pc->rel_month += $1.value * $2; } 487 | tMONTH_UNIT 488 { pc->rel_month += $1; } 489 | tORDINAL tDAY_UNIT 490 { pc->rel_day += $1 * $2; } 491 | tUNUMBER tDAY_UNIT 492 { pc->rel_day += $1.value * $2; } 493 | tDAY_UNIT 494 { pc->rel_day += $1; } 495 | tORDINAL tHOUR_UNIT 496 { pc->rel_hour += $1 * $2; } 497 | tUNUMBER tHOUR_UNIT 498 { pc->rel_hour += $1.value * $2; } 499 | tHOUR_UNIT 500 { pc->rel_hour += $1; } 501 | tORDINAL tMINUTE_UNIT 502 { pc->rel_minutes += $1 * $2; } 503 | tUNUMBER tMINUTE_UNIT 504 { pc->rel_minutes += $1.value * $2; } 505 | tMINUTE_UNIT 506 { pc->rel_minutes += $1; } 507 | tORDINAL tSEC_UNIT 508 { pc->rel_seconds += $1 * $2; } 509 | tUNUMBER tSEC_UNIT 510 { pc->rel_seconds += $1.value * $2; } 511 | tSDECIMAL_NUMBER tSEC_UNIT 512 { pc->rel_seconds += $1.tv_sec * $2; pc->rel_ns += $1.tv_nsec * $2; } 513 | tUDECIMAL_NUMBER tSEC_UNIT 514 { pc->rel_seconds += $1.tv_sec * $2; pc->rel_ns += $1.tv_nsec * $2; } 515 | tSEC_UNIT 516 { pc->rel_seconds += $1; } 517 | relunit_snumber 518 ; 519 520relunit_snumber: 521 tSNUMBER tYEAR_UNIT 522 { pc->rel_year += $1.value * $2; } 523 | tSNUMBER tMONTH_UNIT 524 { pc->rel_month += $1.value * $2; } 525 | tSNUMBER tDAY_UNIT 526 { pc->rel_day += $1.value * $2; } 527 | tSNUMBER tHOUR_UNIT 528 { pc->rel_hour += $1.value * $2; } 529 | tSNUMBER tMINUTE_UNIT 530 { pc->rel_minutes += $1.value * $2; } 531 | tSNUMBER tSEC_UNIT 532 { pc->rel_seconds += $1.value * $2; } 533 ; 534 535seconds: signed_seconds | unsigned_seconds; 536 537signed_seconds: 538 tSDECIMAL_NUMBER 539 | tSNUMBER 540 { $$.tv_sec = $1.value; $$.tv_nsec = 0; } 541 ; 542 543unsigned_seconds: 544 tUDECIMAL_NUMBER 545 | tUNUMBER 546 { $$.tv_sec = $1.value; $$.tv_nsec = 0; } 547 ; 548 549number: 550 tUNUMBER 551 { 552 if (pc->dates_seen && ! pc->year.digits 553 && ! pc->rels_seen && (pc->times_seen || 2 < $1.digits)) 554 pc->year = $1; 555 else 556 { 557 if (4 < $1.digits) 558 { 559 pc->dates_seen++; 560 pc->day = $1.value % 100; 561 pc->month = ($1.value / 100) % 100; 562 pc->year.value = $1.value / 10000; 563 pc->year.digits = $1.digits - 4; 564 } 565 else 566 { 567 pc->times_seen++; 568 if ($1.digits <= 2) 569 { 570 pc->hour = $1.value; 571 pc->minutes = 0; 572 } 573 else 574 { 575 pc->hour = $1.value / 100; 576 pc->minutes = $1.value % 100; 577 } 578 pc->seconds.tv_sec = 0; 579 pc->seconds.tv_nsec = 0; 580 pc->meridian = MER24; 581 } 582 } 583 } 584 ; 585 586o_colon_minutes: 587 /* empty */ 588 { $$ = -1; } 589 | ':' tUNUMBER 590 { $$ = $2.value; } 591 ; 592 593o_merid: 594 /* empty */ 595 { $$ = MER24; } 596 | tMERIDIAN 597 { $$ = $1; } 598 ; 599 600%% 601 602static table const meridian_table[] = 603{ 604 { "AM", tMERIDIAN, MERam }, 605 { "A.M.", tMERIDIAN, MERam }, 606 { "PM", tMERIDIAN, MERpm }, 607 { "P.M.", tMERIDIAN, MERpm }, 608 { NULL, 0, 0 } 609}; 610 611static table const dst_table[] = 612{ 613 { "DST", tDST, 0 } 614}; 615 616static table const month_and_day_table[] = 617{ 618 { "JANUARY", tMONTH, 1 }, 619 { "FEBRUARY", tMONTH, 2 }, 620 { "MARCH", tMONTH, 3 }, 621 { "APRIL", tMONTH, 4 }, 622 { "MAY", tMONTH, 5 }, 623 { "JUNE", tMONTH, 6 }, 624 { "JULY", tMONTH, 7 }, 625 { "AUGUST", tMONTH, 8 }, 626 { "SEPTEMBER",tMONTH, 9 }, 627 { "SEPT", tMONTH, 9 }, 628 { "OCTOBER", tMONTH, 10 }, 629 { "NOVEMBER", tMONTH, 11 }, 630 { "DECEMBER", tMONTH, 12 }, 631 { "SUNDAY", tDAY, 0 }, 632 { "MONDAY", tDAY, 1 }, 633 { "TUESDAY", tDAY, 2 }, 634 { "TUES", tDAY, 2 }, 635 { "WEDNESDAY",tDAY, 3 }, 636 { "WEDNES", tDAY, 3 }, 637 { "THURSDAY", tDAY, 4 }, 638 { "THUR", tDAY, 4 }, 639 { "THURS", tDAY, 4 }, 640 { "FRIDAY", tDAY, 5 }, 641 { "SATURDAY", tDAY, 6 }, 642 { NULL, 0, 0 } 643}; 644 645static table const time_units_table[] = 646{ 647 { "YEAR", tYEAR_UNIT, 1 }, 648 { "MONTH", tMONTH_UNIT, 1 }, 649 { "FORTNIGHT",tDAY_UNIT, 14 }, 650 { "WEEK", tDAY_UNIT, 7 }, 651 { "DAY", tDAY_UNIT, 1 }, 652 { "HOUR", tHOUR_UNIT, 1 }, 653 { "MINUTE", tMINUTE_UNIT, 1 }, 654 { "MIN", tMINUTE_UNIT, 1 }, 655 { "SECOND", tSEC_UNIT, 1 }, 656 { "SEC", tSEC_UNIT, 1 }, 657 { NULL, 0, 0 } 658}; 659 660/* Assorted relative-time words. */ 661static table const relative_time_table[] = 662{ 663 { "TOMORROW", tDAY_UNIT, 1 }, 664 { "YESTERDAY",tDAY_UNIT, -1 }, 665 { "TODAY", tDAY_UNIT, 0 }, 666 { "NOW", tDAY_UNIT, 0 }, 667 { "LAST", tORDINAL, -1 }, 668 { "THIS", tORDINAL, 0 }, 669 { "NEXT", tORDINAL, 1 }, 670 { "FIRST", tORDINAL, 1 }, 671/*{ "SECOND", tORDINAL, 2 }, */ 672 { "THIRD", tORDINAL, 3 }, 673 { "FOURTH", tORDINAL, 4 }, 674 { "FIFTH", tORDINAL, 5 }, 675 { "SIXTH", tORDINAL, 6 }, 676 { "SEVENTH", tORDINAL, 7 }, 677 { "EIGHTH", tORDINAL, 8 }, 678 { "NINTH", tORDINAL, 9 }, 679 { "TENTH", tORDINAL, 10 }, 680 { "ELEVENTH", tORDINAL, 11 }, 681 { "TWELFTH", tORDINAL, 12 }, 682 { "AGO", tAGO, 1 }, 683 { NULL, 0, 0 } 684}; 685 686/* The universal time zone table. These labels can be used even for 687 time stamps that would not otherwise be valid, e.g., GMT time 688 stamps in London during summer. */ 689static table const universal_time_zone_table[] = 690{ 691 { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */ 692 { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */ 693 { "UTC", tZONE, HOUR ( 0) }, 694 { NULL, 0, 0 } 695}; 696 697/* The time zone table. This table is necessarily incomplete, as time 698 zone abbreviations are ambiguous; e.g. Australians interpret "EST" 699 as Eastern time in Australia, not as US Eastern Standard Time. 700 You cannot rely on getdate to handle arbitrary time zone 701 abbreviations; use numeric abbreviations like `-0500' instead. */ 702static table const time_zone_table[] = 703{ 704 { "WET", tZONE, HOUR ( 0) }, /* Western European */ 705 { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */ 706 { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */ 707 { "ART", tZONE, -HOUR ( 3) }, /* Argentina */ 708 { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */ 709 { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */ 710 { "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */ 711 { "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */ 712 { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */ 713 { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */ 714 { "CLT", tZONE, -HOUR ( 4) }, /* Chile */ 715 { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */ 716 { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */ 717 { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */ 718 { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */ 719 { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */ 720 { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */ 721 { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */ 722 { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */ 723 { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */ 724 { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */ 725 { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */ 726 { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */ 727 { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */ 728 { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */ 729 { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */ 730 { "WAT", tZONE, HOUR ( 1) }, /* West Africa */ 731 { "CET", tZONE, HOUR ( 1) }, /* Central European */ 732 { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */ 733 { "MET", tZONE, HOUR ( 1) }, /* Middle European */ 734 { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */ 735 { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */ 736 { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */ 737 { "EET", tZONE, HOUR ( 2) }, /* Eastern European */ 738 { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */ 739 { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */ 740 { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */ 741 { "EAT", tZONE, HOUR ( 3) }, /* East Africa */ 742 { "MSK", tZONE, HOUR ( 3) }, /* Moscow */ 743 { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */ 744 { "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */ 745 { "SGT", tZONE, HOUR ( 8) }, /* Singapore */ 746 { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */ 747 { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */ 748 { "GST", tZONE, HOUR (10) }, /* Guam Standard */ 749 { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */ 750 { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */ 751 { NULL, 0, 0 } 752}; 753 754/* Military time zone table. */ 755static table const military_table[] = 756{ 757 { "A", tZONE, -HOUR ( 1) }, 758 { "B", tZONE, -HOUR ( 2) }, 759 { "C", tZONE, -HOUR ( 3) }, 760 { "D", tZONE, -HOUR ( 4) }, 761 { "E", tZONE, -HOUR ( 5) }, 762 { "F", tZONE, -HOUR ( 6) }, 763 { "G", tZONE, -HOUR ( 7) }, 764 { "H", tZONE, -HOUR ( 8) }, 765 { "I", tZONE, -HOUR ( 9) }, 766 { "K", tZONE, -HOUR (10) }, 767 { "L", tZONE, -HOUR (11) }, 768 { "M", tZONE, -HOUR (12) }, 769 { "N", tZONE, HOUR ( 1) }, 770 { "O", tZONE, HOUR ( 2) }, 771 { "P", tZONE, HOUR ( 3) }, 772 { "Q", tZONE, HOUR ( 4) }, 773 { "R", tZONE, HOUR ( 5) }, 774 { "S", tZONE, HOUR ( 6) }, 775 { "T", tZONE, HOUR ( 7) }, 776 { "U", tZONE, HOUR ( 8) }, 777 { "V", tZONE, HOUR ( 9) }, 778 { "W", tZONE, HOUR (10) }, 779 { "X", tZONE, HOUR (11) }, 780 { "Y", tZONE, HOUR (12) }, 781 { "Z", tZONE, HOUR ( 0) }, 782 { NULL, 0, 0 } 783}; 784 785 786 787/* Convert a time zone expressed as HH:MM into an integer count of 788 minutes. If MM is negative, then S is of the form HHMM and needs 789 to be picked apart; otherwise, S is of the form HH. */ 790 791static long int 792time_zone_hhmm (textint s, long int mm) 793{ 794 if (mm < 0) 795 return (s.value / 100) * 60 + s.value % 100; 796 else 797 return s.value * 60 + (s.negative ? -mm : mm); 798} 799 800static int 801to_hour (long int hours, int meridian) 802{ 803 switch (meridian) 804 { 805 default: /* Pacify GCC. */ 806 case MER24: 807 return 0 <= hours && hours < 24 ? hours : -1; 808 case MERam: 809 return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1; 810 case MERpm: 811 return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1; 812 } 813} 814 815static long int 816to_year (textint textyear) 817{ 818 long int year = textyear.value; 819 820 if (year < 0) 821 year = -year; 822 823 /* XPG4 suggests that years 00-68 map to 2000-2068, and 824 years 69-99 map to 1969-1999. */ 825 else if (textyear.digits == 2) 826 year += year < 69 ? 2000 : 1900; 827 828 return year; 829} 830 831static table const * 832lookup_zone (parser_control const *pc, char const *name) 833{ 834 table const *tp; 835 836 for (tp = universal_time_zone_table; tp->name; tp++) 837 if (strcmp (name, tp->name) == 0) 838 return tp; 839 840 /* Try local zone abbreviations before those in time_zone_table, as 841 the local ones are more likely to be right. */ 842 for (tp = pc->local_time_zone_table; tp->name; tp++) 843 if (strcmp (name, tp->name) == 0) 844 return tp; 845 846 for (tp = time_zone_table; tp->name; tp++) 847 if (strcmp (name, tp->name) == 0) 848 return tp; 849 850 return NULL; 851} 852 853#if ! HAVE_TM_GMTOFF 854/* Yield the difference between *A and *B, 855 measured in seconds, ignoring leap seconds. 856 The body of this function is taken directly from the GNU C Library; 857 see src/strftime.c. */ 858static long int 859tm_diff (struct tm const *a, struct tm const *b) 860{ 861 /* Compute intervening leap days correctly even if year is negative. 862 Take care to avoid int overflow in leap day calculations. */ 863 int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3); 864 int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3); 865 int a100 = a4 / 25 - (a4 % 25 < 0); 866 int b100 = b4 / 25 - (b4 % 25 < 0); 867 int a400 = SHR (a100, 2); 868 int b400 = SHR (b100, 2); 869 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400); 870 long int ayear = a->tm_year; 871 long int years = ayear - b->tm_year; 872 long int days = (365 * years + intervening_leap_days 873 + (a->tm_yday - b->tm_yday)); 874 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour)) 875 + (a->tm_min - b->tm_min)) 876 + (a->tm_sec - b->tm_sec)); 877} 878#endif /* ! HAVE_TM_GMTOFF */ 879 880static table const * 881lookup_word (parser_control const *pc, char *word) 882{ 883 char *p; 884 char *q; 885 size_t wordlen; 886 table const *tp; 887 bool period_found; 888 bool abbrev; 889 890 /* Make it uppercase. */ 891 for (p = word; *p; p++) 892 { 893 unsigned char ch = *p; 894 if (ISLOWER (ch)) 895 *p = toupper (ch); 896 } 897 898 for (tp = meridian_table; tp->name; tp++) 899 if (strcmp (word, tp->name) == 0) 900 return tp; 901 902 /* See if we have an abbreviation for a month. */ 903 wordlen = strlen (word); 904 abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.'); 905 906 for (tp = month_and_day_table; tp->name; tp++) 907 if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0) 908 return tp; 909 910 if ((tp = lookup_zone (pc, word))) 911 return tp; 912 913 if (strcmp (word, dst_table[0].name) == 0) 914 return dst_table; 915 916 for (tp = time_units_table; tp->name; tp++) 917 if (strcmp (word, tp->name) == 0) 918 return tp; 919 920 /* Strip off any plural and try the units table again. */ 921 if (word[wordlen - 1] == 'S') 922 { 923 word[wordlen - 1] = '\0'; 924 for (tp = time_units_table; tp->name; tp++) 925 if (strcmp (word, tp->name) == 0) 926 return tp; 927 word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */ 928 } 929 930 for (tp = relative_time_table; tp->name; tp++) 931 if (strcmp (word, tp->name) == 0) 932 return tp; 933 934 /* Military time zones. */ 935 if (wordlen == 1) 936 for (tp = military_table; tp->name; tp++) 937 if (word[0] == tp->name[0]) 938 return tp; 939 940 /* Drop out any periods and try the time zone table again. */ 941 for (period_found = false, p = q = word; (*p = *q); q++) 942 if (*q == '.') 943 period_found = true; 944 else 945 p++; 946 if (period_found && (tp = lookup_zone (pc, word))) 947 return tp; 948 949 return NULL; 950} 951 952static int 953yylex (YYSTYPE *lvalp, parser_control *pc) 954{ 955 unsigned char c; 956 size_t count; 957 958 for (;;) 959 { 960 while (c = *pc->input, ISSPACE (c)) 961 pc->input++; 962 963 if (ISDIGIT (c) || c == '-' || c == '+') 964 { 965 char const *p; 966 int sign; 967 unsigned long int value; 968 if (c == '-' || c == '+') 969 { 970 sign = c == '-' ? -1 : 1; 971 while (c = *++pc->input, ISSPACE (c)) 972 continue; 973 if (! ISDIGIT (c)) 974 /* skip the '-' sign */ 975 continue; 976 } 977 else 978 sign = 0; 979 p = pc->input; 980 for (value = 0; ; value *= 10) 981 { 982 unsigned long int value1 = value + (c - '0'); 983 if (value1 < value) 984 return '?'; 985 value = value1; 986 c = *++p; 987 if (! ISDIGIT (c)) 988 break; 989 if (ULONG_MAX / 10 < value) 990 return '?'; 991 } 992 if ((c == '.' || c == ',') && ISDIGIT (p[1])) 993 { 994 time_t s; 995 int ns; 996 int digits; 997 unsigned long int value1; 998 999 /* Check for overflow when converting value to time_t. */ 1000 if (sign < 0) 1001 { 1002 s = - value; 1003 if (0 < s) 1004 return '?'; 1005 value1 = -s; 1006 } 1007 else 1008 { 1009 s = value; 1010 if (s < 0) 1011 return '?'; 1012 value1 = s; 1013 } 1014 if (value != value1) 1015 return '?'; 1016 1017 /* Accumulate fraction, to ns precision. */ 1018 p++; 1019 ns = *p++ - '0'; 1020 for (digits = 2; digits <= LOG10_BILLION; digits++) 1021 { 1022 ns *= 10; 1023 if (ISDIGIT (*p)) 1024 ns += *p++ - '0'; 1025 } 1026 1027 /* Skip excess digits, truncating toward -Infinity. */ 1028 if (sign < 0) 1029 for (; ISDIGIT (*p); p++) 1030 if (*p != '0') 1031 { 1032 ns++; 1033 break; 1034 } 1035 while (ISDIGIT (*p)) 1036 p++; 1037 1038 /* Adjust to the timespec convention, which is that 1039 tv_nsec is always a positive offset even if tv_sec is 1040 negative. */ 1041 if (sign < 0 && ns) 1042 { 1043 s--; 1044 if (! (s < 0)) 1045 return '?'; 1046 ns = BILLION - ns; 1047 } 1048 1049 lvalp->timespec.tv_sec = s; 1050 lvalp->timespec.tv_nsec = ns; 1051 pc->input = p; 1052 return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER; 1053 } 1054 else 1055 { 1056 lvalp->textintval.negative = sign < 0; 1057 if (sign < 0) 1058 { 1059 lvalp->textintval.value = - value; 1060 if (0 < lvalp->textintval.value) 1061 return '?'; 1062 } 1063 else 1064 { 1065 lvalp->textintval.value = value; 1066 if (lvalp->textintval.value < 0) 1067 return '?'; 1068 } 1069 lvalp->textintval.digits = p - pc->input; 1070 pc->input = p; 1071 return sign ? tSNUMBER : tUNUMBER; 1072 } 1073 } 1074 1075 if (ISALPHA (c)) 1076 { 1077 char buff[20]; 1078 char *p = buff; 1079 table const *tp; 1080 1081 do 1082 { 1083 if (p < buff + sizeof buff - 1) 1084 *p++ = c; 1085 c = *++pc->input; 1086 } 1087 while (ISALPHA (c) || c == '.'); 1088 1089 *p = '\0'; 1090 tp = lookup_word (pc, buff); 1091 if (! tp) 1092 return '?'; 1093 lvalp->intval = tp->value; 1094 return tp->type; 1095 } 1096 1097 if (c != '(') 1098 return *pc->input++; 1099 count = 0; 1100 do 1101 { 1102 c = *pc->input++; 1103 if (c == '\0') 1104 return c; 1105 if (c == '(') 1106 count++; 1107 else if (c == ')') 1108 count--; 1109 } 1110 while (count != 0); 1111 } 1112} 1113 1114/* Do nothing if the parser reports an error. */ 1115static int 1116yyerror (parser_control *pc ATTRIBUTE_UNUSED, char *s ATTRIBUTE_UNUSED) 1117{ 1118 return 0; 1119} 1120 1121/* If *TM0 is the old and *TM1 is the new value of a struct tm after 1122 passing it to mktime, return true if it's OK that mktime returned T. 1123 It's not OK if *TM0 has out-of-range members. */ 1124 1125static bool 1126mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t) 1127{ 1128 if (t == (time_t) -1) 1129 { 1130 /* Guard against falsely reporting an error when parsing a time 1131 stamp that happens to equal (time_t) -1, on a host that 1132 supports such a time stamp. */ 1133 tm1 = localtime (&t); 1134 if (!tm1) 1135 return false; 1136 } 1137 1138 return ! ((tm0->tm_sec ^ tm1->tm_sec) 1139 | (tm0->tm_min ^ tm1->tm_min) 1140 | (tm0->tm_hour ^ tm1->tm_hour) 1141 | (tm0->tm_mday ^ tm1->tm_mday) 1142 | (tm0->tm_mon ^ tm1->tm_mon) 1143 | (tm0->tm_year ^ tm1->tm_year)); 1144} 1145 1146/* A reasonable upper bound for the size of ordinary TZ strings. 1147 Use heap allocation if TZ's length exceeds this. */ 1148enum { TZBUFSIZE = 100 }; 1149 1150/* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated 1151 otherwise. */ 1152static char * 1153get_tz (char tzbuf[TZBUFSIZE]) 1154{ 1155 char *tz = getenv ("TZ"); 1156 if (tz) 1157 { 1158 size_t tzsize = strlen (tz) + 1; 1159 tz = (tzsize <= TZBUFSIZE 1160 ? memcpy (tzbuf, tz, tzsize) 1161 : xmemdup (tz, tzsize)); 1162 } 1163 return tz; 1164} 1165 1166/* Parse a date/time string, storing the resulting time value into *RESULT. 1167 The string itself is pointed to by P. Return true if successful. 1168 P can be an incomplete or relative time specification; if so, use 1169 *NOW as the basis for the returned time. */ 1170bool 1171get_date (struct timespec *result, char const *p, struct timespec const *now) 1172{ 1173 time_t Start; 1174 long int Start_ns; 1175 struct tm const *tmp; 1176 struct tm tm; 1177 struct tm tm0; 1178 parser_control pc; 1179 struct timespec gettime_buffer; 1180 unsigned char c; 1181 bool tz_was_altered = false; 1182 char *tz0 = NULL; 1183 char tz0buf[TZBUFSIZE]; 1184 bool ok = true; 1185 1186 if (! now) 1187 { 1188 gettime (&gettime_buffer); 1189 now = &gettime_buffer; 1190 } 1191 1192 Start = now->tv_sec; 1193 Start_ns = now->tv_nsec; 1194 1195 tmp = localtime (&now->tv_sec); 1196 if (! tmp) 1197 return false; 1198 1199 while (c = *p, ISSPACE (c)) 1200 p++; 1201 1202 if (strncmp (p, "TZ=\"", 4) == 0) 1203 { 1204 char const *tzbase = p + 4; 1205 size_t tzsize = 1; 1206 char const *s; 1207 1208 for (s = tzbase; *s; s++, tzsize++) 1209 if (*s == '\\') 1210 { 1211 s++; 1212 if (! (*s == '\\' || *s == '"')) 1213 break; 1214 } 1215 else if (*s == '"') 1216 { 1217 char *z; 1218 char *tz1; 1219 char tz1buf[TZBUFSIZE]; 1220 bool large_tz = TZBUFSIZE < tzsize; 1221 bool setenv_ok; 1222 tz0 = get_tz (tz0buf); 1223 z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf; 1224 for (s = tzbase; *s != '"'; s++) 1225 *z++ = *(s += *s == '\\'); 1226 *z = '\0'; 1227 setenv_ok = setenv ("TZ", tz1, 1) == 0; 1228 if (large_tz) 1229 free (tz1); 1230 if (!setenv_ok) 1231 goto fail; 1232 tz_was_altered = true; 1233 p = s + 1; 1234 } 1235 } 1236 1237 pc.input = p; 1238 pc.year.value = tmp->tm_year; 1239 pc.year.value += TM_YEAR_BASE; 1240 pc.year.digits = 0; 1241 pc.month = tmp->tm_mon + 1; 1242 pc.day = tmp->tm_mday; 1243 pc.hour = tmp->tm_hour; 1244 pc.minutes = tmp->tm_min; 1245 pc.seconds.tv_sec = tmp->tm_sec; 1246 pc.seconds.tv_nsec = Start_ns; 1247 tm.tm_isdst = tmp->tm_isdst; 1248 1249 pc.meridian = MER24; 1250 pc.rel_ns = 0; 1251 pc.rel_seconds = 0; 1252 pc.rel_minutes = 0; 1253 pc.rel_hour = 0; 1254 pc.rel_day = 0; 1255 pc.rel_month = 0; 1256 pc.rel_year = 0; 1257 pc.timespec_seen = false; 1258 pc.rels_seen = false; 1259 pc.dates_seen = 0; 1260 pc.days_seen = 0; 1261 pc.times_seen = 0; 1262 pc.local_zones_seen = 0; 1263 pc.dsts_seen = 0; 1264 pc.zones_seen = 0; 1265 1266#if HAVE_STRUCT_TM_TM_ZONE 1267 pc.local_time_zone_table[0].name = tmp->tm_zone; 1268 pc.local_time_zone_table[0].type = tLOCAL_ZONE; 1269 pc.local_time_zone_table[0].value = tmp->tm_isdst; 1270 pc.local_time_zone_table[1].name = NULL; 1271 1272 /* Probe the names used in the next three calendar quarters, looking 1273 for a tm_isdst different from the one we already have. */ 1274 { 1275 int quarter; 1276 for (quarter = 1; quarter <= 3; quarter++) 1277 { 1278 time_t probe = Start + quarter * (90 * 24 * 60 * 60); 1279 struct tm const *probe_tm = localtime (&probe); 1280 if (probe_tm && probe_tm->tm_zone 1281 && probe_tm->tm_isdst != pc.local_time_zone_table[0].value) 1282 { 1283 { 1284 pc.local_time_zone_table[1].name = probe_tm->tm_zone; 1285 pc.local_time_zone_table[1].type = tLOCAL_ZONE; 1286 pc.local_time_zone_table[1].value = probe_tm->tm_isdst; 1287 pc.local_time_zone_table[2].name = NULL; 1288 } 1289 break; 1290 } 1291 } 1292 } 1293#else 1294#if HAVE_TZNAME 1295 { 1296# ifndef tzname 1297 extern char *tzname[]; 1298# endif 1299 int i; 1300 for (i = 0; i < 2; i++) 1301 { 1302 pc.local_time_zone_table[i].name = tzname[i]; 1303 pc.local_time_zone_table[i].type = tLOCAL_ZONE; 1304 pc.local_time_zone_table[i].value = i; 1305 } 1306 pc.local_time_zone_table[i].name = NULL; 1307 } 1308#else 1309 pc.local_time_zone_table[0].name = NULL; 1310#endif 1311#endif 1312 1313 if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name 1314 && ! strcmp (pc.local_time_zone_table[0].name, 1315 pc.local_time_zone_table[1].name)) 1316 { 1317 /* This locale uses the same abbrevation for standard and 1318 daylight times. So if we see that abbreviation, we don't 1319 know whether it's daylight time. */ 1320 pc.local_time_zone_table[0].value = -1; 1321 pc.local_time_zone_table[1].name = NULL; 1322 } 1323 1324 if (yyparse (&pc) != 0) 1325 goto fail; 1326 1327 if (pc.timespec_seen) 1328 *result = pc.seconds; 1329 else 1330 { 1331 if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen 1332 | (pc.local_zones_seen + pc.zones_seen))) 1333 goto fail; 1334 1335 tm.tm_year = to_year (pc.year) - TM_YEAR_BASE; 1336 tm.tm_mon = pc.month - 1; 1337 tm.tm_mday = pc.day; 1338 if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen)) 1339 { 1340 tm.tm_hour = to_hour (pc.hour, pc.meridian); 1341 if (tm.tm_hour < 0) 1342 goto fail; 1343 tm.tm_min = pc.minutes; 1344 tm.tm_sec = pc.seconds.tv_sec; 1345 } 1346 else 1347 { 1348 tm.tm_hour = tm.tm_min = tm.tm_sec = 0; 1349 pc.seconds.tv_nsec = 0; 1350 } 1351 1352 /* Let mktime deduce tm_isdst if we have an absolute time stamp. */ 1353 if (!pc.rels_seen) 1354 tm.tm_isdst = -1; 1355 1356 /* But if the input explicitly specifies local time with or without 1357 DST, give mktime that information. */ 1358 if (pc.local_zones_seen) 1359 tm.tm_isdst = pc.local_isdst; 1360 1361 tm0 = tm; 1362 1363 Start = mktime (&tm); 1364 1365 if (! mktime_ok (&tm0, &tm, Start)) 1366 { 1367 if (! pc.zones_seen) 1368 goto fail; 1369 else 1370 { 1371 /* Guard against falsely reporting errors near the time_t 1372 boundaries when parsing times in other time zones. For 1373 example, suppose the input string "1969-12-31 23:00:00 -0100", 1374 the current time zone is 8 hours ahead of UTC, and the min 1375 time_t value is 1970-01-01 00:00:00 UTC. Then the min 1376 localtime value is 1970-01-01 08:00:00, and mktime will 1377 therefore fail on 1969-12-31 23:00:00. To work around the 1378 problem, set the time zone to 1 hour behind UTC temporarily 1379 by setting TZ="XXX1:00" and try mktime again. */ 1380 1381 long int time_zone = pc.time_zone; 1382 long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone; 1383 long int abs_time_zone_hour = abs_time_zone / 60; 1384 int abs_time_zone_min = abs_time_zone % 60; 1385 char tz1buf[sizeof "XXX+0:00" 1386 + sizeof pc.time_zone * CHAR_BIT / 3]; 1387 if (!tz_was_altered) 1388 tz0 = get_tz (tz0buf); 1389 sprintf (tz1buf, "XXX%s%ld:%02d", "-" + (time_zone < 0), 1390 abs_time_zone_hour, abs_time_zone_min); 1391 if (setenv ("TZ", tz1buf, 1) != 0) 1392 goto fail; 1393 tz_was_altered = true; 1394 tm = tm0; 1395 Start = mktime (&tm); 1396 if (! mktime_ok (&tm0, &tm, Start)) 1397 goto fail; 1398 } 1399 } 1400 1401 if (pc.days_seen && ! pc.dates_seen) 1402 { 1403 tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7 1404 + 7 * (pc.day_ordinal - (0 < pc.day_ordinal))); 1405 tm.tm_isdst = -1; 1406 Start = mktime (&tm); 1407 if (Start == (time_t) -1) 1408 goto fail; 1409 } 1410 1411 if (pc.zones_seen) 1412 { 1413 long int delta = pc.time_zone * 60; 1414 time_t t1; 1415#ifdef HAVE_TM_GMTOFF 1416 delta -= tm.tm_gmtoff; 1417#else 1418 time_t t = Start; 1419 struct tm const *gmt = gmtime (&t); 1420 if (! gmt) 1421 goto fail; 1422 delta -= tm_diff (&tm, gmt); 1423#endif 1424 t1 = Start - delta; 1425 if ((Start < t1) != (delta < 0)) 1426 goto fail; /* time_t overflow */ 1427 Start = t1; 1428 } 1429 1430 /* Add relative date. */ 1431 if (pc.rel_year | pc.rel_month | pc.rel_day) 1432 { 1433 int year = tm.tm_year + pc.rel_year; 1434 int month = tm.tm_mon + pc.rel_month; 1435 int day = tm.tm_mday + pc.rel_day; 1436 if (((year < tm.tm_year) ^ (pc.rel_year < 0)) 1437 | ((month < tm.tm_mon) ^ (pc.rel_month < 0)) 1438 | ((day < tm.tm_mday) ^ (pc.rel_day < 0))) 1439 goto fail; 1440 tm.tm_year = year; 1441 tm.tm_mon = month; 1442 tm.tm_mday = day; 1443 Start = mktime (&tm); 1444 if (Start == (time_t) -1) 1445 goto fail; 1446 } 1447 1448 /* Add relative hours, minutes, and seconds. On hosts that support 1449 leap seconds, ignore the possibility of leap seconds; e.g., 1450 "+ 10 minutes" adds 600 seconds, even if one of them is a 1451 leap second. Typically this is not what the user wants, but it's 1452 too hard to do it the other way, because the time zone indicator 1453 must be applied before relative times, and if mktime is applied 1454 again the time zone will be lost. */ 1455 { 1456 long int sum_ns = pc.seconds.tv_nsec + pc.rel_ns; 1457 long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION; 1458 time_t t0 = Start; 1459 long int d1 = 60 * 60 * pc.rel_hour; 1460 time_t t1 = t0 + d1; 1461 long int d2 = 60 * pc.rel_minutes; 1462 time_t t2 = t1 + d2; 1463 long int d3 = pc.rel_seconds; 1464 time_t t3 = t2 + d3; 1465 long int d4 = (sum_ns - normalized_ns) / BILLION; 1466 time_t t4 = t3 + d4; 1467 1468 if ((d1 / (60 * 60) ^ pc.rel_hour) 1469 | (d2 / 60 ^ pc.rel_minutes) 1470 | ((t1 < t0) ^ (d1 < 0)) 1471 | ((t2 < t1) ^ (d2 < 0)) 1472 | ((t3 < t2) ^ (d3 < 0)) 1473 | ((t4 < t3) ^ (d4 < 0))) 1474 goto fail; 1475 1476 result->tv_sec = t4; 1477 result->tv_nsec = normalized_ns; 1478 } 1479 } 1480 1481 goto done; 1482 1483 fail: 1484 ok = false; 1485 done: 1486 if (tz_was_altered) 1487 ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0; 1488 if (tz0 != tz0buf) 1489 free (tz0); 1490 return ok; 1491} 1492 1493#if TEST 1494 1495int 1496main (int ac, char **av) 1497{ 1498 char buff[BUFSIZ]; 1499 1500 printf ("Enter date, or blank line to exit.\n\t> "); 1501 fflush (stdout); 1502 1503 buff[BUFSIZ - 1] = '\0'; 1504 while (fgets (buff, BUFSIZ - 1, stdin) && buff[0]) 1505 { 1506 struct timespec d; 1507 struct tm const *tm; 1508 if (! get_date (&d, buff, NULL)) 1509 printf ("Bad format - couldn't convert.\n"); 1510 else if (! (tm = localtime (&d.tv_sec))) 1511 { 1512 long int sec = d.tv_sec; 1513 printf ("localtime (%ld) failed\n", sec); 1514 } 1515 else 1516 { 1517 int ns = d.tv_nsec; 1518 printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n", 1519 tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday, 1520 tm->tm_hour, tm->tm_min, tm->tm_sec, ns); 1521 } 1522 printf ("\t> "); 1523 fflush (stdout); 1524 } 1525 return 0; 1526} 1527#endif /* TEST */ 1528