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