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