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