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