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