Added a gnulib with GPL components for use by applications.
[gnutls:gnutls.git] / src / gl / parse-datetime.y
1 %{
2 /* Parse a string into an internal time stamp.
3
4    Copyright (C) 1999-2000, 2002-2013 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 (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             /* Free tz0, in case this is the 2nd or subsequent time through. */
1307             free (tz0);
1308             tz0 = get_tz (tz0buf);
1309             z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf;
1310             for (s = tzbase; *s != '"'; s++)
1311               *z++ = *(s += *s == '\\');
1312             *z = '\0';
1313             setenv_ok = setenv ("TZ", tz1, 1) == 0;
1314             if (large_tz)
1315               free (tz1);
1316             if (!setenv_ok)
1317               goto fail;
1318             tz_was_altered = true;
1319             p = s + 1;
1320           }
1321     }
1322
1323   /* As documented, be careful to treat the empty string just like
1324      a date string of "0".  Without this, an empty string would be
1325      declared invalid when parsed during a DST transition.  */
1326   if (*p == '\0')
1327     p = "0";
1328
1329   pc.input = p;
1330   pc.year.value = tmp->tm_year;
1331   pc.year.value += TM_YEAR_BASE;
1332   pc.year.digits = 0;
1333   pc.month = tmp->tm_mon + 1;
1334   pc.day = tmp->tm_mday;
1335   pc.hour = tmp->tm_hour;
1336   pc.minutes = tmp->tm_min;
1337   pc.seconds.tv_sec = tmp->tm_sec;
1338   pc.seconds.tv_nsec = Start_ns;
1339   tm.tm_isdst = tmp->tm_isdst;
1340
1341   pc.meridian = MER24;
1342   pc.rel = RELATIVE_TIME_0;
1343   pc.timespec_seen = false;
1344   pc.rels_seen = false;
1345   pc.dates_seen = 0;
1346   pc.days_seen = 0;
1347   pc.times_seen = 0;
1348   pc.local_zones_seen = 0;
1349   pc.dsts_seen = 0;
1350   pc.zones_seen = 0;
1351
1352 #if HAVE_STRUCT_TM_TM_ZONE
1353   pc.local_time_zone_table[0].name = tmp->tm_zone;
1354   pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1355   pc.local_time_zone_table[0].value = tmp->tm_isdst;
1356   pc.local_time_zone_table[1].name = NULL;
1357
1358   /* Probe the names used in the next three calendar quarters, looking
1359      for a tm_isdst different from the one we already have.  */
1360   {
1361     int quarter;
1362     for (quarter = 1; quarter <= 3; quarter++)
1363       {
1364         time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1365         struct tm const *probe_tm = localtime (&probe);
1366         if (probe_tm && probe_tm->tm_zone
1367             && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1368           {
1369               {
1370                 pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1371                 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1372                 pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1373                 pc.local_time_zone_table[2].name = NULL;
1374               }
1375             break;
1376           }
1377       }
1378   }
1379 #else
1380 #if HAVE_TZNAME
1381   {
1382 # if !HAVE_DECL_TZNAME
1383     extern char *tzname[];
1384 # endif
1385     int i;
1386     for (i = 0; i < 2; i++)
1387       {
1388         pc.local_time_zone_table[i].name = tzname[i];
1389         pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1390         pc.local_time_zone_table[i].value = i;
1391       }
1392     pc.local_time_zone_table[i].name = NULL;
1393   }
1394 #else
1395   pc.local_time_zone_table[0].name = NULL;
1396 #endif
1397 #endif
1398
1399   if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1400       && ! strcmp (pc.local_time_zone_table[0].name,
1401                    pc.local_time_zone_table[1].name))
1402     {
1403       /* This locale uses the same abbreviation for standard and
1404          daylight times.  So if we see that abbreviation, we don't
1405          know whether it's daylight time.  */
1406       pc.local_time_zone_table[0].value = -1;
1407       pc.local_time_zone_table[1].name = NULL;
1408     }
1409
1410   if (yyparse (&pc) != 0)
1411     goto fail;
1412
1413   if (pc.timespec_seen)
1414     *result = pc.seconds;
1415   else
1416     {
1417       if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1418                | (pc.local_zones_seen + pc.zones_seen)))
1419         goto fail;
1420
1421       tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
1422       tm.tm_mon = pc.month - 1;
1423       tm.tm_mday = pc.day;
1424       if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1425         {
1426           tm.tm_hour = to_hour (pc.hour, pc.meridian);
1427           if (tm.tm_hour < 0)
1428             goto fail;
1429           tm.tm_min = pc.minutes;
1430           tm.tm_sec = pc.seconds.tv_sec;
1431         }
1432       else
1433         {
1434           tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1435           pc.seconds.tv_nsec = 0;
1436         }
1437
1438       /* Let mktime deduce tm_isdst if we have an absolute time stamp.  */
1439       if (pc.dates_seen | pc.days_seen | pc.times_seen)
1440         tm.tm_isdst = -1;
1441
1442       /* But if the input explicitly specifies local time with or without
1443          DST, give mktime that information.  */
1444       if (pc.local_zones_seen)
1445         tm.tm_isdst = pc.local_isdst;
1446
1447       tm0 = tm;
1448
1449       Start = mktime (&tm);
1450
1451       if (! mktime_ok (&tm0, &tm, Start))
1452         {
1453           if (! pc.zones_seen)
1454             goto fail;
1455           else
1456             {
1457               /* Guard against falsely reporting errors near the time_t
1458                  boundaries when parsing times in other time zones.  For
1459                  example, suppose the input string "1969-12-31 23:00:00 -0100",
1460                  the current time zone is 8 hours ahead of UTC, and the min
1461                  time_t value is 1970-01-01 00:00:00 UTC.  Then the min
1462                  localtime value is 1970-01-01 08:00:00, and mktime will
1463                  therefore fail on 1969-12-31 23:00:00.  To work around the
1464                  problem, set the time zone to 1 hour behind UTC temporarily
1465                  by setting TZ="XXX1:00" and try mktime again.  */
1466
1467               long int time_zone = pc.time_zone;
1468               long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
1469               long int abs_time_zone_hour = abs_time_zone / 60;
1470               int abs_time_zone_min = abs_time_zone % 60;
1471               char tz1buf[sizeof "XXX+0:00"
1472                           + sizeof pc.time_zone * CHAR_BIT / 3];
1473               if (!tz_was_altered)
1474                 tz0 = get_tz (tz0buf);
1475               sprintf (tz1buf, "XXX%s%ld:%02d", &"-"[time_zone < 0],
1476                        abs_time_zone_hour, abs_time_zone_min);
1477               if (setenv ("TZ", tz1buf, 1) != 0)
1478                 goto fail;
1479               tz_was_altered = true;
1480               tm = tm0;
1481               Start = mktime (&tm);
1482               if (! mktime_ok (&tm0, &tm, Start))
1483                 goto fail;
1484             }
1485         }
1486
1487       if (pc.days_seen && ! pc.dates_seen)
1488         {
1489           tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1490                          + 7 * (pc.day_ordinal
1491                                 - (0 < pc.day_ordinal
1492                                    && tm.tm_wday != pc.day_number)));
1493           tm.tm_isdst = -1;
1494           Start = mktime (&tm);
1495           if (Start == (time_t) -1)
1496             goto fail;
1497         }
1498
1499       /* Add relative date.  */
1500       if (pc.rel.year | pc.rel.month | pc.rel.day)
1501         {
1502           int year = tm.tm_year + pc.rel.year;
1503           int month = tm.tm_mon + pc.rel.month;
1504           int day = tm.tm_mday + pc.rel.day;
1505           if (((year < tm.tm_year) ^ (pc.rel.year < 0))
1506               | ((month < tm.tm_mon) ^ (pc.rel.month < 0))
1507               | ((day < tm.tm_mday) ^ (pc.rel.day < 0)))
1508             goto fail;
1509           tm.tm_year = year;
1510           tm.tm_mon = month;
1511           tm.tm_mday = day;
1512           tm.tm_hour = tm0.tm_hour;
1513           tm.tm_min = tm0.tm_min;
1514           tm.tm_sec = tm0.tm_sec;
1515           tm.tm_isdst = tm0.tm_isdst;
1516           Start = mktime (&tm);
1517           if (Start == (time_t) -1)
1518             goto fail;
1519         }
1520
1521       /* The only "output" of this if-block is an updated Start value,
1522          so this block must follow others that clobber Start.  */
1523       if (pc.zones_seen)
1524         {
1525           long int delta = pc.time_zone * 60;
1526           time_t t1;
1527 #ifdef HAVE_TM_GMTOFF
1528           delta -= tm.tm_gmtoff;
1529 #else
1530           time_t t = Start;
1531           struct tm const *gmt = gmtime (&t);
1532           if (! gmt)
1533             goto fail;
1534           delta -= tm_diff (&tm, gmt);
1535 #endif
1536           t1 = Start - delta;
1537           if ((Start < t1) != (delta < 0))
1538             goto fail;  /* time_t overflow */
1539           Start = t1;
1540         }
1541
1542       /* Add relative hours, minutes, and seconds.  On hosts that support
1543          leap seconds, ignore the possibility of leap seconds; e.g.,
1544          "+ 10 minutes" adds 600 seconds, even if one of them is a
1545          leap second.  Typically this is not what the user wants, but it's
1546          too hard to do it the other way, because the time zone indicator
1547          must be applied before relative times, and if mktime is applied
1548          again the time zone will be lost.  */
1549       {
1550         long int sum_ns = pc.seconds.tv_nsec + pc.rel.ns;
1551         long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
1552         time_t t0 = Start;
1553         long int d1 = 60 * 60 * pc.rel.hour;
1554         time_t t1 = t0 + d1;
1555         long int d2 = 60 * pc.rel.minutes;
1556         time_t t2 = t1 + d2;
1557         long_time_t d3 = pc.rel.seconds;
1558         long_time_t t3 = t2 + d3;
1559         long int d4 = (sum_ns - normalized_ns) / BILLION;
1560         long_time_t t4 = t3 + d4;
1561         time_t t5 = t4;
1562
1563         if ((d1 / (60 * 60) ^ pc.rel.hour)
1564             | (d2 / 60 ^ pc.rel.minutes)
1565             | ((t1 < t0) ^ (d1 < 0))
1566             | ((t2 < t1) ^ (d2 < 0))
1567             | ((t3 < t2) ^ (d3 < 0))
1568             | ((t4 < t3) ^ (d4 < 0))
1569             | (t5 != t4))
1570           goto fail;
1571
1572         result->tv_sec = t5;
1573         result->tv_nsec = normalized_ns;
1574       }
1575     }
1576
1577   goto done;
1578
1579  fail:
1580   ok = false;
1581  done:
1582   if (tz_was_altered)
1583     ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
1584   if (tz0 != tz0buf)
1585     free (tz0);
1586   return ok;
1587 }
1588
1589 #if TEST
1590
1591 int
1592 main (int ac, char **av)
1593 {
1594   char buff[BUFSIZ];
1595
1596   printf ("Enter date, or blank line to exit.\n\t> ");
1597   fflush (stdout);
1598
1599   buff[BUFSIZ - 1] = '\0';
1600   while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1601     {
1602       struct timespec d;
1603       struct tm const *tm;
1604       if (! parse_datetime (&d, buff, NULL))
1605         printf ("Bad format - couldn't convert.\n");
1606       else if (! (tm = localtime (&d.tv_sec)))
1607         {
1608           long int sec = d.tv_sec;
1609           printf ("localtime (%ld) failed\n", sec);
1610         }
1611       else
1612         {
1613           int ns = d.tv_nsec;
1614           printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
1615                   tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
1616                   tm->tm_hour, tm->tm_min, tm->tm_sec, ns);
1617         }
1618       printf ("\t> ");
1619       fflush (stdout);
1620     }
1621   return 0;
1622 }
1623 #endif /* TEST */