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