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