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