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