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