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