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