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