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