revert MirBSD patch in order to be able to build PDF docs
[alioth/cvs.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 #ifdef USE_LIBBSD
42 size_t strlcat(char *, const char *, size_t);
43 #endif
44
45 /* There's no need to extend the stack, so there's no need to involve
46    alloca.  */
47 #define YYSTACK_USE_ALLOCA 0
48
49 /* Tell Bison how much stack space is needed.  20 should be plenty for
50    this grammar, which is not right recursive.  Beware setting it too
51    high, since that might cause problems on machines whose
52    implementations have lame stack-overflow checking.  */
53 #define YYMAXDEPTH 20
54 #define YYINITDEPTH YYMAXDEPTH
55
56 /* Since the code of getdate.y is not included in the Emacs executable
57    itself, there is no need to #define static in this file.  Even if
58    the code were included in the Emacs executable, it probably
59    wouldn't do any harm to #undef it here; this will only cause
60    problems if we try to write to a static variable, which I don't
61    think this code needs to do.  */
62 #ifdef emacs
63 # undef static
64 #endif
65
66 #include <ctype.h>
67 #include <limits.h>
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <string.h>
71
72 #ifndef IN_RCS
73 #include "setenv.h"
74 #include "xalloc.h"
75 #else /* IN_RCS */
76 #include <unistd.h>
77
78 void *
79 xmalloc(size_t s)
80 {
81         static const char xmalloc_enomem[] = "memory exhausted\n";
82         void *x;
83
84         if ((x = malloc(s)) == NULL) {
85                 write(2, xmalloc_enomem, sizeof(xmalloc_enomem) - 1);
86                 exit(1);
87         }
88
89         return (x);
90 }
91
92 void *
93 xmemdup(void const *p, size_t s)
94 {
95         return (memcpy(xmalloc(s), p, s));
96 }
97 #endif /* IN_RCS */
98
99 #if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII)
100 # define IN_CTYPE_DOMAIN(c) 1
101 #else
102 # define IN_CTYPE_DOMAIN(c) isascii (c)
103 #endif
104
105 #define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
106 #define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
107 #define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
108
109 /* ISDIGIT differs from isdigit, as follows:
110    - Its arg may be any int or unsigned int; it need not be an unsigned char.
111    - It's guaranteed to evaluate its argument exactly once.
112    - It's typically faster.
113    POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
114    isdigit unless it's important to use the locale's definition
115    of `digit' even when the host does not conform to POSIX.  */
116 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
117
118 #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
119 # define __attribute__(x)
120 #endif
121
122 #ifndef ATTRIBUTE_UNUSED
123 # define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
124 #endif
125
126 #ifndef __RCSID
127 #define __RCSID(x) /* nothing */
128 #endif
129
130 __RCSID("$MirOS: src/gnu/usr.bin/cvs/lib/getdate.y,v 1.6.2.7 2016/10/21 20:58:03 tg Exp $");
131 /* placeholder line for $miros$ so that cpp #line directives work */
132
133 /* Shift A right by B bits portably, by dividing A by 2**B and
134    truncating towards minus infinity.  A and B should be free of side
135    effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
136    INT_BITS is the number of useful bits in an int.  GNU code can
137    assume that INT_BITS is at least 32.
138
139    ISO C99 says that A >> B is implementation-defined if A < 0.  Some
140    implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
141    right in the usual way when A < 0, so SHR falls back on division if
142    ordinary A >> B doesn't seem to be the usual signed shift.  */
143 #define SHR(a, b)       \
144   (-1 >> 1 == -1        \
145    ? (a) >> (b)         \
146    : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
147
148 #define EPOCH_YEAR 1970
149 #define TM_YEAR_BASE 1900
150
151 #define HOUR(x) ((x) * 60)
152
153 /* An integer value, and the number of digits in its textual
154    representation.  */
155 typedef struct
156 {
157   bool negative;
158   long int value;
159   size_t digits;
160 } textint;
161
162 /* An entry in the lexical lookup table.  */
163 typedef struct
164 {
165   char const *name;
166   int type;
167   int value;
168 } table;
169
170 /* Meridian: am, pm, or 24-hour style.  */
171 enum { MERam, MERpm, MER24 };
172
173 enum { BILLION = 1000000000, LOG10_BILLION = 9 };
174
175 /* Information passed to and from the parser.  */
176 typedef struct
177 {
178   /* The input string remaining to be parsed. */
179   const char *input;
180
181   /* N, if this is the Nth Tuesday.  */
182   long int day_ordinal;
183
184   /* Day of week; Sunday is 0.  */
185   int day_number;
186
187   /* tm_isdst flag for the local zone.  */
188   int local_isdst;
189
190   /* Time zone, in minutes east of UTC.  */
191   long int time_zone;
192
193   /* Style used for time.  */
194   int meridian;
195
196   /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds.  */
197   textint year;
198   long int month;
199   long int day;
200   long int hour;
201   long int minutes;
202   struct timespec seconds; /* includes nanoseconds */
203
204   /* Relative year, month, day, hour, minutes, seconds, and nanoseconds.  */
205   long int rel_year;
206   long int rel_month;
207   long int rel_day;
208   long int rel_hour;
209   long int rel_minutes;
210   long int rel_seconds;
211   long int rel_ns;
212
213   /* Presence or counts of nonterminals of various flavors parsed so far.  */
214   bool timespec_seen;
215   bool rels_seen;
216   size_t dates_seen;
217   size_t days_seen;
218   size_t local_zones_seen;
219   size_t dsts_seen;
220   size_t times_seen;
221   size_t zones_seen;
222
223   /* Table of local time zone abbrevations, terminated by a null entry.  */
224   table local_time_zone_table[3];
225 } parser_control;
226
227 union YYSTYPE;
228 static int yylex (union YYSTYPE *, parser_control *);
229 static int yyerror (parser_control *, char *);
230 static long int time_zone_hhmm (textint, long int);
231
232 %}
233
234 /* We want a reentrant parser, even if the TZ manipulation and the calls to
235    localtime and gmtime are not reentrant.  */
236 %pure-parser
237 %parse-param { parser_control *pc }
238 %lex-param { parser_control *pc }
239
240 /* This grammar has 20 shift/reduce conflicts. */
241 %expect 20
242
243 %union
244 {
245   long int intval;
246   textint textintval;
247   struct timespec timespec;
248 }
249
250 %token tAGO tDST
251
252 %token <intval> tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tLOCAL_ZONE tMERIDIAN
253 %token <intval> tMINUTE_UNIT tMONTH tMONTH_UNIT tORDINAL
254 %token <intval> tSEC_UNIT tYEAR_UNIT tZONE
255
256 %token <textintval> tSNUMBER tUNUMBER
257 %token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER
258
259 %type <intval> o_colon_minutes o_merid
260 %type <timespec> seconds signed_seconds unsigned_seconds
261
262 %%
263
264 spec:
265     timespec
266   | items
267   ;
268
269 timespec:
270     '@' seconds
271       {
272         pc->seconds = $2;
273         pc->timespec_seen = true;
274       }
275   ;
276
277 items:
278     /* empty */
279   | items item
280   ;
281
282 item:
283     time
284       { pc->times_seen++; }
285   | local_zone
286       { pc->local_zones_seen++; }
287   | zone
288       { pc->zones_seen++; }
289   | date
290       { pc->dates_seen++; }
291   | day
292       { pc->days_seen++; }
293   | rel
294       { pc->rels_seen = true; }
295   | number
296   ;
297
298 time:
299     tUNUMBER tMERIDIAN
300       {
301         pc->hour = $1.value;
302         pc->minutes = 0;
303         pc->seconds.tv_sec = 0;
304         pc->seconds.tv_nsec = 0;
305         pc->meridian = $2;
306       }
307   | tUNUMBER ':' tUNUMBER o_merid
308       {
309         pc->hour = $1.value;
310         pc->minutes = $3.value;
311         pc->seconds.tv_sec = 0;
312         pc->seconds.tv_nsec = 0;
313         pc->meridian = $4;
314       }
315   | tUNUMBER ':' tUNUMBER tSNUMBER o_colon_minutes
316       {
317         pc->hour = $1.value;
318         pc->minutes = $3.value;
319         pc->seconds.tv_sec = 0;
320         pc->seconds.tv_nsec = 0;
321         pc->meridian = MER24;
322         pc->zones_seen++;
323         pc->time_zone = time_zone_hhmm ($4, $5);
324       }
325   | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_merid
326       {
327         pc->hour = $1.value;
328         pc->minutes = $3.value;
329         pc->seconds = $5;
330         pc->meridian = $6;
331       }
332   | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER o_colon_minutes
333       {
334         pc->hour = $1.value;
335         pc->minutes = $3.value;
336         pc->seconds = $5;
337         pc->meridian = MER24;
338         pc->zones_seen++;
339         pc->time_zone = time_zone_hhmm ($6, $7);
340       }
341   ;
342
343 local_zone:
344     tLOCAL_ZONE
345       {
346         pc->local_isdst = $1;
347         pc->dsts_seen += (0 < $1);
348       }
349   | tLOCAL_ZONE tDST
350       {
351         pc->local_isdst = 1;
352         pc->dsts_seen += (0 < $1) + 1;
353       }
354   ;
355
356 zone:
357     tZONE
358       { pc->time_zone = $1; }
359   | tZONE relunit_snumber
360       { pc->time_zone = $1; pc->rels_seen = true; }
361   | tZONE tSNUMBER o_colon_minutes
362       { pc->time_zone = $1 + time_zone_hhmm ($2, $3); }
363   | tDAYZONE
364       { pc->time_zone = $1 + 60; }
365   | tZONE tDST
366       { pc->time_zone = $1 + 60; }
367   ;
368
369 day:
370     tDAY
371       {
372         pc->day_ordinal = 1;
373         pc->day_number = $1;
374       }
375   | tDAY ','
376       {
377         pc->day_ordinal = 1;
378         pc->day_number = $1;
379       }
380   | tORDINAL tDAY
381       {
382         pc->day_ordinal = $1;
383         pc->day_number = $2;
384       }
385   | tUNUMBER tDAY
386       {
387         pc->day_ordinal = $1.value;
388         pc->day_number = $2;
389       }
390   ;
391
392 date:
393     tUNUMBER '/' tUNUMBER
394       {
395         pc->month = $1.value;
396         pc->day = $3.value;
397       }
398   | tUNUMBER '/' tUNUMBER '/' tUNUMBER
399       {
400         /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
401            otherwise as MM/DD/YY.
402            The goal in recognizing YYYY/MM/DD is solely to support legacy
403            machine-generated dates like those in an RCS log listing.  If
404            you want portability, use the ISO 8601 format.  */
405         if (4 <= $1.digits)
406           {
407             pc->year = $1;
408             pc->month = $3.value;
409             pc->day = $5.value;
410           }
411         else
412           {
413             pc->month = $1.value;
414             pc->day = $3.value;
415             pc->year = $5;
416           }
417       }
418   | tUNUMBER tSNUMBER tSNUMBER
419       {
420         /* ISO 8601 format.  YYYY-MM-DD.  */
421         pc->year = $1;
422         pc->month = -$2.value;
423         pc->day = -$3.value;
424       }
425   | tUNUMBER tMONTH tSNUMBER
426       {
427         /* e.g. 17-JUN-1992.  */
428         pc->day = $1.value;
429         pc->month = $2;
430         pc->year.value = -$3.value;
431         pc->year.digits = $3.digits;
432       }
433   | tMONTH tSNUMBER tSNUMBER
434       {
435         /* e.g. JUN-17-1992.  */
436         pc->month = $1;
437         pc->day = -$2.value;
438         pc->year.value = -$3.value;
439         pc->year.digits = $3.digits;
440       }
441   | tMONTH tUNUMBER
442       {
443         pc->month = $1;
444         pc->day = $2.value;
445       }
446   | tMONTH tUNUMBER ',' tUNUMBER
447       {
448         pc->month = $1;
449         pc->day = $2.value;
450         pc->year = $4;
451       }
452   | tUNUMBER tMONTH
453       {
454         pc->day = $1.value;
455         pc->month = $2;
456       }
457   | tUNUMBER tMONTH tUNUMBER
458       {
459         pc->day = $1.value;
460         pc->month = $2;
461         pc->year = $3;
462       }
463   ;
464
465 rel:
466     relunit tAGO
467       {
468         pc->rel_ns = -pc->rel_ns;
469         pc->rel_seconds = -pc->rel_seconds;
470         pc->rel_minutes = -pc->rel_minutes;
471         pc->rel_hour = -pc->rel_hour;
472         pc->rel_day = -pc->rel_day;
473         pc->rel_month = -pc->rel_month;
474         pc->rel_year = -pc->rel_year;
475       }
476   | relunit
477   ;
478
479 relunit:
480     tORDINAL tYEAR_UNIT
481       { pc->rel_year += $1 * $2; }
482   | tUNUMBER tYEAR_UNIT
483       { pc->rel_year += $1.value * $2; }
484   | tYEAR_UNIT
485       { pc->rel_year += $1; }
486   | tORDINAL tMONTH_UNIT
487       { pc->rel_month += $1 * $2; }
488   | tUNUMBER tMONTH_UNIT
489       { pc->rel_month += $1.value * $2; }
490   | tMONTH_UNIT
491       { pc->rel_month += $1; }
492   | tORDINAL tDAY_UNIT
493       { pc->rel_day += $1 * $2; }
494   | tUNUMBER tDAY_UNIT
495       { pc->rel_day += $1.value * $2; }
496   | tDAY_UNIT
497       { pc->rel_day += $1; }
498   | tORDINAL tHOUR_UNIT
499       { pc->rel_hour += $1 * $2; }
500   | tUNUMBER tHOUR_UNIT
501       { pc->rel_hour += $1.value * $2; }
502   | tHOUR_UNIT
503       { pc->rel_hour += $1; }
504   | tORDINAL tMINUTE_UNIT
505       { pc->rel_minutes += $1 * $2; }
506   | tUNUMBER tMINUTE_UNIT
507       { pc->rel_minutes += $1.value * $2; }
508   | tMINUTE_UNIT
509       { pc->rel_minutes += $1; }
510   | tORDINAL tSEC_UNIT
511       { pc->rel_seconds += $1 * $2; }
512   | tUNUMBER tSEC_UNIT
513       { pc->rel_seconds += $1.value * $2; }
514   | tSDECIMAL_NUMBER tSEC_UNIT
515       { pc->rel_seconds += $1.tv_sec * $2; pc->rel_ns += $1.tv_nsec * $2; }
516   | tUDECIMAL_NUMBER tSEC_UNIT
517       { pc->rel_seconds += $1.tv_sec * $2; pc->rel_ns += $1.tv_nsec * $2; }
518   | tSEC_UNIT
519       { pc->rel_seconds += $1; }
520   | relunit_snumber
521   ;
522
523 relunit_snumber:
524     tSNUMBER tYEAR_UNIT
525       { pc->rel_year += $1.value * $2; }
526   | tSNUMBER tMONTH_UNIT
527       { pc->rel_month += $1.value * $2; }
528   | tSNUMBER tDAY_UNIT
529       { pc->rel_day += $1.value * $2; }
530   | tSNUMBER tHOUR_UNIT
531       { pc->rel_hour += $1.value * $2; }
532   | tSNUMBER tMINUTE_UNIT
533       { pc->rel_minutes += $1.value * $2; }
534   | tSNUMBER tSEC_UNIT
535       { pc->rel_seconds += $1.value * $2; }
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_ns = 0;
1254   pc.rel_seconds = 0;
1255   pc.rel_minutes = 0;
1256   pc.rel_hour = 0;
1257   pc.rel_day = 0;
1258   pc.rel_month = 0;
1259   pc.rel_year = 0;
1260   pc.timespec_seen = false;
1261   pc.rels_seen = false;
1262   pc.dates_seen = 0;
1263   pc.days_seen = 0;
1264   pc.times_seen = 0;
1265   pc.local_zones_seen = 0;
1266   pc.dsts_seen = 0;
1267   pc.zones_seen = 0;
1268
1269 #if HAVE_STRUCT_TM_TM_ZONE
1270   pc.local_time_zone_table[0].name = tmp->tm_zone;
1271   pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1272   pc.local_time_zone_table[0].value = tmp->tm_isdst;
1273   pc.local_time_zone_table[1].name = NULL;
1274
1275   /* Probe the names used in the next three calendar quarters, looking
1276      for a tm_isdst different from the one we already have.  */
1277   {
1278     int quarter;
1279     for (quarter = 1; quarter <= 3; quarter++)
1280       {
1281         time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1282         struct tm const *probe_tm = localtime (&probe);
1283         if (probe_tm && probe_tm->tm_zone
1284             && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1285           {
1286               {
1287                 pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1288                 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1289                 pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1290                 pc.local_time_zone_table[2].name = NULL;
1291               }
1292             break;
1293           }
1294       }
1295   }
1296 #else
1297 #if HAVE_TZNAME
1298   {
1299 # ifndef tzname
1300     extern char *tzname[];
1301 # endif
1302     int i;
1303     for (i = 0; i < 2; i++)
1304       {
1305         pc.local_time_zone_table[i].name = tzname[i];
1306         pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1307         pc.local_time_zone_table[i].value = i;
1308       }
1309     pc.local_time_zone_table[i].name = NULL;
1310   }
1311 #else
1312   pc.local_time_zone_table[0].name = NULL;
1313 #endif
1314 #endif
1315
1316   if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1317       && ! strcmp (pc.local_time_zone_table[0].name,
1318                    pc.local_time_zone_table[1].name))
1319     {
1320       /* This locale uses the same abbrevation for standard and
1321          daylight times.  So if we see that abbreviation, we don't
1322          know whether it's daylight time.  */
1323       pc.local_time_zone_table[0].value = -1;
1324       pc.local_time_zone_table[1].name = NULL;
1325     }
1326
1327   if (yyparse (&pc) != 0)
1328     goto fail;
1329
1330   if (pc.timespec_seen)
1331     *result = pc.seconds;
1332   else
1333     {
1334       if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1335                | (pc.local_zones_seen + pc.zones_seen)))
1336         goto fail;
1337
1338       tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
1339       tm.tm_mon = pc.month - 1;
1340       tm.tm_mday = pc.day;
1341       if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1342         {
1343           tm.tm_hour = to_hour (pc.hour, pc.meridian);
1344           if (tm.tm_hour < 0)
1345             goto fail;
1346           tm.tm_min = pc.minutes;
1347           tm.tm_sec = pc.seconds.tv_sec;
1348         }
1349       else
1350         {
1351           tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1352           pc.seconds.tv_nsec = 0;
1353         }
1354
1355       /* Let mktime deduce tm_isdst if we have an absolute time stamp.  */
1356       if (!pc.rels_seen)
1357         tm.tm_isdst = -1;
1358
1359       /* But if the input explicitly specifies local time with or without
1360          DST, give mktime that information.  */
1361       if (pc.local_zones_seen)
1362         tm.tm_isdst = pc.local_isdst;
1363
1364       tm0 = tm;
1365
1366       Start = mktime (&tm);
1367
1368       if (! mktime_ok (&tm0, &tm, Start))
1369         {
1370           if (! pc.zones_seen)
1371             goto fail;
1372           else
1373             {
1374               /* Guard against falsely reporting errors near the time_t
1375                  boundaries when parsing times in other time zones.  For
1376                  example, suppose the input string "1969-12-31 23:00:00 -0100",
1377                  the current time zone is 8 hours ahead of UTC, and the min
1378                  time_t value is 1970-01-01 00:00:00 UTC.  Then the min
1379                  localtime value is 1970-01-01 08:00:00, and mktime will
1380                  therefore fail on 1969-12-31 23:00:00.  To work around the
1381                  problem, set the time zone to 1 hour behind UTC temporarily
1382                  by setting TZ="XXX1:00" and try mktime again.  */
1383
1384               long int time_zone = pc.time_zone;
1385               long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
1386               long int abs_time_zone_hour = abs_time_zone / 60;
1387               int abs_time_zone_min = abs_time_zone % 60;
1388               char tz1buf[sizeof "XXX+0:00"
1389                           + sizeof pc.time_zone * CHAR_BIT / 3];
1390               if (!tz_was_altered)
1391                 tz0 = get_tz (tz0buf);
1392               snprintf(tz1buf, sizeof(tz1buf),
1393                        "XXX%s%ld:%02d", "-" + (time_zone < 0),
1394                        abs_time_zone_hour, abs_time_zone_min);
1395               if (setenv ("TZ", tz1buf, 1) != 0)
1396                 goto fail;
1397               tz_was_altered = true;
1398               tm = tm0;
1399               Start = mktime (&tm);
1400               if (! mktime_ok (&tm0, &tm, Start))
1401                 goto fail;
1402             }
1403         }
1404
1405       if (pc.days_seen && ! pc.dates_seen)
1406         {
1407           tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1408                          + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
1409           tm.tm_isdst = -1;
1410           Start = mktime (&tm);
1411           if (Start == (time_t) -1)
1412             goto fail;
1413         }
1414
1415       if (pc.zones_seen)
1416         {
1417           long int delta = pc.time_zone * 60;
1418           time_t t1;
1419 #ifdef HAVE_TM_GMTOFF
1420           delta -= tm.tm_gmtoff;
1421 #else
1422           time_t t = Start;
1423           struct tm const *gmt = gmtime (&t);
1424           if (! gmt)
1425             goto fail;
1426           delta -= tm_diff (&tm, gmt);
1427 #endif
1428           t1 = Start - delta;
1429           if ((Start < t1) != (delta < 0))
1430             goto fail;  /* time_t overflow */
1431           Start = t1;
1432         }
1433
1434       /* Add relative date.  */
1435       if (pc.rel_year | pc.rel_month | pc.rel_day)
1436         {
1437           int year = tm.tm_year + pc.rel_year;
1438           int month = tm.tm_mon + pc.rel_month;
1439           int day = tm.tm_mday + pc.rel_day;
1440           if (((year < tm.tm_year) ^ (pc.rel_year < 0))
1441               | ((month < tm.tm_mon) ^ (pc.rel_month < 0))
1442               | ((day < tm.tm_mday) ^ (pc.rel_day < 0)))
1443             goto fail;
1444           tm.tm_year = year;
1445           tm.tm_mon = month;
1446           tm.tm_mday = day;
1447           Start = mktime (&tm);
1448           if (Start == (time_t) -1)
1449             goto fail;
1450         }
1451
1452       /* Add relative hours, minutes, and seconds.  On hosts that support
1453          leap seconds, ignore the possibility of leap seconds; e.g.,
1454          "+ 10 minutes" adds 600 seconds, even if one of them is a
1455          leap second.  Typically this is not what the user wants, but it's
1456          too hard to do it the other way, because the time zone indicator
1457          must be applied before relative times, and if mktime is applied
1458          again the time zone will be lost.  */
1459       {
1460         long int sum_ns = pc.seconds.tv_nsec + pc.rel_ns;
1461         long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
1462         time_t t0 = Start;
1463         long int d1 = 60 * 60 * pc.rel_hour;
1464         time_t t1 = t0 + d1;
1465         long int d2 = 60 * pc.rel_minutes;
1466         time_t t2 = t1 + d2;
1467         long int d3 = pc.rel_seconds;
1468         time_t t3 = t2 + d3;
1469         long int d4 = (sum_ns - normalized_ns) / BILLION;
1470         time_t t4 = t3 + d4;
1471
1472         if ((d1 / (60 * 60) ^ pc.rel_hour)
1473             | (d2 / 60 ^ pc.rel_minutes)
1474             | ((t1 < t0) ^ (d1 < 0))
1475             | ((t2 < t1) ^ (d2 < 0))
1476             | ((t3 < t2) ^ (d3 < 0))
1477             | ((t4 < t3) ^ (d4 < 0)))
1478           goto fail;
1479
1480         result->tv_sec = t4;
1481         result->tv_nsec = normalized_ns;
1482       }
1483     }
1484
1485   goto done;
1486
1487  fail:
1488   ok = false;
1489  done:
1490   if (tz_was_altered)
1491     ok &= (tz0 ? setenv("TZ", tz0, 1) : (unsetenv("TZ"), 0)) == 0;
1492   if (tz0 != tz0buf)
1493     free (tz0);
1494   return ok;
1495 }
1496
1497 #if TEST
1498
1499 int
1500 main(int argc, char **argv)
1501 {
1502   char buff[BUFSIZ];
1503   int cmd = 0;
1504
1505   if (argc > 1) {
1506     int i = 1;
1507     buff[0] = '\0';
1508     while (i < argc) {
1509       if (i > 1)
1510         strlcat(buff, " ", BUFSIZ);
1511       strlcat(buff, argv[i++], BUFSIZ);
1512     }
1513     cmd++;
1514     goto once;
1515   }
1516
1517   printf("Enter date, or blank line to exit.\n> ");
1518   fflush (stdout);
1519
1520   buff[BUFSIZ - 1] = '\0';
1521   while (fgets(buff, BUFSIZ - 1, stdin) && buff[0] &&
1522          buff[0] != '\r' && buff[0] != '\n')
1523     {
1524       struct timespec d;
1525       struct tm const *tm;
1526  once:
1527       if (! get_date (&d, buff, NULL))
1528         printf ("Bad format - couldn't convert.\n");
1529       else if (! (tm = localtime (&d.tv_sec)))
1530         {
1531           printf ("localtime (%lld) failed\n", (long long)d.tv_sec);
1532         }
1533       else
1534         {
1535           int ns = d.tv_nsec;
1536           printf ("%13lld =\t%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
1537                   (long long)d.tv_sec, (long)tm->tm_year + 1900,
1538                   tm->tm_mon + 1, tm->tm_mday,
1539                   tm->tm_hour, tm->tm_min, tm->tm_sec, ns);
1540         }
1541       if (cmd)
1542         return 0;
1543       printf ("> ");
1544       fflush (stdout);
1545     }
1546   return 0;
1547 }
1548 #endif /* TEST */