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