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