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