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