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