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