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