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