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