add ext4,vfat and tar.bz2
[u-tools.git] / u-tools / apps / tar / gnu / parse-datetime.y
blobd77955f40d5dec92b7c8e04d829c06529156f6b3
1 %{
2 /* Parse a string into an internal time stamp.
4 Copyright (C) 1999-2000, 2002-2011 Free Software Foundation, Inc.
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 /* Originally written by Steven M. Bellovin <smb@research.att.com> while
20 at the University of North Carolina at Chapel Hill. Later tweaked by
21 a couple of people on Usenet. Completely overhauled by Rich $alz
22 <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990.
24 Modified by Paul Eggert <eggert@twinsun.com> in August 1999 to do
25 the right thing about local DST. Also modified by Paul Eggert
26 <eggert@cs.ucla.edu> in February 2004 to support
27 nanosecond-resolution time stamps, and in October 2004 to support
28 TZ strings in dates. */
30 /* FIXME: Check for arithmetic overflow in all cases, not just
31 some of them. */
33 #include <config.h>
35 #include "parse-datetime.h"
37 #include "intprops.h"
38 #include "timespec.h"
39 #include "verify.h"
41 /* There's no need to extend the stack, so there's no need to involve
42 alloca. */
43 #define YYSTACK_USE_ALLOCA 0
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
52 /* Since the code of parse-datetime.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
62 #include <c-ctype.h>
63 #include <limits.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
68 #include "xalloc.h"
70 /* Bison's skeleton tests _STDLIB_H, while some stdlib.h headers
71 use _STDLIB_H_ as witness. Map the latter to the one bison uses. */
72 /* FIXME: this is temporary. Remove when we have a mechanism to ensure
73 that the version we're using is fixed, too. */
74 #ifdef _STDLIB_H_
75 # undef _STDLIB_H
76 # define _STDLIB_H 1
77 #endif
79 /* ISDIGIT differs from isdigit, as follows:
80 - Its arg may be any int or unsigned int; it need not be an unsigned char
81 or EOF.
82 - It's typically faster.
83 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
84 isdigit unless it's important to use the locale's definition
85 of `digit' even when the host does not conform to POSIX. */
86 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
88 /* Shift A right by B bits portably, by dividing A by 2**B and
89 truncating towards minus infinity. A and B should be free of side
90 effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
91 INT_BITS is the number of useful bits in an int. GNU code can
92 assume that INT_BITS is at least 32.
94 ISO C99 says that A >> B is implementation-defined if A < 0. Some
95 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
96 right in the usual way when A < 0, so SHR falls back on division if
97 ordinary A >> B doesn't seem to be the usual signed shift. */
98 #define SHR(a, b) \
99 (-1 >> 1 == -1 \
100 ? (a) >> (b) \
101 : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
103 #define EPOCH_YEAR 1970
104 #define TM_YEAR_BASE 1900
106 #define HOUR(x) ((x) * 60)
108 /* long_time_t is a signed integer type that contains all time_t values. */
109 verify (TYPE_IS_INTEGER (time_t));
110 #if TIME_T_FITS_IN_LONG_INT
111 typedef long int long_time_t;
112 #else
113 typedef time_t long_time_t;
114 #endif
116 /* Lots of this code assumes time_t and time_t-like values fit into
117 long_time_t. */
118 verify (TYPE_MINIMUM (long_time_t) <= TYPE_MINIMUM (time_t)
119 && TYPE_MAXIMUM (time_t) <= TYPE_MAXIMUM (long_time_t));
121 /* FIXME: It also assumes that signed integer overflow silently wraps around,
122 but this is not true any more with recent versions of GCC 4. */
124 /* An integer value, and the number of digits in its textual
125 representation. */
126 typedef struct
128 bool negative;
129 long int value;
130 size_t digits;
131 } textint;
133 /* An entry in the lexical lookup table. */
134 typedef struct
136 char const *name;
137 int type;
138 int value;
139 } table;
141 /* Meridian: am, pm, or 24-hour style. */
142 enum { MERam, MERpm, MER24 };
144 enum { BILLION = 1000000000, LOG10_BILLION = 9 };
146 /* Relative times. */
147 typedef struct
149 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
150 long int year;
151 long int month;
152 long int day;
153 long int hour;
154 long int minutes;
155 long_time_t seconds;
156 long int ns;
157 } relative_time;
159 #if HAVE_COMPOUND_LITERALS
160 # define RELATIVE_TIME_0 ((relative_time) { 0, 0, 0, 0, 0, 0, 0 })
161 #else
162 static relative_time const RELATIVE_TIME_0;
163 #endif
165 /* Information passed to and from the parser. */
166 typedef struct
168 /* The input string remaining to be parsed. */
169 const char *input;
171 /* N, if this is the Nth Tuesday. */
172 long int day_ordinal;
174 /* Day of week; Sunday is 0. */
175 int day_number;
177 /* tm_isdst flag for the local zone. */
178 int local_isdst;
180 /* Time zone, in minutes east of UTC. */
181 long int time_zone;
183 /* Style used for time. */
184 int meridian;
186 /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds. */
187 textint year;
188 long int month;
189 long int day;
190 long int hour;
191 long int minutes;
192 struct timespec seconds; /* includes nanoseconds */
194 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
195 relative_time rel;
197 /* Presence or counts of nonterminals of various flavors parsed so far. */
198 bool timespec_seen;
199 bool rels_seen;
200 size_t dates_seen;
201 size_t days_seen;
202 size_t local_zones_seen;
203 size_t dsts_seen;
204 size_t times_seen;
205 size_t zones_seen;
207 /* Table of local time zone abbrevations, terminated by a null entry. */
208 table local_time_zone_table[3];
209 } parser_control;
211 union YYSTYPE;
212 static int yylex (union YYSTYPE *, parser_control *);
213 static int yyerror (parser_control const *, char const *);
214 static long int time_zone_hhmm (parser_control *, textint, long int);
216 /* Extract into *PC any date and time info from a string of digits
217 of the form e.g., YYYYMMDD, YYMMDD, HHMM, HH (and sometimes YYY,
218 YYYY, ...). */
219 static void
220 digits_to_date_time (parser_control *pc, textint text_int)
222 if (pc->dates_seen && ! pc->year.digits
223 && ! pc->rels_seen && (pc->times_seen || 2 < text_int.digits))
224 pc->year = text_int;
225 else
227 if (4 < text_int.digits)
229 pc->dates_seen++;
230 pc->day = text_int.value % 100;
231 pc->month = (text_int.value / 100) % 100;
232 pc->year.value = text_int.value / 10000;
233 pc->year.digits = text_int.digits - 4;
235 else
237 pc->times_seen++;
238 if (text_int.digits <= 2)
240 pc->hour = text_int.value;
241 pc->minutes = 0;
243 else
245 pc->hour = text_int.value / 100;
246 pc->minutes = text_int.value % 100;
248 pc->seconds.tv_sec = 0;
249 pc->seconds.tv_nsec = 0;
250 pc->meridian = MER24;
255 /* Increment PC->rel by FACTOR * REL (FACTOR is 1 or -1). */
256 static void
257 apply_relative_time (parser_control *pc, relative_time rel, int factor)
259 pc->rel.ns += factor * rel.ns;
260 pc->rel.seconds += factor * rel.seconds;
261 pc->rel.minutes += factor * rel.minutes;
262 pc->rel.hour += factor * rel.hour;
263 pc->rel.day += factor * rel.day;
264 pc->rel.month += factor * rel.month;
265 pc->rel.year += factor * rel.year;
266 pc->rels_seen = true;
269 /* Set PC-> hour, minutes, seconds and nanoseconds members from arguments. */
270 static void
271 set_hhmmss (parser_control *pc, long int hour, long int minutes,
272 time_t sec, long int nsec)
274 pc->hour = hour;
275 pc->minutes = minutes;
276 pc->seconds.tv_sec = sec;
277 pc->seconds.tv_nsec = nsec;
282 /* We want a reentrant parser, even if the TZ manipulation and the calls to
283 localtime and gmtime are not reentrant. */
284 %pure-parser
285 %parse-param { parser_control *pc }
286 %lex-param { parser_control *pc }
288 /* This grammar has 20 shift/reduce conflicts. */
289 %expect 20
291 %union
293 long int intval;
294 textint textintval;
295 struct timespec timespec;
296 relative_time rel;
299 %token tAGO tDST
301 %token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT
302 %token <intval> tDAY_UNIT tDAY_SHIFT
304 %token <intval> tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN
305 %token <intval> tMONTH tORDINAL tZONE
307 %token <textintval> tSNUMBER tUNUMBER
308 %token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER
310 %type <intval> o_colon_minutes o_merid
311 %type <timespec> seconds signed_seconds unsigned_seconds
313 %type <rel> relunit relunit_snumber dayshift
317 spec:
318 timespec
319 | items
322 timespec:
323 '@' seconds
325 pc->seconds = $2;
326 pc->timespec_seen = true;
330 items:
331 /* empty */
332 | items item
335 item:
336 time
337 { pc->times_seen++; }
338 | local_zone
339 { pc->local_zones_seen++; }
340 | zone
341 { pc->zones_seen++; }
342 | date
343 { pc->dates_seen++; }
344 | day
345 { pc->days_seen++; }
346 | rel
347 | number
348 | hybrid
351 time:
352 tUNUMBER tMERIDIAN
354 set_hhmmss (pc, $1.value, 0, 0, 0);
355 pc->meridian = $2;
357 | tUNUMBER ':' tUNUMBER o_merid
359 set_hhmmss (pc, $1.value, $3.value, 0, 0);
360 pc->meridian = $4;
362 | tUNUMBER ':' tUNUMBER tSNUMBER o_colon_minutes
364 set_hhmmss (pc, $1.value, $3.value, 0, 0);
365 pc->meridian = MER24;
366 pc->zones_seen++;
367 pc->time_zone = time_zone_hhmm (pc, $4, $5);
369 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_merid
371 set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
372 pc->meridian = $6;
374 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER o_colon_minutes
376 set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
377 pc->meridian = MER24;
378 pc->zones_seen++;
379 pc->time_zone = time_zone_hhmm (pc, $6, $7);
383 local_zone:
384 tLOCAL_ZONE
386 pc->local_isdst = $1;
387 pc->dsts_seen += (0 < $1);
389 | tLOCAL_ZONE tDST
391 pc->local_isdst = 1;
392 pc->dsts_seen += (0 < $1) + 1;
396 zone:
397 tZONE
398 { pc->time_zone = $1; }
399 | tZONE relunit_snumber
400 { pc->time_zone = $1;
401 apply_relative_time (pc, $2, 1); }
402 | tZONE tSNUMBER o_colon_minutes
403 { pc->time_zone = $1 + time_zone_hhmm (pc, $2, $3); }
404 | tDAYZONE
405 { pc->time_zone = $1 + 60; }
406 | tZONE tDST
407 { pc->time_zone = $1 + 60; }
410 day:
411 tDAY
413 pc->day_ordinal = 0;
414 pc->day_number = $1;
416 | tDAY ','
418 pc->day_ordinal = 0;
419 pc->day_number = $1;
421 | tORDINAL tDAY
423 pc->day_ordinal = $1;
424 pc->day_number = $2;
426 | tUNUMBER tDAY
428 pc->day_ordinal = $1.value;
429 pc->day_number = $2;
433 date:
434 tUNUMBER '/' tUNUMBER
436 pc->month = $1.value;
437 pc->day = $3.value;
439 | tUNUMBER '/' tUNUMBER '/' tUNUMBER
441 /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
442 otherwise as MM/DD/YY.
443 The goal in recognizing YYYY/MM/DD is solely to support legacy
444 machine-generated dates like those in an RCS log listing. If
445 you want portability, use the ISO 8601 format. */
446 if (4 <= $1.digits)
448 pc->year = $1;
449 pc->month = $3.value;
450 pc->day = $5.value;
452 else
454 pc->month = $1.value;
455 pc->day = $3.value;
456 pc->year = $5;
459 | tUNUMBER tSNUMBER tSNUMBER
461 /* ISO 8601 format. YYYY-MM-DD. */
462 pc->year = $1;
463 pc->month = -$2.value;
464 pc->day = -$3.value;
466 | tUNUMBER tMONTH tSNUMBER
468 /* e.g. 17-JUN-1992. */
469 pc->day = $1.value;
470 pc->month = $2;
471 pc->year.value = -$3.value;
472 pc->year.digits = $3.digits;
474 | tMONTH tSNUMBER tSNUMBER
476 /* e.g. JUN-17-1992. */
477 pc->month = $1;
478 pc->day = -$2.value;
479 pc->year.value = -$3.value;
480 pc->year.digits = $3.digits;
482 | tMONTH tUNUMBER
484 pc->month = $1;
485 pc->day = $2.value;
487 | tMONTH tUNUMBER ',' tUNUMBER
489 pc->month = $1;
490 pc->day = $2.value;
491 pc->year = $4;
493 | tUNUMBER tMONTH
495 pc->day = $1.value;
496 pc->month = $2;
498 | tUNUMBER tMONTH tUNUMBER
500 pc->day = $1.value;
501 pc->month = $2;
502 pc->year = $3;
506 rel:
507 relunit tAGO
508 { apply_relative_time (pc, $1, -1); }
509 | relunit
510 { apply_relative_time (pc, $1, 1); }
511 | dayshift
512 { apply_relative_time (pc, $1, 1); }
515 relunit:
516 tORDINAL tYEAR_UNIT
517 { $$ = RELATIVE_TIME_0; $$.year = $1; }
518 | tUNUMBER tYEAR_UNIT
519 { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
520 | tYEAR_UNIT
521 { $$ = RELATIVE_TIME_0; $$.year = 1; }
522 | tORDINAL tMONTH_UNIT
523 { $$ = RELATIVE_TIME_0; $$.month = $1; }
524 | tUNUMBER tMONTH_UNIT
525 { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
526 | tMONTH_UNIT
527 { $$ = RELATIVE_TIME_0; $$.month = 1; }
528 | tORDINAL tDAY_UNIT
529 { $$ = RELATIVE_TIME_0; $$.day = $1 * $2; }
530 | tUNUMBER tDAY_UNIT
531 { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
532 | tDAY_UNIT
533 { $$ = RELATIVE_TIME_0; $$.day = $1; }
534 | tORDINAL tHOUR_UNIT
535 { $$ = RELATIVE_TIME_0; $$.hour = $1; }
536 | tUNUMBER tHOUR_UNIT
537 { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
538 | tHOUR_UNIT
539 { $$ = RELATIVE_TIME_0; $$.hour = 1; }
540 | tORDINAL tMINUTE_UNIT
541 { $$ = RELATIVE_TIME_0; $$.minutes = $1; }
542 | tUNUMBER tMINUTE_UNIT
543 { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
544 | tMINUTE_UNIT
545 { $$ = RELATIVE_TIME_0; $$.minutes = 1; }
546 | tORDINAL tSEC_UNIT
547 { $$ = RELATIVE_TIME_0; $$.seconds = $1; }
548 | tUNUMBER tSEC_UNIT
549 { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
550 | tSDECIMAL_NUMBER tSEC_UNIT
551 { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
552 | tUDECIMAL_NUMBER tSEC_UNIT
553 { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
554 | tSEC_UNIT
555 { $$ = RELATIVE_TIME_0; $$.seconds = 1; }
556 | relunit_snumber
559 relunit_snumber:
560 tSNUMBER tYEAR_UNIT
561 { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
562 | tSNUMBER tMONTH_UNIT
563 { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
564 | tSNUMBER tDAY_UNIT
565 { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
566 | tSNUMBER tHOUR_UNIT
567 { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
568 | tSNUMBER tMINUTE_UNIT
569 { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
570 | tSNUMBER tSEC_UNIT
571 { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
574 dayshift:
575 tDAY_SHIFT
576 { $$ = RELATIVE_TIME_0; $$.day = $1; }
579 seconds: signed_seconds | unsigned_seconds;
581 signed_seconds:
582 tSDECIMAL_NUMBER
583 | tSNUMBER
584 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
587 unsigned_seconds:
588 tUDECIMAL_NUMBER
589 | tUNUMBER
590 { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
593 number:
594 tUNUMBER
595 { digits_to_date_time (pc, $1); }
598 hybrid:
599 tUNUMBER relunit_snumber
601 /* Hybrid all-digit and relative offset, so that we accept e.g.,
602 "YYYYMMDD +N days" as well as "YYYYMMDD N days". */
603 digits_to_date_time (pc, $1);
604 apply_relative_time (pc, $2, 1);
608 o_colon_minutes:
609 /* empty */
610 { $$ = -1; }
611 | ':' tUNUMBER
612 { $$ = $2.value; }
615 o_merid:
616 /* empty */
617 { $$ = MER24; }
618 | tMERIDIAN
619 { $$ = $1; }
624 static table const meridian_table[] =
626 { "AM", tMERIDIAN, MERam },
627 { "A.M.", tMERIDIAN, MERam },
628 { "PM", tMERIDIAN, MERpm },
629 { "P.M.", tMERIDIAN, MERpm },
630 { NULL, 0, 0 }
633 static table const dst_table[] =
635 { "DST", tDST, 0 }
638 static table const month_and_day_table[] =
640 { "JANUARY", tMONTH, 1 },
641 { "FEBRUARY", tMONTH, 2 },
642 { "MARCH", tMONTH, 3 },
643 { "APRIL", tMONTH, 4 },
644 { "MAY", tMONTH, 5 },
645 { "JUNE", tMONTH, 6 },
646 { "JULY", tMONTH, 7 },
647 { "AUGUST", tMONTH, 8 },
648 { "SEPTEMBER",tMONTH, 9 },
649 { "SEPT", tMONTH, 9 },
650 { "OCTOBER", tMONTH, 10 },
651 { "NOVEMBER", tMONTH, 11 },
652 { "DECEMBER", tMONTH, 12 },
653 { "SUNDAY", tDAY, 0 },
654 { "MONDAY", tDAY, 1 },
655 { "TUESDAY", tDAY, 2 },
656 { "TUES", tDAY, 2 },
657 { "WEDNESDAY",tDAY, 3 },
658 { "WEDNES", tDAY, 3 },
659 { "THURSDAY", tDAY, 4 },
660 { "THUR", tDAY, 4 },
661 { "THURS", tDAY, 4 },
662 { "FRIDAY", tDAY, 5 },
663 { "SATURDAY", tDAY, 6 },
664 { NULL, 0, 0 }
667 static table const time_units_table[] =
669 { "YEAR", tYEAR_UNIT, 1 },
670 { "MONTH", tMONTH_UNIT, 1 },
671 { "FORTNIGHT",tDAY_UNIT, 14 },
672 { "WEEK", tDAY_UNIT, 7 },
673 { "DAY", tDAY_UNIT, 1 },
674 { "HOUR", tHOUR_UNIT, 1 },
675 { "MINUTE", tMINUTE_UNIT, 1 },
676 { "MIN", tMINUTE_UNIT, 1 },
677 { "SECOND", tSEC_UNIT, 1 },
678 { "SEC", tSEC_UNIT, 1 },
679 { NULL, 0, 0 }
682 /* Assorted relative-time words. */
683 static table const relative_time_table[] =
685 { "TOMORROW", tDAY_SHIFT, 1 },
686 { "YESTERDAY",tDAY_SHIFT, -1 },
687 { "TODAY", tDAY_SHIFT, 0 },
688 { "NOW", tDAY_SHIFT, 0 },
689 { "LAST", tORDINAL, -1 },
690 { "THIS", tORDINAL, 0 },
691 { "NEXT", tORDINAL, 1 },
692 { "FIRST", tORDINAL, 1 },
693 /*{ "SECOND", tORDINAL, 2 }, */
694 { "THIRD", tORDINAL, 3 },
695 { "FOURTH", tORDINAL, 4 },
696 { "FIFTH", tORDINAL, 5 },
697 { "SIXTH", tORDINAL, 6 },
698 { "SEVENTH", tORDINAL, 7 },
699 { "EIGHTH", tORDINAL, 8 },
700 { "NINTH", tORDINAL, 9 },
701 { "TENTH", tORDINAL, 10 },
702 { "ELEVENTH", tORDINAL, 11 },
703 { "TWELFTH", tORDINAL, 12 },
704 { "AGO", tAGO, 1 },
705 { NULL, 0, 0 }
708 /* The universal time zone table. These labels can be used even for
709 time stamps that would not otherwise be valid, e.g., GMT time
710 stamps in London during summer. */
711 static table const universal_time_zone_table[] =
713 { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
714 { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
715 { "UTC", tZONE, HOUR ( 0) },
716 { NULL, 0, 0 }
719 /* The time zone table. This table is necessarily incomplete, as time
720 zone abbreviations are ambiguous; e.g. Australians interpret "EST"
721 as Eastern time in Australia, not as US Eastern Standard Time.
722 You cannot rely on parse_datetime to handle arbitrary time zone
723 abbreviations; use numeric abbreviations like `-0500' instead. */
724 static table const time_zone_table[] =
726 { "WET", tZONE, HOUR ( 0) }, /* Western European */
727 { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
728 { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
729 { "ART", tZONE, -HOUR ( 3) }, /* Argentina */
730 { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
731 { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
732 { "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */
733 { "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */
734 { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
735 { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
736 { "CLT", tZONE, -HOUR ( 4) }, /* Chile */
737 { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
738 { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
739 { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
740 { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
741 { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
742 { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
743 { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
744 { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
745 { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
746 { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
747 { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
748 { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
749 { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
750 { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
751 { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
752 { "WAT", tZONE, HOUR ( 1) }, /* West Africa */
753 { "CET", tZONE, HOUR ( 1) }, /* Central European */
754 { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
755 { "MET", tZONE, HOUR ( 1) }, /* Middle European */
756 { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
757 { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
758 { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
759 { "EET", tZONE, HOUR ( 2) }, /* Eastern European */
760 { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
761 { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
762 { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
763 { "EAT", tZONE, HOUR ( 3) }, /* East Africa */
764 { "MSK", tZONE, HOUR ( 3) }, /* Moscow */
765 { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
766 { "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */
767 { "SGT", tZONE, HOUR ( 8) }, /* Singapore */
768 { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
769 { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
770 { "GST", tZONE, HOUR (10) }, /* Guam Standard */
771 { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
772 { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
773 { NULL, 0, 0 }
776 /* Military time zone table. */
777 static table const military_table[] =
779 { "A", tZONE, -HOUR ( 1) },
780 { "B", tZONE, -HOUR ( 2) },
781 { "C", tZONE, -HOUR ( 3) },
782 { "D", tZONE, -HOUR ( 4) },
783 { "E", tZONE, -HOUR ( 5) },
784 { "F", tZONE, -HOUR ( 6) },
785 { "G", tZONE, -HOUR ( 7) },
786 { "H", tZONE, -HOUR ( 8) },
787 { "I", tZONE, -HOUR ( 9) },
788 { "K", tZONE, -HOUR (10) },
789 { "L", tZONE, -HOUR (11) },
790 { "M", tZONE, -HOUR (12) },
791 { "N", tZONE, HOUR ( 1) },
792 { "O", tZONE, HOUR ( 2) },
793 { "P", tZONE, HOUR ( 3) },
794 { "Q", tZONE, HOUR ( 4) },
795 { "R", tZONE, HOUR ( 5) },
796 { "S", tZONE, HOUR ( 6) },
797 { "T", tZONE, HOUR ( 7) },
798 { "U", tZONE, HOUR ( 8) },
799 { "V", tZONE, HOUR ( 9) },
800 { "W", tZONE, HOUR (10) },
801 { "X", tZONE, HOUR (11) },
802 { "Y", tZONE, HOUR (12) },
803 { "Z", tZONE, HOUR ( 0) },
804 { NULL, 0, 0 }
809 /* Convert a time zone expressed as HH:MM into an integer count of
810 minutes. If MM is negative, then S is of the form HHMM and needs
811 to be picked apart; otherwise, S is of the form HH. As specified in
812 http://www.opengroup.org/susv3xbd/xbd_chap08.html#tag_08_03, allow
813 only valid TZ range, and consider first two digits as hours, if no
814 minutes specified. */
816 static long int
817 time_zone_hhmm (parser_control *pc, textint s, long int mm)
819 long int n_minutes;
821 /* If the length of S is 1 or 2 and no minutes are specified,
822 interpret it as a number of hours. */
823 if (s.digits <= 2 && mm < 0)
824 s.value *= 100;
826 if (mm < 0)
827 n_minutes = (s.value / 100) * 60 + s.value % 100;
828 else
829 n_minutes = s.value * 60 + (s.negative ? -mm : mm);
831 /* If the absolute number of minutes is larger than 24 hours,
832 arrange to reject it by incrementing pc->zones_seen. Thus,
833 we allow only values in the range UTC-24:00 to UTC+24:00. */
834 if (24 * 60 < abs (n_minutes))
835 pc->zones_seen++;
837 return n_minutes;
840 static int
841 to_hour (long int hours, int meridian)
843 switch (meridian)
845 default: /* Pacify GCC. */
846 case MER24:
847 return 0 <= hours && hours < 24 ? hours : -1;
848 case MERam:
849 return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
850 case MERpm:
851 return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
855 static long int
856 to_year (textint textyear)
858 long int year = textyear.value;
860 if (year < 0)
861 year = -year;
863 /* XPG4 suggests that years 00-68 map to 2000-2068, and
864 years 69-99 map to 1969-1999. */
865 else if (textyear.digits == 2)
866 year += year < 69 ? 2000 : 1900;
868 return year;
871 static table const *
872 lookup_zone (parser_control const *pc, char const *name)
874 table const *tp;
876 for (tp = universal_time_zone_table; tp->name; tp++)
877 if (strcmp (name, tp->name) == 0)
878 return tp;
880 /* Try local zone abbreviations before those in time_zone_table, as
881 the local ones are more likely to be right. */
882 for (tp = pc->local_time_zone_table; tp->name; tp++)
883 if (strcmp (name, tp->name) == 0)
884 return tp;
886 for (tp = time_zone_table; tp->name; tp++)
887 if (strcmp (name, tp->name) == 0)
888 return tp;
890 return NULL;
893 #if ! HAVE_TM_GMTOFF
894 /* Yield the difference between *A and *B,
895 measured in seconds, ignoring leap seconds.
896 The body of this function is taken directly from the GNU C Library;
897 see src/strftime.c. */
898 static long int
899 tm_diff (struct tm const *a, struct tm const *b)
901 /* Compute intervening leap days correctly even if year is negative.
902 Take care to avoid int overflow in leap day calculations. */
903 int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
904 int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
905 int a100 = a4 / 25 - (a4 % 25 < 0);
906 int b100 = b4 / 25 - (b4 % 25 < 0);
907 int a400 = SHR (a100, 2);
908 int b400 = SHR (b100, 2);
909 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
910 long int ayear = a->tm_year;
911 long int years = ayear - b->tm_year;
912 long int days = (365 * years + intervening_leap_days
913 + (a->tm_yday - b->tm_yday));
914 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
915 + (a->tm_min - b->tm_min))
916 + (a->tm_sec - b->tm_sec));
918 #endif /* ! HAVE_TM_GMTOFF */
920 static table const *
921 lookup_word (parser_control const *pc, char *word)
923 char *p;
924 char *q;
925 size_t wordlen;
926 table const *tp;
927 bool period_found;
928 bool abbrev;
930 /* Make it uppercase. */
931 for (p = word; *p; p++)
933 unsigned char ch = *p;
934 *p = c_toupper (ch);
937 for (tp = meridian_table; tp->name; tp++)
938 if (strcmp (word, tp->name) == 0)
939 return tp;
941 /* See if we have an abbreviation for a month. */
942 wordlen = strlen (word);
943 abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
945 for (tp = month_and_day_table; tp->name; tp++)
946 if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
947 return tp;
949 if ((tp = lookup_zone (pc, word)))
950 return tp;
952 if (strcmp (word, dst_table[0].name) == 0)
953 return dst_table;
955 for (tp = time_units_table; tp->name; tp++)
956 if (strcmp (word, tp->name) == 0)
957 return tp;
959 /* Strip off any plural and try the units table again. */
960 if (word[wordlen - 1] == 'S')
962 word[wordlen - 1] = '\0';
963 for (tp = time_units_table; tp->name; tp++)
964 if (strcmp (word, tp->name) == 0)
965 return tp;
966 word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
969 for (tp = relative_time_table; tp->name; tp++)
970 if (strcmp (word, tp->name) == 0)
971 return tp;
973 /* Military time zones. */
974 if (wordlen == 1)
975 for (tp = military_table; tp->name; tp++)
976 if (word[0] == tp->name[0])
977 return tp;
979 /* Drop out any periods and try the time zone table again. */
980 for (period_found = false, p = q = word; (*p = *q); q++)
981 if (*q == '.')
982 period_found = true;
983 else
984 p++;
985 if (period_found && (tp = lookup_zone (pc, word)))
986 return tp;
988 return NULL;
991 static int
992 yylex (YYSTYPE *lvalp, parser_control *pc)
994 unsigned char c;
995 size_t count;
997 for (;;)
999 while (c = *pc->input, c_isspace (c))
1000 pc->input++;
1002 if (ISDIGIT (c) || c == '-' || c == '+')
1004 char const *p;
1005 int sign;
1006 unsigned long int value;
1007 if (c == '-' || c == '+')
1009 sign = c == '-' ? -1 : 1;
1010 while (c = *++pc->input, c_isspace (c))
1011 continue;
1012 if (! ISDIGIT (c))
1013 /* skip the '-' sign */
1014 continue;
1016 else
1017 sign = 0;
1018 p = pc->input;
1019 for (value = 0; ; value *= 10)
1021 unsigned long int value1 = value + (c - '0');
1022 if (value1 < value)
1023 return '?';
1024 value = value1;
1025 c = *++p;
1026 if (! ISDIGIT (c))
1027 break;
1028 if (ULONG_MAX / 10 < value)
1029 return '?';
1031 if ((c == '.' || c == ',') && ISDIGIT (p[1]))
1033 time_t s;
1034 int ns;
1035 int digits;
1036 unsigned long int value1;
1038 /* Check for overflow when converting value to time_t. */
1039 if (sign < 0)
1041 s = - value;
1042 if (0 < s)
1043 return '?';
1044 value1 = -s;
1046 else
1048 s = value;
1049 if (s < 0)
1050 return '?';
1051 value1 = s;
1053 if (value != value1)
1054 return '?';
1056 /* Accumulate fraction, to ns precision. */
1057 p++;
1058 ns = *p++ - '0';
1059 for (digits = 2; digits <= LOG10_BILLION; digits++)
1061 ns *= 10;
1062 if (ISDIGIT (*p))
1063 ns += *p++ - '0';
1066 /* Skip excess digits, truncating toward -Infinity. */
1067 if (sign < 0)
1068 for (; ISDIGIT (*p); p++)
1069 if (*p != '0')
1071 ns++;
1072 break;
1074 while (ISDIGIT (*p))
1075 p++;
1077 /* Adjust to the timespec convention, which is that
1078 tv_nsec is always a positive offset even if tv_sec is
1079 negative. */
1080 if (sign < 0 && ns)
1082 s--;
1083 if (! (s < 0))
1084 return '?';
1085 ns = BILLION - ns;
1088 lvalp->timespec.tv_sec = s;
1089 lvalp->timespec.tv_nsec = ns;
1090 pc->input = p;
1091 return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1093 else
1095 lvalp->textintval.negative = sign < 0;
1096 if (sign < 0)
1098 lvalp->textintval.value = - value;
1099 if (0 < lvalp->textintval.value)
1100 return '?';
1102 else
1104 lvalp->textintval.value = value;
1105 if (lvalp->textintval.value < 0)
1106 return '?';
1108 lvalp->textintval.digits = p - pc->input;
1109 pc->input = p;
1110 return sign ? tSNUMBER : tUNUMBER;
1114 if (c_isalpha (c))
1116 char buff[20];
1117 char *p = buff;
1118 table const *tp;
1122 if (p < buff + sizeof buff - 1)
1123 *p++ = c;
1124 c = *++pc->input;
1126 while (c_isalpha (c) || c == '.');
1128 *p = '\0';
1129 tp = lookup_word (pc, buff);
1130 if (! tp)
1131 return '?';
1132 lvalp->intval = tp->value;
1133 return tp->type;
1136 if (c != '(')
1137 return *pc->input++;
1138 count = 0;
1141 c = *pc->input++;
1142 if (c == '\0')
1143 return c;
1144 if (c == '(')
1145 count++;
1146 else if (c == ')')
1147 count--;
1149 while (count != 0);
1153 /* Do nothing if the parser reports an error. */
1154 static int
1155 yyerror (parser_control const *pc _GL_UNUSED,
1156 char const *s _GL_UNUSED)
1158 return 0;
1161 /* If *TM0 is the old and *TM1 is the new value of a struct tm after
1162 passing it to mktime, return true if it's OK that mktime returned T.
1163 It's not OK if *TM0 has out-of-range members. */
1165 static bool
1166 mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
1168 if (t == (time_t) -1)
1170 /* Guard against falsely reporting an error when parsing a time
1171 stamp that happens to equal (time_t) -1, on a host that
1172 supports such a time stamp. */
1173 tm1 = localtime (&t);
1174 if (!tm1)
1175 return false;
1178 return ! ((tm0->tm_sec ^ tm1->tm_sec)
1179 | (tm0->tm_min ^ tm1->tm_min)
1180 | (tm0->tm_hour ^ tm1->tm_hour)
1181 | (tm0->tm_mday ^ tm1->tm_mday)
1182 | (tm0->tm_mon ^ tm1->tm_mon)
1183 | (tm0->tm_year ^ tm1->tm_year));
1186 /* A reasonable upper bound for the size of ordinary TZ strings.
1187 Use heap allocation if TZ's length exceeds this. */
1188 enum { TZBUFSIZE = 100 };
1190 /* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
1191 otherwise. */
1192 static char *
1193 get_tz (char tzbuf[TZBUFSIZE])
1195 char *tz = getenv ("TZ");
1196 if (tz)
1198 size_t tzsize = strlen (tz) + 1;
1199 tz = (tzsize <= TZBUFSIZE
1200 ? memcpy (tzbuf, tz, tzsize)
1201 : xmemdup (tz, tzsize));
1203 return tz;
1206 /* Parse a date/time string, storing the resulting time value into *RESULT.
1207 The string itself is pointed to by P. Return true if successful.
1208 P can be an incomplete or relative time specification; if so, use
1209 *NOW as the basis for the returned time. */
1210 bool
1211 parse_datetime (struct timespec *result, char const *p,
1212 struct timespec const *now)
1214 time_t Start;
1215 long int Start_ns;
1216 struct tm const *tmp;
1217 struct tm tm;
1218 struct tm tm0;
1219 parser_control pc;
1220 struct timespec gettime_buffer;
1221 unsigned char c;
1222 bool tz_was_altered = false;
1223 char *tz0 = NULL;
1224 char tz0buf[TZBUFSIZE];
1225 bool ok = true;
1227 if (! now)
1229 gettime (&gettime_buffer);
1230 now = &gettime_buffer;
1233 Start = now->tv_sec;
1234 Start_ns = now->tv_nsec;
1236 tmp = localtime (&now->tv_sec);
1237 if (! tmp)
1238 return false;
1240 while (c = *p, c_isspace (c))
1241 p++;
1243 if (strncmp (p, "TZ=\"", 4) == 0)
1245 char const *tzbase = p + 4;
1246 size_t tzsize = 1;
1247 char const *s;
1249 for (s = tzbase; *s; s++, tzsize++)
1250 if (*s == '\\')
1252 s++;
1253 if (! (*s == '\\' || *s == '"'))
1254 break;
1256 else if (*s == '"')
1258 char *z;
1259 char *tz1;
1260 char tz1buf[TZBUFSIZE];
1261 bool large_tz = TZBUFSIZE < tzsize;
1262 bool setenv_ok;
1263 /* Free tz0, in case this is the 2nd or subsequent time through. */
1264 free (tz0);
1265 tz0 = get_tz (tz0buf);
1266 z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf;
1267 for (s = tzbase; *s != '"'; s++)
1268 *z++ = *(s += *s == '\\');
1269 *z = '\0';
1270 setenv_ok = setenv ("TZ", tz1, 1) == 0;
1271 if (large_tz)
1272 free (tz1);
1273 if (!setenv_ok)
1274 goto fail;
1275 tz_was_altered = true;
1276 p = s + 1;
1280 /* As documented, be careful to treat the empty string just like
1281 a date string of "0". Without this, an empty string would be
1282 declared invalid when parsed during a DST transition. */
1283 if (*p == '\0')
1284 p = "0";
1286 pc.input = p;
1287 pc.year.value = tmp->tm_year;
1288 pc.year.value += TM_YEAR_BASE;
1289 pc.year.digits = 0;
1290 pc.month = tmp->tm_mon + 1;
1291 pc.day = tmp->tm_mday;
1292 pc.hour = tmp->tm_hour;
1293 pc.minutes = tmp->tm_min;
1294 pc.seconds.tv_sec = tmp->tm_sec;
1295 pc.seconds.tv_nsec = Start_ns;
1296 tm.tm_isdst = tmp->tm_isdst;
1298 pc.meridian = MER24;
1299 pc.rel = RELATIVE_TIME_0;
1300 pc.timespec_seen = false;
1301 pc.rels_seen = false;
1302 pc.dates_seen = 0;
1303 pc.days_seen = 0;
1304 pc.times_seen = 0;
1305 pc.local_zones_seen = 0;
1306 pc.dsts_seen = 0;
1307 pc.zones_seen = 0;
1309 #if HAVE_STRUCT_TM_TM_ZONE
1310 pc.local_time_zone_table[0].name = tmp->tm_zone;
1311 pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1312 pc.local_time_zone_table[0].value = tmp->tm_isdst;
1313 pc.local_time_zone_table[1].name = NULL;
1315 /* Probe the names used in the next three calendar quarters, looking
1316 for a tm_isdst different from the one we already have. */
1318 int quarter;
1319 for (quarter = 1; quarter <= 3; quarter++)
1321 time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1322 struct tm const *probe_tm = localtime (&probe);
1323 if (probe_tm && probe_tm->tm_zone
1324 && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1327 pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1328 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1329 pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1330 pc.local_time_zone_table[2].name = NULL;
1332 break;
1336 #else
1337 #if HAVE_TZNAME
1339 # if !HAVE_DECL_TZNAME
1340 extern char *tzname[];
1341 # endif
1342 int i;
1343 for (i = 0; i < 2; i++)
1345 pc.local_time_zone_table[i].name = tzname[i];
1346 pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1347 pc.local_time_zone_table[i].value = i;
1349 pc.local_time_zone_table[i].name = NULL;
1351 #else
1352 pc.local_time_zone_table[0].name = NULL;
1353 #endif
1354 #endif
1356 if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1357 && ! strcmp (pc.local_time_zone_table[0].name,
1358 pc.local_time_zone_table[1].name))
1360 /* This locale uses the same abbrevation for standard and
1361 daylight times. So if we see that abbreviation, we don't
1362 know whether it's daylight time. */
1363 pc.local_time_zone_table[0].value = -1;
1364 pc.local_time_zone_table[1].name = NULL;
1367 if (yyparse (&pc) != 0)
1368 goto fail;
1370 if (pc.timespec_seen)
1371 *result = pc.seconds;
1372 else
1374 if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1375 | (pc.local_zones_seen + pc.zones_seen)))
1376 goto fail;
1378 tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
1379 tm.tm_mon = pc.month - 1;
1380 tm.tm_mday = pc.day;
1381 if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1383 tm.tm_hour = to_hour (pc.hour, pc.meridian);
1384 if (tm.tm_hour < 0)
1385 goto fail;
1386 tm.tm_min = pc.minutes;
1387 tm.tm_sec = pc.seconds.tv_sec;
1389 else
1391 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1392 pc.seconds.tv_nsec = 0;
1395 /* Let mktime deduce tm_isdst if we have an absolute time stamp. */
1396 if (pc.dates_seen | pc.days_seen | pc.times_seen)
1397 tm.tm_isdst = -1;
1399 /* But if the input explicitly specifies local time with or without
1400 DST, give mktime that information. */
1401 if (pc.local_zones_seen)
1402 tm.tm_isdst = pc.local_isdst;
1404 tm0 = tm;
1406 Start = mktime (&tm);
1408 if (! mktime_ok (&tm0, &tm, Start))
1410 if (! pc.zones_seen)
1411 goto fail;
1412 else
1414 /* Guard against falsely reporting errors near the time_t
1415 boundaries when parsing times in other time zones. For
1416 example, suppose the input string "1969-12-31 23:00:00 -0100",
1417 the current time zone is 8 hours ahead of UTC, and the min
1418 time_t value is 1970-01-01 00:00:00 UTC. Then the min
1419 localtime value is 1970-01-01 08:00:00, and mktime will
1420 therefore fail on 1969-12-31 23:00:00. To work around the
1421 problem, set the time zone to 1 hour behind UTC temporarily
1422 by setting TZ="XXX1:00" and try mktime again. */
1424 long int time_zone = pc.time_zone;
1425 long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
1426 long int abs_time_zone_hour = abs_time_zone / 60;
1427 int abs_time_zone_min = abs_time_zone % 60;
1428 char tz1buf[sizeof "XXX+0:00"
1429 + sizeof pc.time_zone * CHAR_BIT / 3];
1430 if (!tz_was_altered)
1431 tz0 = get_tz (tz0buf);
1432 sprintf (tz1buf, "XXX%s%ld:%02d", "-" + (time_zone < 0),
1433 abs_time_zone_hour, abs_time_zone_min);
1434 if (setenv ("TZ", tz1buf, 1) != 0)
1435 goto fail;
1436 tz_was_altered = true;
1437 tm = tm0;
1438 Start = mktime (&tm);
1439 if (! mktime_ok (&tm0, &tm, Start))
1440 goto fail;
1444 if (pc.days_seen && ! pc.dates_seen)
1446 tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1447 + 7 * (pc.day_ordinal
1448 - (0 < pc.day_ordinal
1449 && tm.tm_wday != pc.day_number)));
1450 tm.tm_isdst = -1;
1451 Start = mktime (&tm);
1452 if (Start == (time_t) -1)
1453 goto fail;
1456 /* Add relative date. */
1457 if (pc.rel.year | pc.rel.month | pc.rel.day)
1459 int year = tm.tm_year + pc.rel.year;
1460 int month = tm.tm_mon + pc.rel.month;
1461 int day = tm.tm_mday + pc.rel.day;
1462 if (((year < tm.tm_year) ^ (pc.rel.year < 0))
1463 | ((month < tm.tm_mon) ^ (pc.rel.month < 0))
1464 | ((day < tm.tm_mday) ^ (pc.rel.day < 0)))
1465 goto fail;
1466 tm.tm_year = year;
1467 tm.tm_mon = month;
1468 tm.tm_mday = day;
1469 tm.tm_hour = tm0.tm_hour;
1470 tm.tm_min = tm0.tm_min;
1471 tm.tm_sec = tm0.tm_sec;
1472 tm.tm_isdst = tm0.tm_isdst;
1473 Start = mktime (&tm);
1474 if (Start == (time_t) -1)
1475 goto fail;
1478 /* The only "output" of this if-block is an updated Start value,
1479 so this block must follow others that clobber Start. */
1480 if (pc.zones_seen)
1482 long int delta = pc.time_zone * 60;
1483 time_t t1;
1484 #ifdef HAVE_TM_GMTOFF
1485 delta -= tm.tm_gmtoff;
1486 #else
1487 time_t t = Start;
1488 struct tm const *gmt = gmtime (&t);
1489 if (! gmt)
1490 goto fail;
1491 delta -= tm_diff (&tm, gmt);
1492 #endif
1493 t1 = Start - delta;
1494 if ((Start < t1) != (delta < 0))
1495 goto fail; /* time_t overflow */
1496 Start = t1;
1499 /* Add relative hours, minutes, and seconds. On hosts that support
1500 leap seconds, ignore the possibility of leap seconds; e.g.,
1501 "+ 10 minutes" adds 600 seconds, even if one of them is a
1502 leap second. Typically this is not what the user wants, but it's
1503 too hard to do it the other way, because the time zone indicator
1504 must be applied before relative times, and if mktime is applied
1505 again the time zone will be lost. */
1507 long int sum_ns = pc.seconds.tv_nsec + pc.rel.ns;
1508 long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
1509 time_t t0 = Start;
1510 long int d1 = 60 * 60 * pc.rel.hour;
1511 time_t t1 = t0 + d1;
1512 long int d2 = 60 * pc.rel.minutes;
1513 time_t t2 = t1 + d2;
1514 long_time_t d3 = pc.rel.seconds;
1515 long_time_t t3 = t2 + d3;
1516 long int d4 = (sum_ns - normalized_ns) / BILLION;
1517 long_time_t t4 = t3 + d4;
1518 time_t t5 = t4;
1520 if ((d1 / (60 * 60) ^ pc.rel.hour)
1521 | (d2 / 60 ^ pc.rel.minutes)
1522 | ((t1 < t0) ^ (d1 < 0))
1523 | ((t2 < t1) ^ (d2 < 0))
1524 | ((t3 < t2) ^ (d3 < 0))
1525 | ((t4 < t3) ^ (d4 < 0))
1526 | (t5 != t4))
1527 goto fail;
1529 result->tv_sec = t5;
1530 result->tv_nsec = normalized_ns;
1534 goto done;
1536 fail:
1537 ok = false;
1538 done:
1539 if (tz_was_altered)
1540 ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
1541 if (tz0 != tz0buf)
1542 free (tz0);
1543 return ok;
1546 #if TEST
1549 main (int ac, char **av)
1551 char buff[BUFSIZ];
1553 printf ("Enter date, or blank line to exit.\n\t> ");
1554 fflush (stdout);
1556 buff[BUFSIZ - 1] = '\0';
1557 while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1559 struct timespec d;
1560 struct tm const *tm;
1561 if (! parse_datetime (&d, buff, NULL))
1562 printf ("Bad format - couldn't convert.\n");
1563 else if (! (tm = localtime (&d.tv_sec)))
1565 long int sec = d.tv_sec;
1566 printf ("localtime (%ld) failed\n", sec);
1568 else
1570 int ns = d.tv_nsec;
1571 printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
1572 tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
1573 tm->tm_hour, tm->tm_min, tm->tm_sec, ns);
1575 printf ("\t> ");
1576 fflush (stdout);
1578 return 0;
1580 #endif /* TEST */