]> git.cworth.org Git - tar/blob - lib/getdate.y
Imported Upstream version 1.21
[tar] / 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 tDAY_SHIFT
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 dayshift
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   | dayshift
506       { apply_relative_time (pc, $1, 1); }
507   ;
508
509 relunit:
510     tORDINAL tYEAR_UNIT
511       { $$ = RELATIVE_TIME_0; $$.year = $1; }
512   | tUNUMBER tYEAR_UNIT
513       { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
514   | tYEAR_UNIT
515       { $$ = RELATIVE_TIME_0; $$.year = 1; }
516   | tORDINAL tMONTH_UNIT
517       { $$ = RELATIVE_TIME_0; $$.month = $1; }
518   | tUNUMBER tMONTH_UNIT
519       { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
520   | tMONTH_UNIT
521       { $$ = RELATIVE_TIME_0; $$.month = 1; }
522   | tORDINAL tDAY_UNIT
523       { $$ = RELATIVE_TIME_0; $$.day = $1 * $2; }
524   | tUNUMBER tDAY_UNIT
525       { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
526   | tDAY_UNIT
527       { $$ = RELATIVE_TIME_0; $$.day = $1; }
528   | tORDINAL tHOUR_UNIT
529       { $$ = RELATIVE_TIME_0; $$.hour = $1; }
530   | tUNUMBER tHOUR_UNIT
531       { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
532   | tHOUR_UNIT
533       { $$ = RELATIVE_TIME_0; $$.hour = 1; }
534   | tORDINAL tMINUTE_UNIT
535       { $$ = RELATIVE_TIME_0; $$.minutes = $1; }
536   | tUNUMBER tMINUTE_UNIT
537       { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
538   | tMINUTE_UNIT
539       { $$ = RELATIVE_TIME_0; $$.minutes = 1; }
540   | tORDINAL tSEC_UNIT
541       { $$ = RELATIVE_TIME_0; $$.seconds = $1; }
542   | tUNUMBER tSEC_UNIT
543       { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
544   | tSDECIMAL_NUMBER tSEC_UNIT
545       { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
546   | tUDECIMAL_NUMBER tSEC_UNIT
547       { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
548   | tSEC_UNIT
549       { $$ = RELATIVE_TIME_0; $$.seconds = 1; }
550   | relunit_snumber
551   ;
552
553 relunit_snumber:
554     tSNUMBER tYEAR_UNIT
555       { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
556   | tSNUMBER tMONTH_UNIT
557       { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
558   | tSNUMBER tDAY_UNIT
559       { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
560   | tSNUMBER tHOUR_UNIT
561       { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
562   | tSNUMBER tMINUTE_UNIT
563       { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
564   | tSNUMBER tSEC_UNIT
565       { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
566   ;
567
568 dayshift:
569     tDAY_SHIFT
570       { $$ = RELATIVE_TIME_0; $$.day = $1; }
571   ;
572
573 seconds: signed_seconds | unsigned_seconds;
574
575 signed_seconds:
576     tSDECIMAL_NUMBER
577   | tSNUMBER
578       { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
579   ;
580
581 unsigned_seconds:
582     tUDECIMAL_NUMBER
583   | tUNUMBER
584       { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
585   ;
586
587 number:
588     tUNUMBER
589       { digits_to_date_time (pc, $1); }
590   ;
591
592 hybrid:
593     tUNUMBER relunit_snumber
594       {
595         /* Hybrid all-digit and relative offset, so that we accept e.g.,
596            "YYYYMMDD +N days" as well as "YYYYMMDD N days".  */
597         digits_to_date_time (pc, $1);
598         apply_relative_time (pc, $2, 1);
599       }
600   ;
601
602 o_colon_minutes:
603     /* empty */
604       { $$ = -1; }
605   | ':' tUNUMBER
606       { $$ = $2.value; }
607   ;
608
609 o_merid:
610     /* empty */
611       { $$ = MER24; }
612   | tMERIDIAN
613       { $$ = $1; }
614   ;
615
616 %%
617
618 static table const meridian_table[] =
619 {
620   { "AM",   tMERIDIAN, MERam },
621   { "A.M.", tMERIDIAN, MERam },
622   { "PM",   tMERIDIAN, MERpm },
623   { "P.M.", tMERIDIAN, MERpm },
624   { NULL, 0, 0 }
625 };
626
627 static table const dst_table[] =
628 {
629   { "DST", tDST, 0 }
630 };
631
632 static table const month_and_day_table[] =
633 {
634   { "JANUARY",  tMONTH,  1 },
635   { "FEBRUARY", tMONTH,  2 },
636   { "MARCH",    tMONTH,  3 },
637   { "APRIL",    tMONTH,  4 },
638   { "MAY",      tMONTH,  5 },
639   { "JUNE",     tMONTH,  6 },
640   { "JULY",     tMONTH,  7 },
641   { "AUGUST",   tMONTH,  8 },
642   { "SEPTEMBER",tMONTH,  9 },
643   { "SEPT",     tMONTH,  9 },
644   { "OCTOBER",  tMONTH, 10 },
645   { "NOVEMBER", tMONTH, 11 },
646   { "DECEMBER", tMONTH, 12 },
647   { "SUNDAY",   tDAY,    0 },
648   { "MONDAY",   tDAY,    1 },
649   { "TUESDAY",  tDAY,    2 },
650   { "TUES",     tDAY,    2 },
651   { "WEDNESDAY",tDAY,    3 },
652   { "WEDNES",   tDAY,    3 },
653   { "THURSDAY", tDAY,    4 },
654   { "THUR",     tDAY,    4 },
655   { "THURS",    tDAY,    4 },
656   { "FRIDAY",   tDAY,    5 },
657   { "SATURDAY", tDAY,    6 },
658   { NULL, 0, 0 }
659 };
660
661 static table const time_units_table[] =
662 {
663   { "YEAR",     tYEAR_UNIT,      1 },
664   { "MONTH",    tMONTH_UNIT,     1 },
665   { "FORTNIGHT",tDAY_UNIT,      14 },
666   { "WEEK",     tDAY_UNIT,       7 },
667   { "DAY",      tDAY_UNIT,       1 },
668   { "HOUR",     tHOUR_UNIT,      1 },
669   { "MINUTE",   tMINUTE_UNIT,    1 },
670   { "MIN",      tMINUTE_UNIT,    1 },
671   { "SECOND",   tSEC_UNIT,       1 },
672   { "SEC",      tSEC_UNIT,       1 },
673   { NULL, 0, 0 }
674 };
675
676 /* Assorted relative-time words. */
677 static table const relative_time_table[] =
678 {
679   { "TOMORROW", tDAY_SHIFT,      1 },
680   { "YESTERDAY",tDAY_SHIFT,     -1 },
681   { "TODAY",    tDAY_SHIFT,      0 },
682   { "NOW",      tDAY_SHIFT,      0 },
683   { "LAST",     tORDINAL,       -1 },
684   { "THIS",     tORDINAL,        0 },
685   { "NEXT",     tORDINAL,        1 },
686   { "FIRST",    tORDINAL,        1 },
687 /*{ "SECOND",   tORDINAL,        2 }, */
688   { "THIRD",    tORDINAL,        3 },
689   { "FOURTH",   tORDINAL,        4 },
690   { "FIFTH",    tORDINAL,        5 },
691   { "SIXTH",    tORDINAL,        6 },
692   { "SEVENTH",  tORDINAL,        7 },
693   { "EIGHTH",   tORDINAL,        8 },
694   { "NINTH",    tORDINAL,        9 },
695   { "TENTH",    tORDINAL,       10 },
696   { "ELEVENTH", tORDINAL,       11 },
697   { "TWELFTH",  tORDINAL,       12 },
698   { "AGO",      tAGO,            1 },
699   { NULL, 0, 0 }
700 };
701
702 /* The universal time zone table.  These labels can be used even for
703    time stamps that would not otherwise be valid, e.g., GMT time
704    stamps in London during summer.  */
705 static table const universal_time_zone_table[] =
706 {
707   { "GMT",      tZONE,     HOUR ( 0) }, /* Greenwich Mean */
708   { "UT",       tZONE,     HOUR ( 0) }, /* Universal (Coordinated) */
709   { "UTC",      tZONE,     HOUR ( 0) },
710   { NULL, 0, 0 }
711 };
712
713 /* The time zone table.  This table is necessarily incomplete, as time
714    zone abbreviations are ambiguous; e.g. Australians interpret "EST"
715    as Eastern time in Australia, not as US Eastern Standard Time.
716    You cannot rely on getdate to handle arbitrary time zone
717    abbreviations; use numeric abbreviations like `-0500' instead.  */
718 static table const time_zone_table[] =
719 {
720   { "WET",      tZONE,     HOUR ( 0) }, /* Western European */
721   { "WEST",     tDAYZONE,  HOUR ( 0) }, /* Western European Summer */
722   { "BST",      tDAYZONE,  HOUR ( 0) }, /* British Summer */
723   { "ART",      tZONE,    -HOUR ( 3) }, /* Argentina */
724   { "BRT",      tZONE,    -HOUR ( 3) }, /* Brazil */
725   { "BRST",     tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
726   { "NST",      tZONE,   -(HOUR ( 3) + 30) },   /* Newfoundland Standard */
727   { "NDT",      tDAYZONE,-(HOUR ( 3) + 30) },   /* Newfoundland Daylight */
728   { "AST",      tZONE,    -HOUR ( 4) }, /* Atlantic Standard */
729   { "ADT",      tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
730   { "CLT",      tZONE,    -HOUR ( 4) }, /* Chile */
731   { "CLST",     tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
732   { "EST",      tZONE,    -HOUR ( 5) }, /* Eastern Standard */
733   { "EDT",      tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
734   { "CST",      tZONE,    -HOUR ( 6) }, /* Central Standard */
735   { "CDT",      tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
736   { "MST",      tZONE,    -HOUR ( 7) }, /* Mountain Standard */
737   { "MDT",      tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
738   { "PST",      tZONE,    -HOUR ( 8) }, /* Pacific Standard */
739   { "PDT",      tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
740   { "AKST",     tZONE,    -HOUR ( 9) }, /* Alaska Standard */
741   { "AKDT",     tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
742   { "HST",      tZONE,    -HOUR (10) }, /* Hawaii Standard */
743   { "HAST",     tZONE,    -HOUR (10) }, /* Hawaii-Aleutian Standard */
744   { "HADT",     tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
745   { "SST",      tZONE,    -HOUR (12) }, /* Samoa Standard */
746   { "WAT",      tZONE,     HOUR ( 1) }, /* West Africa */
747   { "CET",      tZONE,     HOUR ( 1) }, /* Central European */
748   { "CEST",     tDAYZONE,  HOUR ( 1) }, /* Central European Summer */
749   { "MET",      tZONE,     HOUR ( 1) }, /* Middle European */
750   { "MEZ",      tZONE,     HOUR ( 1) }, /* Middle European */
751   { "MEST",     tDAYZONE,  HOUR ( 1) }, /* Middle European Summer */
752   { "MESZ",     tDAYZONE,  HOUR ( 1) }, /* Middle European Summer */
753   { "EET",      tZONE,     HOUR ( 2) }, /* Eastern European */
754   { "EEST",     tDAYZONE,  HOUR ( 2) }, /* Eastern European Summer */
755   { "CAT",      tZONE,     HOUR ( 2) }, /* Central Africa */
756   { "SAST",     tZONE,     HOUR ( 2) }, /* South Africa Standard */
757   { "EAT",      tZONE,     HOUR ( 3) }, /* East Africa */
758   { "MSK",      tZONE,     HOUR ( 3) }, /* Moscow */
759   { "MSD",      tDAYZONE,  HOUR ( 3) }, /* Moscow Daylight */
760   { "IST",      tZONE,    (HOUR ( 5) + 30) },   /* India Standard */
761   { "SGT",      tZONE,     HOUR ( 8) }, /* Singapore */
762   { "KST",      tZONE,     HOUR ( 9) }, /* Korea Standard */
763   { "JST",      tZONE,     HOUR ( 9) }, /* Japan Standard */
764   { "GST",      tZONE,     HOUR (10) }, /* Guam Standard */
765   { "NZST",     tZONE,     HOUR (12) }, /* New Zealand Standard */
766   { "NZDT",     tDAYZONE,  HOUR (12) }, /* New Zealand Daylight */
767   { NULL, 0, 0 }
768 };
769
770 /* Military time zone table. */
771 static table const military_table[] =
772 {
773   { "A", tZONE, -HOUR ( 1) },
774   { "B", tZONE, -HOUR ( 2) },
775   { "C", tZONE, -HOUR ( 3) },
776   { "D", tZONE, -HOUR ( 4) },
777   { "E", tZONE, -HOUR ( 5) },
778   { "F", tZONE, -HOUR ( 6) },
779   { "G", tZONE, -HOUR ( 7) },
780   { "H", tZONE, -HOUR ( 8) },
781   { "I", tZONE, -HOUR ( 9) },
782   { "K", tZONE, -HOUR (10) },
783   { "L", tZONE, -HOUR (11) },
784   { "M", tZONE, -HOUR (12) },
785   { "N", tZONE,  HOUR ( 1) },
786   { "O", tZONE,  HOUR ( 2) },
787   { "P", tZONE,  HOUR ( 3) },
788   { "Q", tZONE,  HOUR ( 4) },
789   { "R", tZONE,  HOUR ( 5) },
790   { "S", tZONE,  HOUR ( 6) },
791   { "T", tZONE,  HOUR ( 7) },
792   { "U", tZONE,  HOUR ( 8) },
793   { "V", tZONE,  HOUR ( 9) },
794   { "W", tZONE,  HOUR (10) },
795   { "X", tZONE,  HOUR (11) },
796   { "Y", tZONE,  HOUR (12) },
797   { "Z", tZONE,  HOUR ( 0) },
798   { NULL, 0, 0 }
799 };
800
801 \f
802
803 /* Convert a time zone expressed as HH:MM into an integer count of
804    minutes.  If MM is negative, then S is of the form HHMM and needs
805    to be picked apart; otherwise, S is of the form HH.  As specified in
806    http://www.opengroup.org/susv3xbd/xbd_chap08.html#tag_08_03, allow
807    only valid TZ range, and consider first two digits as hours, if no
808    minutes specified.  */
809
810 static long int
811 time_zone_hhmm (parser_control *pc, textint s, long int mm)
812 {
813   long int n_minutes;
814
815   /* If the length of S is 1 or 2 and no minutes are specified,
816      interpret it as a number of hours.  */
817   if (s.digits <= 2 && mm < 0)
818     s.value *= 100;
819
820   if (mm < 0)
821     n_minutes = (s.value / 100) * 60 + s.value % 100;
822   else
823     n_minutes = s.value * 60 + (s.negative ? -mm : mm);
824
825   /* If the absolute number of minutes is larger than 24 hours,
826      arrange to reject it by incrementing pc->zones_seen.  Thus,
827      we allow only values in the range UTC-24:00 to UTC+24:00.  */
828   if (24 * 60 < abs (n_minutes))
829     pc->zones_seen++;
830
831   return n_minutes;
832 }
833
834 static int
835 to_hour (long int hours, int meridian)
836 {
837   switch (meridian)
838     {
839     default: /* Pacify GCC.  */
840     case MER24:
841       return 0 <= hours && hours < 24 ? hours : -1;
842     case MERam:
843       return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
844     case MERpm:
845       return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
846     }
847 }
848
849 static long int
850 to_year (textint textyear)
851 {
852   long int year = textyear.value;
853
854   if (year < 0)
855     year = -year;
856
857   /* XPG4 suggests that years 00-68 map to 2000-2068, and
858      years 69-99 map to 1969-1999.  */
859   else if (textyear.digits == 2)
860     year += year < 69 ? 2000 : 1900;
861
862   return year;
863 }
864
865 static table const *
866 lookup_zone (parser_control const *pc, char const *name)
867 {
868   table const *tp;
869
870   for (tp = universal_time_zone_table; tp->name; tp++)
871     if (strcmp (name, tp->name) == 0)
872       return tp;
873
874   /* Try local zone abbreviations before those in time_zone_table, as
875      the local ones are more likely to be right.  */
876   for (tp = pc->local_time_zone_table; tp->name; tp++)
877     if (strcmp (name, tp->name) == 0)
878       return tp;
879
880   for (tp = time_zone_table; tp->name; tp++)
881     if (strcmp (name, tp->name) == 0)
882       return tp;
883
884   return NULL;
885 }
886
887 #if ! HAVE_TM_GMTOFF
888 /* Yield the difference between *A and *B,
889    measured in seconds, ignoring leap seconds.
890    The body of this function is taken directly from the GNU C Library;
891    see src/strftime.c.  */
892 static long int
893 tm_diff (struct tm const *a, struct tm const *b)
894 {
895   /* Compute intervening leap days correctly even if year is negative.
896      Take care to avoid int overflow in leap day calculations.  */
897   int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
898   int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
899   int a100 = a4 / 25 - (a4 % 25 < 0);
900   int b100 = b4 / 25 - (b4 % 25 < 0);
901   int a400 = SHR (a100, 2);
902   int b400 = SHR (b100, 2);
903   int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
904   long int ayear = a->tm_year;
905   long int years = ayear - b->tm_year;
906   long int days = (365 * years + intervening_leap_days
907                    + (a->tm_yday - b->tm_yday));
908   return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
909                 + (a->tm_min - b->tm_min))
910           + (a->tm_sec - b->tm_sec));
911 }
912 #endif /* ! HAVE_TM_GMTOFF */
913
914 static table const *
915 lookup_word (parser_control const *pc, char *word)
916 {
917   char *p;
918   char *q;
919   size_t wordlen;
920   table const *tp;
921   bool period_found;
922   bool abbrev;
923
924   /* Make it uppercase.  */
925   for (p = word; *p; p++)
926     {
927       unsigned char ch = *p;
928       *p = c_toupper (ch);
929     }
930
931   for (tp = meridian_table; tp->name; tp++)
932     if (strcmp (word, tp->name) == 0)
933       return tp;
934
935   /* See if we have an abbreviation for a month. */
936   wordlen = strlen (word);
937   abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
938
939   for (tp = month_and_day_table; tp->name; tp++)
940     if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
941       return tp;
942
943   if ((tp = lookup_zone (pc, word)))
944     return tp;
945
946   if (strcmp (word, dst_table[0].name) == 0)
947     return dst_table;
948
949   for (tp = time_units_table; tp->name; tp++)
950     if (strcmp (word, tp->name) == 0)
951       return tp;
952
953   /* Strip off any plural and try the units table again. */
954   if (word[wordlen - 1] == 'S')
955     {
956       word[wordlen - 1] = '\0';
957       for (tp = time_units_table; tp->name; tp++)
958         if (strcmp (word, tp->name) == 0)
959           return tp;
960       word[wordlen - 1] = 'S';  /* For "this" in relative_time_table.  */
961     }
962
963   for (tp = relative_time_table; tp->name; tp++)
964     if (strcmp (word, tp->name) == 0)
965       return tp;
966
967   /* Military time zones. */
968   if (wordlen == 1)
969     for (tp = military_table; tp->name; tp++)
970       if (word[0] == tp->name[0])
971         return tp;
972
973   /* Drop out any periods and try the time zone table again. */
974   for (period_found = false, p = q = word; (*p = *q); q++)
975     if (*q == '.')
976       period_found = true;
977     else
978       p++;
979   if (period_found && (tp = lookup_zone (pc, word)))
980     return tp;
981
982   return NULL;
983 }
984
985 static int
986 yylex (YYSTYPE *lvalp, parser_control *pc)
987 {
988   unsigned char c;
989   size_t count;
990
991   for (;;)
992     {
993       while (c = *pc->input, c_isspace (c))
994         pc->input++;
995
996       if (ISDIGIT (c) || c == '-' || c == '+')
997         {
998           char const *p;
999           int sign;
1000           unsigned long int value;
1001           if (c == '-' || c == '+')
1002             {
1003               sign = c == '-' ? -1 : 1;
1004               while (c = *++pc->input, c_isspace (c))
1005                 continue;
1006               if (! ISDIGIT (c))
1007                 /* skip the '-' sign */
1008                 continue;
1009             }
1010           else
1011             sign = 0;
1012           p = pc->input;
1013           for (value = 0; ; value *= 10)
1014             {
1015               unsigned long int value1 = value + (c - '0');
1016               if (value1 < value)
1017                 return '?';
1018               value = value1;
1019               c = *++p;
1020               if (! ISDIGIT (c))
1021                 break;
1022               if (ULONG_MAX / 10 < value)
1023                 return '?';
1024             }
1025           if ((c == '.' || c == ',') && ISDIGIT (p[1]))
1026             {
1027               time_t s;
1028               int ns;
1029               int digits;
1030               unsigned long int value1;
1031
1032               /* Check for overflow when converting value to time_t.  */
1033               if (sign < 0)
1034                 {
1035                   s = - value;
1036                   if (0 < s)
1037                     return '?';
1038                   value1 = -s;
1039                 }
1040               else
1041                 {
1042                   s = value;
1043                   if (s < 0)
1044                     return '?';
1045                   value1 = s;
1046                 }
1047               if (value != value1)
1048                 return '?';
1049
1050               /* Accumulate fraction, to ns precision.  */
1051               p++;
1052               ns = *p++ - '0';
1053               for (digits = 2; digits <= LOG10_BILLION; digits++)
1054                 {
1055                   ns *= 10;
1056                   if (ISDIGIT (*p))
1057                     ns += *p++ - '0';
1058                 }
1059
1060               /* Skip excess digits, truncating toward -Infinity.  */
1061               if (sign < 0)
1062                 for (; ISDIGIT (*p); p++)
1063                   if (*p != '0')
1064                     {
1065                       ns++;
1066                       break;
1067                     }
1068               while (ISDIGIT (*p))
1069                 p++;
1070
1071               /* Adjust to the timespec convention, which is that
1072                  tv_nsec is always a positive offset even if tv_sec is
1073                  negative.  */
1074               if (sign < 0 && ns)
1075                 {
1076                   s--;
1077                   if (! (s < 0))
1078                     return '?';
1079                   ns = BILLION - ns;
1080                 }
1081
1082               lvalp->timespec.tv_sec = s;
1083               lvalp->timespec.tv_nsec = ns;
1084               pc->input = p;
1085               return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1086             }
1087           else
1088             {
1089               lvalp->textintval.negative = sign < 0;
1090               if (sign < 0)
1091                 {
1092                   lvalp->textintval.value = - value;
1093                   if (0 < lvalp->textintval.value)
1094                     return '?';
1095                 }
1096               else
1097                 {
1098                   lvalp->textintval.value = value;
1099                   if (lvalp->textintval.value < 0)
1100                     return '?';
1101                 }
1102               lvalp->textintval.digits = p - pc->input;
1103               pc->input = p;
1104               return sign ? tSNUMBER : tUNUMBER;
1105             }
1106         }
1107
1108       if (c_isalpha (c))
1109         {
1110           char buff[20];
1111           char *p = buff;
1112           table const *tp;
1113
1114           do
1115             {
1116               if (p < buff + sizeof buff - 1)
1117                 *p++ = c;
1118               c = *++pc->input;
1119             }
1120           while (c_isalpha (c) || c == '.');
1121
1122           *p = '\0';
1123           tp = lookup_word (pc, buff);
1124           if (! tp)
1125             return '?';
1126           lvalp->intval = tp->value;
1127           return tp->type;
1128         }
1129
1130       if (c != '(')
1131         return *pc->input++;
1132       count = 0;
1133       do
1134         {
1135           c = *pc->input++;
1136           if (c == '\0')
1137             return c;
1138           if (c == '(')
1139             count++;
1140           else if (c == ')')
1141             count--;
1142         }
1143       while (count != 0);
1144     }
1145 }
1146
1147 /* Do nothing if the parser reports an error.  */
1148 static int
1149 yyerror (parser_control const *pc ATTRIBUTE_UNUSED,
1150          char const *s ATTRIBUTE_UNUSED)
1151 {
1152   return 0;
1153 }
1154
1155 /* If *TM0 is the old and *TM1 is the new value of a struct tm after
1156    passing it to mktime, return true if it's OK that mktime returned T.
1157    It's not OK if *TM0 has out-of-range members.  */
1158
1159 static bool
1160 mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
1161 {
1162   if (t == (time_t) -1)
1163     {
1164       /* Guard against falsely reporting an error when parsing a time
1165          stamp that happens to equal (time_t) -1, on a host that
1166          supports such a time stamp.  */
1167       tm1 = localtime (&t);
1168       if (!tm1)
1169         return false;
1170     }
1171
1172   return ! ((tm0->tm_sec ^ tm1->tm_sec)
1173             | (tm0->tm_min ^ tm1->tm_min)
1174             | (tm0->tm_hour ^ tm1->tm_hour)
1175             | (tm0->tm_mday ^ tm1->tm_mday)
1176             | (tm0->tm_mon ^ tm1->tm_mon)
1177             | (tm0->tm_year ^ tm1->tm_year));
1178 }
1179
1180 /* A reasonable upper bound for the size of ordinary TZ strings.
1181    Use heap allocation if TZ's length exceeds this.  */
1182 enum { TZBUFSIZE = 100 };
1183
1184 /* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
1185    otherwise.  */
1186 static char *
1187 get_tz (char tzbuf[TZBUFSIZE])
1188 {
1189   char *tz = getenv ("TZ");
1190   if (tz)
1191     {
1192       size_t tzsize = strlen (tz) + 1;
1193       tz = (tzsize <= TZBUFSIZE
1194             ? memcpy (tzbuf, tz, tzsize)
1195             : xmemdup (tz, tzsize));
1196     }
1197   return tz;
1198 }
1199
1200 /* Parse a date/time string, storing the resulting time value into *RESULT.
1201    The string itself is pointed to by P.  Return true if successful.
1202    P can be an incomplete or relative time specification; if so, use
1203    *NOW as the basis for the returned time.  */
1204 bool
1205 get_date (struct timespec *result, char const *p, struct timespec const *now)
1206 {
1207   time_t Start;
1208   long int Start_ns;
1209   struct tm const *tmp;
1210   struct tm tm;
1211   struct tm tm0;
1212   parser_control pc;
1213   struct timespec gettime_buffer;
1214   unsigned char c;
1215   bool tz_was_altered = false;
1216   char *tz0 = NULL;
1217   char tz0buf[TZBUFSIZE];
1218   bool ok = true;
1219
1220   if (! now)
1221     {
1222       gettime (&gettime_buffer);
1223       now = &gettime_buffer;
1224     }
1225
1226   Start = now->tv_sec;
1227   Start_ns = now->tv_nsec;
1228
1229   tmp = localtime (&now->tv_sec);
1230   if (! tmp)
1231     return false;
1232
1233   while (c = *p, c_isspace (c))
1234     p++;
1235
1236   if (strncmp (p, "TZ=\"", 4) == 0)
1237     {
1238       char const *tzbase = p + 4;
1239       size_t tzsize = 1;
1240       char const *s;
1241
1242       for (s = tzbase; *s; s++, tzsize++)
1243         if (*s == '\\')
1244           {
1245             s++;
1246             if (! (*s == '\\' || *s == '"'))
1247               break;
1248           }
1249         else if (*s == '"')
1250           {
1251             char *z;
1252             char *tz1;
1253             char tz1buf[TZBUFSIZE];
1254             bool large_tz = TZBUFSIZE < tzsize;
1255             bool setenv_ok;
1256             tz0 = get_tz (tz0buf);
1257             z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf;
1258             for (s = tzbase; *s != '"'; s++)
1259               *z++ = *(s += *s == '\\');
1260             *z = '\0';
1261             setenv_ok = setenv ("TZ", tz1, 1) == 0;
1262             if (large_tz)
1263               free (tz1);
1264             if (!setenv_ok)
1265               goto fail;
1266             tz_was_altered = true;
1267             p = s + 1;
1268           }
1269     }
1270
1271   /* As documented, be careful to treat the empty string just like
1272      a date string of "0".  Without this, an empty string would be
1273      declared invalid when parsed during a DST transition.  */
1274   if (*p == '\0')
1275     p = "0";
1276
1277   pc.input = p;
1278   pc.year.value = tmp->tm_year;
1279   pc.year.value += TM_YEAR_BASE;
1280   pc.year.digits = 0;
1281   pc.month = tmp->tm_mon + 1;
1282   pc.day = tmp->tm_mday;
1283   pc.hour = tmp->tm_hour;
1284   pc.minutes = tmp->tm_min;
1285   pc.seconds.tv_sec = tmp->tm_sec;
1286   pc.seconds.tv_nsec = Start_ns;
1287   tm.tm_isdst = tmp->tm_isdst;
1288
1289   pc.meridian = MER24;
1290   pc.rel = RELATIVE_TIME_0;
1291   pc.timespec_seen = false;
1292   pc.rels_seen = false;
1293   pc.dates_seen = 0;
1294   pc.days_seen = 0;
1295   pc.times_seen = 0;
1296   pc.local_zones_seen = 0;
1297   pc.dsts_seen = 0;
1298   pc.zones_seen = 0;
1299
1300 #if HAVE_STRUCT_TM_TM_ZONE
1301   pc.local_time_zone_table[0].name = tmp->tm_zone;
1302   pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1303   pc.local_time_zone_table[0].value = tmp->tm_isdst;
1304   pc.local_time_zone_table[1].name = NULL;
1305
1306   /* Probe the names used in the next three calendar quarters, looking
1307      for a tm_isdst different from the one we already have.  */
1308   {
1309     int quarter;
1310     for (quarter = 1; quarter <= 3; quarter++)
1311       {
1312         time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1313         struct tm const *probe_tm = localtime (&probe);
1314         if (probe_tm && probe_tm->tm_zone
1315             && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1316           {
1317               {
1318                 pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1319                 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1320                 pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1321                 pc.local_time_zone_table[2].name = NULL;
1322               }
1323             break;
1324           }
1325       }
1326   }
1327 #else
1328 #if HAVE_TZNAME
1329   {
1330 # if !HAVE_DECL_TZNAME
1331     extern char *tzname[];
1332 # endif
1333     int i;
1334     for (i = 0; i < 2; i++)
1335       {
1336         pc.local_time_zone_table[i].name = tzname[i];
1337         pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1338         pc.local_time_zone_table[i].value = i;
1339       }
1340     pc.local_time_zone_table[i].name = NULL;
1341   }
1342 #else
1343   pc.local_time_zone_table[0].name = NULL;
1344 #endif
1345 #endif
1346
1347   if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1348       && ! strcmp (pc.local_time_zone_table[0].name,
1349                    pc.local_time_zone_table[1].name))
1350     {
1351       /* This locale uses the same abbrevation for standard and
1352          daylight times.  So if we see that abbreviation, we don't
1353          know whether it's daylight time.  */
1354       pc.local_time_zone_table[0].value = -1;
1355       pc.local_time_zone_table[1].name = NULL;
1356     }
1357
1358   if (yyparse (&pc) != 0)
1359     goto fail;
1360
1361   if (pc.timespec_seen)
1362     *result = pc.seconds;
1363   else
1364     {
1365       if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1366                | (pc.local_zones_seen + pc.zones_seen)))
1367         goto fail;
1368
1369       tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
1370       tm.tm_mon = pc.month - 1;
1371       tm.tm_mday = pc.day;
1372       if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1373         {
1374           tm.tm_hour = to_hour (pc.hour, pc.meridian);
1375           if (tm.tm_hour < 0)
1376             goto fail;
1377           tm.tm_min = pc.minutes;
1378           tm.tm_sec = pc.seconds.tv_sec;
1379         }
1380       else
1381         {
1382           tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1383           pc.seconds.tv_nsec = 0;
1384         }
1385
1386       /* Let mktime deduce tm_isdst if we have an absolute time stamp.  */
1387       if (pc.dates_seen | pc.days_seen | pc.times_seen)
1388         tm.tm_isdst = -1;
1389
1390       /* But if the input explicitly specifies local time with or without
1391          DST, give mktime that information.  */
1392       if (pc.local_zones_seen)
1393         tm.tm_isdst = pc.local_isdst;
1394
1395       tm0 = tm;
1396
1397       Start = mktime (&tm);
1398
1399       if (! mktime_ok (&tm0, &tm, Start))
1400         {
1401           if (! pc.zones_seen)
1402             goto fail;
1403           else
1404             {
1405               /* Guard against falsely reporting errors near the time_t
1406                  boundaries when parsing times in other time zones.  For
1407                  example, suppose the input string "1969-12-31 23:00:00 -0100",
1408                  the current time zone is 8 hours ahead of UTC, and the min
1409                  time_t value is 1970-01-01 00:00:00 UTC.  Then the min
1410                  localtime value is 1970-01-01 08:00:00, and mktime will
1411                  therefore fail on 1969-12-31 23:00:00.  To work around the
1412                  problem, set the time zone to 1 hour behind UTC temporarily
1413                  by setting TZ="XXX1:00" and try mktime again.  */
1414
1415               long int time_zone = pc.time_zone;
1416               long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
1417               long int abs_time_zone_hour = abs_time_zone / 60;
1418               int abs_time_zone_min = abs_time_zone % 60;
1419               char tz1buf[sizeof "XXX+0:00"
1420                           + sizeof pc.time_zone * CHAR_BIT / 3];
1421               if (!tz_was_altered)
1422                 tz0 = get_tz (tz0buf);
1423               sprintf (tz1buf, "XXX%s%ld:%02d", "-" + (time_zone < 0),
1424                        abs_time_zone_hour, abs_time_zone_min);
1425               if (setenv ("TZ", tz1buf, 1) != 0)
1426                 goto fail;
1427               tz_was_altered = true;
1428               tm = tm0;
1429               Start = mktime (&tm);
1430               if (! mktime_ok (&tm0, &tm, Start))
1431                 goto fail;
1432             }
1433         }
1434
1435       if (pc.days_seen && ! pc.dates_seen)
1436         {
1437           tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1438                          + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
1439           tm.tm_isdst = -1;
1440           Start = mktime (&tm);
1441           if (Start == (time_t) -1)
1442             goto fail;
1443         }
1444
1445       /* Add relative date.  */
1446       if (pc.rel.year | pc.rel.month | pc.rel.day)
1447         {
1448           int year = tm.tm_year + pc.rel.year;
1449           int month = tm.tm_mon + pc.rel.month;
1450           int day = tm.tm_mday + pc.rel.day;
1451           if (((year < tm.tm_year) ^ (pc.rel.year < 0))
1452               | ((month < tm.tm_mon) ^ (pc.rel.month < 0))
1453               | ((day < tm.tm_mday) ^ (pc.rel.day < 0)))
1454             goto fail;
1455           tm.tm_year = year;
1456           tm.tm_mon = month;
1457           tm.tm_mday = day;
1458           tm.tm_hour = tm0.tm_hour;
1459           tm.tm_min = tm0.tm_min;
1460           tm.tm_sec = tm0.tm_sec;
1461           tm.tm_isdst = tm0.tm_isdst;
1462           Start = mktime (&tm);
1463           if (Start == (time_t) -1)
1464             goto fail;
1465         }
1466
1467       /* The only "output" of this if-block is an updated Start value,
1468          so this block must follow others that clobber Start.  */
1469       if (pc.zones_seen)
1470         {
1471           long int delta = pc.time_zone * 60;
1472           time_t t1;
1473 #ifdef HAVE_TM_GMTOFF
1474           delta -= tm.tm_gmtoff;
1475 #else
1476           time_t t = Start;
1477           struct tm const *gmt = gmtime (&t);
1478           if (! gmt)
1479             goto fail;
1480           delta -= tm_diff (&tm, gmt);
1481 #endif
1482           t1 = Start - delta;
1483           if ((Start < t1) != (delta < 0))
1484             goto fail;  /* time_t overflow */
1485           Start = t1;
1486         }
1487
1488       /* Add relative hours, minutes, and seconds.  On hosts that support
1489          leap seconds, ignore the possibility of leap seconds; e.g.,
1490          "+ 10 minutes" adds 600 seconds, even if one of them is a
1491          leap second.  Typically this is not what the user wants, but it's
1492          too hard to do it the other way, because the time zone indicator
1493          must be applied before relative times, and if mktime is applied
1494          again the time zone will be lost.  */
1495       {
1496         long int sum_ns = pc.seconds.tv_nsec + pc.rel.ns;
1497         long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
1498         time_t t0 = Start;
1499         long int d1 = 60 * 60 * pc.rel.hour;
1500         time_t t1 = t0 + d1;
1501         long int d2 = 60 * pc.rel.minutes;
1502         time_t t2 = t1 + d2;
1503         long int d3 = pc.rel.seconds;
1504         time_t t3 = t2 + d3;
1505         long int d4 = (sum_ns - normalized_ns) / BILLION;
1506         time_t t4 = t3 + d4;
1507
1508         if ((d1 / (60 * 60) ^ pc.rel.hour)
1509             | (d2 / 60 ^ pc.rel.minutes)
1510             | ((t1 < t0) ^ (d1 < 0))
1511             | ((t2 < t1) ^ (d2 < 0))
1512             | ((t3 < t2) ^ (d3 < 0))
1513             | ((t4 < t3) ^ (d4 < 0)))
1514           goto fail;
1515
1516         result->tv_sec = t4;
1517         result->tv_nsec = normalized_ns;
1518       }
1519     }
1520
1521   goto done;
1522
1523  fail:
1524   ok = false;
1525  done:
1526   if (tz_was_altered)
1527     ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
1528   if (tz0 != tz0buf)
1529     free (tz0);
1530   return ok;
1531 }
1532
1533 #if TEST
1534
1535 int
1536 main (int ac, char **av)
1537 {
1538   char buff[BUFSIZ];
1539
1540   printf ("Enter date, or blank line to exit.\n\t> ");
1541   fflush (stdout);
1542
1543   buff[BUFSIZ - 1] = '\0';
1544   while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1545     {
1546       struct timespec d;
1547       struct tm const *tm;
1548       if (! get_date (&d, buff, NULL))
1549         printf ("Bad format - couldn't convert.\n");
1550       else if (! (tm = localtime (&d.tv_sec)))
1551         {
1552           long int sec = d.tv_sec;
1553           printf ("localtime (%ld) failed\n", sec);
1554         }
1555       else
1556         {
1557           int ns = d.tv_nsec;
1558           printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
1559                   tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
1560                   tm->tm_hour, tm->tm_min, tm->tm_sec, ns);
1561         }
1562       printf ("\t> ");
1563       fflush (stdout);
1564     }
1565   return 0;
1566 }
1567 #endif /* TEST */