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