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