Update
[shishi.git] / gl / getdate.y
blobcbf3ca104aa877d3192335843f03cc3f723aa335
1 %{
2 /* Parse a string into an internal time stamp.
4 Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007 Free Software
5 Foundation, Inc.
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.
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.
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. */
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.
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. */
32 /* FIXME: Check for arithmetic overflow in all cases, not just
33 some of them. */
35 #include <config.h>
37 #include "getdate.h"
38 #include "timespec.h"
40 /* There's no need to extend the stack, so there's no need to involve
41 alloca. */
42 #define YYSTACK_USE_ALLOCA 0
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
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
61 #include <ctype.h>
62 #include <limits.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
67 #include "setenv.h"
68 #include "xalloc.h"
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)
80 #ifndef __attribute__
81 # if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
82 # define __attribute__(x)
83 # endif
84 #endif
86 #ifndef ATTRIBUTE_UNUSED
87 # define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
88 #endif
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.
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))
105 #define EPOCH_YEAR 1970
106 #define TM_YEAR_BASE 1900
108 #define HOUR(x) ((x) * 60)
110 /* An integer value, and the number of digits in its textual
111 representation. */
112 typedef struct
114 bool negative;
115 long int value;
116 size_t digits;
117 } textint;
119 /* An entry in the lexical lookup table. */
120 typedef struct
122 char const *name;
123 int type;
124 int value;
125 } table;
127 /* Meridian: am, pm, or 24-hour style. */
128 enum { MERam, MERpm, MER24 };
130 enum { BILLION = 1000000000, LOG10_BILLION = 9 };
132 /* Relative times. */
133 typedef struct
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;
145 #if HAVE_COMPOUND_LITERALS
146 # define RELATIVE_TIME_0 ((relative_time) { 0, 0, 0, 0, 0, 0, 0 })
147 #else
148 static relative_time const RELATIVE_TIME_0;
149 #endif
151 /* Information passed to and from the parser. */
152 typedef struct
154 /* The input string remaining to be parsed. */
155 const char *input;
157 /* N, if this is the Nth Tuesday. */
158 long int day_ordinal;
160 /* Day of week; Sunday is 0. */
161 int day_number;
163 /* tm_isdst flag for the local zone. */
164 int local_isdst;
166 /* Time zone, in minutes east of UTC. */
167 long int time_zone;
169 /* Style used for time. */
170 int meridian;
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 */
180 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
181 relative_time rel;
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;
193 /* Table of local time zone abbrevations, terminated by a null entry. */
194 table local_time_zone_table[3];
195 } parser_control;
197 union YYSTYPE;
198 static int yylex (union YYSTYPE *, parser_control *);
199 static int yyerror (parser_control const *, char const *);
200 static long int time_zone_hhmm (textint, long int);
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 }
210 /* This grammar has 20 shift/reduce conflicts. */
211 %expect 20
213 %union
215 long int intval;
216 textint textintval;
217 struct timespec timespec;
218 relative_time rel;
221 %token tAGO tDST
223 %token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT
224 %token <intval> tDAY_UNIT
226 %token <intval> tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN
227 %token <intval> tMONTH tORDINAL tZONE
229 %token <textintval> tSNUMBER tUNUMBER
230 %token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER
232 %type <intval> o_colon_minutes o_merid
233 %type <timespec> seconds signed_seconds unsigned_seconds
235 %type <rel> relunit relunit_snumber
239 spec:
240 timespec
241 | items
244 timespec:
245 '@' seconds
247 pc->seconds = $2;
248 pc->timespec_seen = true;
252 items:
253 /* empty */
254 | items item
257 item:
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
273 time:
274 tUNUMBER tMERIDIAN
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;
282 | tUNUMBER ':' tUNUMBER o_merid
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;
290 | tUNUMBER ':' tUNUMBER tSNUMBER o_colon_minutes
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);
300 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_merid
302 pc->hour = $1.value;
303 pc->minutes = $3.value;
304 pc->seconds = $5;
305 pc->meridian = $6;
307 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER o_colon_minutes
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);
318 local_zone:
319 tLOCAL_ZONE
321 pc->local_isdst = $1;
322 pc->dsts_seen += (0 < $1);
324 | tLOCAL_ZONE tDST
326 pc->local_isdst = 1;
327 pc->dsts_seen += (0 < $1) + 1;
331 zone:
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; }
352 day:
353 tDAY
355 pc->day_ordinal = 1;
356 pc->day_number = $1;
358 | tDAY ','
360 pc->day_ordinal = 1;
361 pc->day_number = $1;
363 | tORDINAL tDAY
365 pc->day_ordinal = $1;
366 pc->day_number = $2;
368 | tUNUMBER tDAY
370 pc->day_ordinal = $1.value;
371 pc->day_number = $2;
375 date:
376 tUNUMBER '/' tUNUMBER
378 pc->month = $1.value;
379 pc->day = $3.value;
381 | tUNUMBER '/' tUNUMBER '/' tUNUMBER
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)
390 pc->year = $1;
391 pc->month = $3.value;
392 pc->day = $5.value;
394 else
396 pc->month = $1.value;
397 pc->day = $3.value;
398 pc->year = $5;
401 | tUNUMBER tSNUMBER tSNUMBER
403 /* ISO 8601 format. YYYY-MM-DD. */
404 pc->year = $1;
405 pc->month = -$2.value;
406 pc->day = -$3.value;
408 | tUNUMBER tMONTH tSNUMBER
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;
416 | tMONTH tSNUMBER tSNUMBER
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;
424 | tMONTH tUNUMBER
426 pc->month = $1;
427 pc->day = $2.value;
429 | tMONTH tUNUMBER ',' tUNUMBER
431 pc->month = $1;
432 pc->day = $2.value;
433 pc->year = $4;
435 | tUNUMBER tMONTH
437 pc->day = $1.value;
438 pc->month = $2;
440 | tUNUMBER tMONTH tUNUMBER
442 pc->day = $1.value;
443 pc->month = $2;
444 pc->year = $3;
448 rel:
449 relunit tAGO
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;
459 | relunit
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;
471 relunit:
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
515 relunit_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; }
530 seconds: signed_seconds | unsigned_seconds;
532 signed_seconds:
533 tSDECIMAL_NUMBER
534 | tSNUMBER
535 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
538 unsigned_seconds:
539 tUDECIMAL_NUMBER
540 | tUNUMBER
541 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
544 number:
545 tUNUMBER
547 if (pc->dates_seen && ! pc->year.digits
548 && ! pc->rels_seen && (pc->times_seen || 2 < $1.digits))
549 pc->year = $1;
550 else
552 if (4 < $1.digits)
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;
560 else
562 pc->times_seen++;
563 if ($1.digits <= 2)
565 pc->hour = $1.value;
566 pc->minutes = 0;
568 else
570 pc->hour = $1.value / 100;
571 pc->minutes = $1.value % 100;
573 pc->seconds.tv_sec = 0;
574 pc->seconds.tv_nsec = 0;
575 pc->meridian = MER24;
581 o_colon_minutes:
582 /* empty */
583 { $$ = -1; }
584 | ':' tUNUMBER
585 { $$ = $2.value; }
588 o_merid:
589 /* empty */
590 { $$ = MER24; }
591 | tMERIDIAN
592 { $$ = $1; }
597 static table const meridian_table[] =
599 { "AM", tMERIDIAN, MERam },
600 { "A.M.", tMERIDIAN, MERam },
601 { "PM", tMERIDIAN, MERpm },
602 { "P.M.", tMERIDIAN, MERpm },
603 { NULL, 0, 0 }
606 static table const dst_table[] =
608 { "DST", tDST, 0 }
611 static table const month_and_day_table[] =
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 }
640 static table const time_units_table[] =
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 }
655 /* Assorted relative-time words. */
656 static table const relative_time_table[] =
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 }
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. */
684 static table const universal_time_zone_table[] =
686 { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
687 { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
688 { "UTC", tZONE, HOUR ( 0) },
689 { NULL, 0, 0 }
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. */
697 static table const time_zone_table[] =
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 }
749 /* Military time zone table. */
750 static table const military_table[] =
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 }
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. */
786 static long int
787 time_zone_hhmm (textint s, long int mm)
789 if (mm < 0)
790 return (s.value / 100) * 60 + s.value % 100;
791 else
792 return s.value * 60 + (s.negative ? -mm : mm);
795 static int
796 to_hour (long int hours, int meridian)
798 switch (meridian)
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;
810 static long int
811 to_year (textint textyear)
813 long int year = textyear.value;
815 if (year < 0)
816 year = -year;
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;
823 return year;
826 static table const *
827 lookup_zone (parser_control const *pc, char const *name)
829 table const *tp;
831 for (tp = universal_time_zone_table; tp->name; tp++)
832 if (strcmp (name, tp->name) == 0)
833 return tp;
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;
841 for (tp = time_zone_table; tp->name; tp++)
842 if (strcmp (name, tp->name) == 0)
843 return tp;
845 return NULL;
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. */
853 static long int
854 tm_diff (struct tm const *a, struct tm const *b)
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));
873 #endif /* ! HAVE_TM_GMTOFF */
875 static table const *
876 lookup_word (parser_control const *pc, char *word)
878 char *p;
879 char *q;
880 size_t wordlen;
881 table const *tp;
882 bool period_found;
883 bool abbrev;
885 /* Make it uppercase. */
886 for (p = word; *p; p++)
888 unsigned char ch = *p;
889 *p = toupper (ch);
892 for (tp = meridian_table; tp->name; tp++)
893 if (strcmp (word, tp->name) == 0)
894 return tp;
896 /* See if we have an abbreviation for a month. */
897 wordlen = strlen (word);
898 abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
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;
904 if ((tp = lookup_zone (pc, word)))
905 return tp;
907 if (strcmp (word, dst_table[0].name) == 0)
908 return dst_table;
910 for (tp = time_units_table; tp->name; tp++)
911 if (strcmp (word, tp->name) == 0)
912 return tp;
914 /* Strip off any plural and try the units table again. */
915 if (word[wordlen - 1] == 'S')
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. */
924 for (tp = relative_time_table; tp->name; tp++)
925 if (strcmp (word, tp->name) == 0)
926 return tp;
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;
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;
943 return NULL;
946 static int
947 yylex (YYSTYPE *lvalp, parser_control *pc)
949 unsigned char c;
950 size_t count;
952 for (;;)
954 while (c = *pc->input, isspace (c))
955 pc->input++;
957 if (ISDIGIT (c) || c == '-' || c == '+')
959 char const *p;
960 int sign;
961 unsigned long int value;
962 if (c == '-' || c == '+')
964 sign = c == '-' ? -1 : 1;
965 while (c = *++pc->input, isspace (c))
966 continue;
967 if (! ISDIGIT (c))
968 /* skip the '-' sign */
969 continue;
971 else
972 sign = 0;
973 p = pc->input;
974 for (value = 0; ; value *= 10)
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 '?';
986 if ((c == '.' || c == ',') && ISDIGIT (p[1]))
988 time_t s;
989 int ns;
990 int digits;
991 unsigned long int value1;
993 /* Check for overflow when converting value to time_t. */
994 if (sign < 0)
996 s = - value;
997 if (0 < s)
998 return '?';
999 value1 = -s;
1001 else
1003 s = value;
1004 if (s < 0)
1005 return '?';
1006 value1 = s;
1008 if (value != value1)
1009 return '?';
1011 /* Accumulate fraction, to ns precision. */
1012 p++;
1013 ns = *p++ - '0';
1014 for (digits = 2; digits <= LOG10_BILLION; digits++)
1016 ns *= 10;
1017 if (ISDIGIT (*p))
1018 ns += *p++ - '0';
1021 /* Skip excess digits, truncating toward -Infinity. */
1022 if (sign < 0)
1023 for (; ISDIGIT (*p); p++)
1024 if (*p != '0')
1026 ns++;
1027 break;
1029 while (ISDIGIT (*p))
1030 p++;
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)
1037 s--;
1038 if (! (s < 0))
1039 return '?';
1040 ns = BILLION - ns;
1043 lvalp->timespec.tv_sec = s;
1044 lvalp->timespec.tv_nsec = ns;
1045 pc->input = p;
1046 return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1048 else
1050 lvalp->textintval.negative = sign < 0;
1051 if (sign < 0)
1053 lvalp->textintval.value = - value;
1054 if (0 < lvalp->textintval.value)
1055 return '?';
1057 else
1059 lvalp->textintval.value = value;
1060 if (lvalp->textintval.value < 0)
1061 return '?';
1063 lvalp->textintval.digits = p - pc->input;
1064 pc->input = p;
1065 return sign ? tSNUMBER : tUNUMBER;
1069 if (isalpha (c))
1071 char buff[20];
1072 char *p = buff;
1073 table const *tp;
1077 if (p < buff + sizeof buff - 1)
1078 *p++ = c;
1079 c = *++pc->input;
1081 while (isalpha (c) || c == '.');
1083 *p = '\0';
1084 tp = lookup_word (pc, buff);
1085 if (! tp)
1086 return '?';
1087 lvalp->intval = tp->value;
1088 return tp->type;
1091 if (c != '(')
1092 return *pc->input++;
1093 count = 0;
1096 c = *pc->input++;
1097 if (c == '\0')
1098 return c;
1099 if (c == '(')
1100 count++;
1101 else if (c == ')')
1102 count--;
1104 while (count != 0);
1108 /* Do nothing if the parser reports an error. */
1109 static int
1110 yyerror (parser_control const *pc ATTRIBUTE_UNUSED,
1111 char const *s ATTRIBUTE_UNUSED)
1113 return 0;
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. */
1120 static bool
1121 mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
1123 if (t == (time_t) -1)
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;
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));
1141 /* A reasonable upper bound for the size of ordinary TZ strings.
1142 Use heap allocation if TZ's length exceeds this. */
1143 enum { TZBUFSIZE = 100 };
1145 /* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
1146 otherwise. */
1147 static char *
1148 get_tz (char tzbuf[TZBUFSIZE])
1150 char *tz = getenv ("TZ");
1151 if (tz)
1153 size_t tzsize = strlen (tz) + 1;
1154 tz = (tzsize <= TZBUFSIZE
1155 ? memcpy (tzbuf, tz, tzsize)
1156 : xmemdup (tz, tzsize));
1158 return tz;
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. */
1165 bool
1166 get_date (struct timespec *result, char const *p, struct timespec const *now)
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;
1181 if (! now)
1183 gettime (&gettime_buffer);
1184 now = &gettime_buffer;
1187 Start = now->tv_sec;
1188 Start_ns = now->tv_nsec;
1190 tmp = localtime (&now->tv_sec);
1191 if (! tmp)
1192 return false;
1194 while (c = *p, isspace (c))
1195 p++;
1197 if (strncmp (p, "TZ=\"", 4) == 0)
1199 char const *tzbase = p + 4;
1200 size_t tzsize = 1;
1201 char const *s;
1203 for (s = tzbase; *s; s++, tzsize++)
1204 if (*s == '\\')
1206 s++;
1207 if (! (*s == '\\' || *s == '"'))
1208 break;
1210 else if (*s == '"')
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;
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;
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;
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;
1261 /* Probe the names used in the next three calendar quarters, looking
1262 for a tm_isdst different from the one we already have. */
1264 int quarter;
1265 for (quarter = 1; quarter <= 3; quarter++)
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)
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;
1278 break;
1282 #else
1283 #if HAVE_TZNAME
1285 # ifndef tzname
1286 extern char *tzname[];
1287 # endif
1288 int i;
1289 for (i = 0; i < 2; i++)
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;
1295 pc.local_time_zone_table[i].name = NULL;
1297 #else
1298 pc.local_time_zone_table[0].name = NULL;
1299 #endif
1300 #endif
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))
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;
1313 if (yyparse (&pc) != 0)
1314 goto fail;
1316 if (pc.timespec_seen)
1317 *result = pc.seconds;
1318 else
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;
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))
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;
1335 else
1337 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1338 pc.seconds.tv_nsec = 0;
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;
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;
1350 tm0 = tm;
1352 Start = mktime (&tm);
1354 if (! mktime_ok (&tm0, &tm, Start))
1356 if (! pc.zones_seen)
1357 goto fail;
1358 else
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. */
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;
1390 if (pc.days_seen && ! pc.dates_seen)
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;
1400 if (pc.zones_seen)
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;
1419 /* Add relative date. */
1420 if (pc.rel.year | pc.rel.month | pc.rel.day)
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;
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. */
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;
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;
1469 result->tv_sec = t4;
1470 result->tv_nsec = normalized_ns;
1474 goto done;
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;
1486 #if TEST
1489 main (int ac, char **av)
1491 char buff[BUFSIZ];
1493 printf ("Enter date, or blank line to exit.\n\t> ");
1494 fflush (stdout);
1496 buff[BUFSIZ - 1] = '\0';
1497 while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
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)))
1505 long int sec = d.tv_sec;
1506 printf ("localtime (%ld) failed\n", sec);
1508 else
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);
1515 printf ("\t> ");
1516 fflush (stdout);
1518 return 0;
1520 #endif /* TEST */