1 %{
2 /* Parse a string into an internal timestamp.
3 
4    Copyright (C) 1999-2000, 2002-2020 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 <https://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 Assaf Gordon <assafgordon@gmail.com> in 2016 to add
25    debug output.
26 
27    Modified by Paul Eggert <eggert@twinsun.com> in 1999 to do the
28    right thing about local DST.  Also modified by Paul Eggert
29    <eggert@cs.ucla.edu> in 2004 to support nanosecond-resolution
30    timestamps, in 2004 to support TZ strings in dates, and in 2017 to
31    check for integer overflow and to support longer-than-'long'
32    'time_t' and 'tv_nsec'.  */
33 
34 #include <config.h>
35 
36 #include "parse-datetime.h"
37 
38 #include "intprops.h"
39 #include "timespec.h"
40 #include "verify.h"
41 #include "strftime.h"
42 
43 /* There's no need to extend the stack, so there's no need to involve
44    alloca.  */
45 #define YYSTACK_USE_ALLOCA 0
46 
47 /* Tell Bison how much stack space is needed.  20 should be plenty for
48    this grammar, which is not right recursive.  Beware setting it too
49    high, since that might cause problems on machines whose
50    implementations have lame stack-overflow checking.  */
51 #define YYMAXDEPTH 20
52 #define YYINITDEPTH YYMAXDEPTH
53 
54 /* Since the code of parse-datetime.y is not included in the Emacs executable
55    itself, there is no need to #define static in this file.  Even if
56    the code were included in the Emacs executable, it probably
57    wouldn't do any harm to #undef it here; this will only cause
58    problems if we try to write to a static variable, which I don't
59    think this code needs to do.  */
60 #ifdef emacs
61 # undef static
62 #endif
63 
64 #include <inttypes.h>
65 #include <c-ctype.h>
66 #include <limits.h>
67 #include <stdarg.h>
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <string.h>
71 
72 #include "gettext.h"
73 
74 #define _(str) gettext (str)
75 
76 /* Bison's skeleton tests _STDLIB_H, while some stdlib.h headers
77    use _STDLIB_H_ as witness.  Map the latter to the one bison uses.  */
78 /* FIXME: this is temporary.  Remove when we have a mechanism to ensure
79    that the version we're using is fixed, too.  */
80 #ifdef _STDLIB_H_
81 # undef _STDLIB_H
82 # define _STDLIB_H 1
83 #endif
84 
85 /* Shift A right by B bits portably, by dividing A by 2**B and
86    truncating towards minus infinity.  A and B should be free of side
87    effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
88    INT_BITS is the number of useful bits in an int.  GNU code can
89    assume that INT_BITS is at least 32.
90 
91    ISO C99 says that A >> B is implementation-defined if A < 0.  Some
92    implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
93    right in the usual way when A < 0, so SHR falls back on division if
94    ordinary A >> B doesn't seem to be the usual signed shift.  */
95 #define SHR(a, b)       \
96   (-1 >> 1 == -1        \
97    ? (a) >> (b)         \
98    : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
99 
100 #define HOUR(x) (60 * 60 * (x))
101 
102 #define STREQ(a, b) (strcmp (a, b) == 0)
103 
104 /* Verify that time_t is an integer as POSIX requires, and that every
105    time_t value fits in intmax_t.  Please file a bug report if these
106    assumptions are false on your platform.  */
107 verify (TYPE_IS_INTEGER (time_t));
108 verify (!TYPE_SIGNED (time_t) || INTMAX_MIN <= TYPE_MINIMUM (time_t));
109 verify (TYPE_MAXIMUM (time_t) <= INTMAX_MAX);
110 
111 /* True if N is out of range for time_t.  */
112 static bool
time_overflow(intmax_t n)113 time_overflow (intmax_t n)
114 {
115   return ! ((TYPE_SIGNED (time_t) ? TYPE_MINIMUM (time_t) <= n : 0 <= n)
116             && n <= TYPE_MAXIMUM (time_t));
117 }
118 
119 /* Convert a possibly-signed character to an unsigned character.  This is
120    a bit safer than casting to unsigned char, since it catches some type
121    errors that the cast doesn't.  */
to_uchar(char ch)122 static unsigned char to_uchar (char ch) { return ch; }
123 
124 static void _GL_ATTRIBUTE_FORMAT ((__printf__, 1, 2))
dbg_printf(char const * msg,...)125 dbg_printf (char const *msg, ...)
126 {
127   va_list args;
128   /* TODO: use gnulib's 'program_name' instead?  */
129   fputs ("date: ", stderr);
130 
131   va_start (args, msg);
132   vfprintf (stderr, msg, args);
133   va_end (args);
134 }
135 
136 
137 /* An integer value, and the number of digits in its textual
138    representation.  */
139 typedef struct
140 {
141   bool negative;
142   intmax_t value;
143   ptrdiff_t digits;
144 } textint;
145 
146 /* An entry in the lexical lookup table.  */
147 typedef struct
148 {
149   char const *name;
150   int type;
151   int value;
152 } table;
153 
154 /* Meridian: am, pm, or 24-hour style.  */
155 enum { MERam, MERpm, MER24 };
156 
157 /* A reasonable upper bound for the buffer used in debug output.  */
158 enum { DBGBUFSIZE = 100 };
159 
160 enum { BILLION = 1000000000, LOG10_BILLION = 9 };
161 
162 /* Relative times.  */
163 typedef struct
164 {
165   /* Relative year, month, day, hour, minutes, seconds, and nanoseconds.  */
166   intmax_t year;
167   intmax_t month;
168   intmax_t day;
169   intmax_t hour;
170   intmax_t minutes;
171   intmax_t seconds;
172   int ns;
173 } relative_time;
174 
175 #if HAVE_COMPOUND_LITERALS
176 # define RELATIVE_TIME_0 ((relative_time) { 0, 0, 0, 0, 0, 0, 0 })
177 #else
178 static relative_time const RELATIVE_TIME_0;
179 #endif
180 
181 /* Information passed to and from the parser.  */
182 typedef struct
183 {
184   /* The input string remaining to be parsed.  */
185   const char *input;
186 
187   /* N, if this is the Nth Tuesday.  */
188   intmax_t day_ordinal;
189 
190   /* Day of week; Sunday is 0.  */
191   int day_number;
192 
193   /* tm_isdst flag for the local zone.  */
194   int local_isdst;
195 
196   /* Time zone, in seconds east of UT.  */
197   int time_zone;
198 
199   /* Style used for time.  */
200   int meridian;
201 
202   /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds.  */
203   textint year;
204   intmax_t month;
205   intmax_t day;
206   intmax_t hour;
207   intmax_t minutes;
208   struct timespec seconds; /* includes nanoseconds */
209 
210   /* Relative year, month, day, hour, minutes, seconds, and nanoseconds.  */
211   relative_time rel;
212 
213   /* Presence or counts of nonterminals of various flavors parsed so far.  */
214   bool timespec_seen;
215   bool rels_seen;
216   ptrdiff_t dates_seen;
217   ptrdiff_t days_seen;
218   ptrdiff_t local_zones_seen;
219   ptrdiff_t dsts_seen;
220   ptrdiff_t times_seen;
221   ptrdiff_t zones_seen;
222   bool year_seen;
223 
224   /* Print debugging output to stderr.  */
225   bool parse_datetime_debug;
226 
227   /* Which of the 'seen' parts have been printed when debugging.  */
228   bool debug_dates_seen;
229   bool debug_days_seen;
230   bool debug_local_zones_seen;
231   bool debug_times_seen;
232   bool debug_zones_seen;
233   bool debug_year_seen;
234 
235   /* The user specified explicit ordinal day value.  */
236   bool debug_ordinal_day_seen;
237 
238   /* Table of local time zone abbreviations, terminated by a null entry.  */
239   table local_time_zone_table[3];
240 } parser_control;
241 
242 union YYSTYPE;
243 static int yylex (union YYSTYPE *, parser_control *);
244 static int yyerror (parser_control const *, char const *);
245 static bool time_zone_hhmm (parser_control *, textint, intmax_t);
246 
247 /* Extract into *PC any date and time info from a string of digits
248    of the form e.g., YYYYMMDD, YYMMDD, HHMM, HH (and sometimes YYY,
249    YYYY, ...).  */
250 static void
digits_to_date_time(parser_control * pc,textint text_int)251 digits_to_date_time (parser_control *pc, textint text_int)
252 {
253   if (pc->dates_seen && ! pc->year.digits
254       && ! pc->rels_seen && (pc->times_seen || 2 < text_int.digits))
255     {
256       pc->year_seen = true;
257       pc->year = text_int;
258     }
259   else
260     {
261       if (4 < text_int.digits)
262         {
263           pc->dates_seen++;
264           pc->day = text_int.value % 100;
265           pc->month = (text_int.value / 100) % 100;
266           pc->year.value = text_int.value / 10000;
267           pc->year.digits = text_int.digits - 4;
268         }
269       else
270         {
271           pc->times_seen++;
272           if (text_int.digits <= 2)
273             {
274               pc->hour = text_int.value;
275               pc->minutes = 0;
276             }
277           else
278             {
279               pc->hour = text_int.value / 100;
280               pc->minutes = text_int.value % 100;
281             }
282           pc->seconds.tv_sec = 0;
283           pc->seconds.tv_nsec = 0;
284           pc->meridian = MER24;
285         }
286     }
287 }
288 
289 /* Increment PC->rel by FACTOR * REL (FACTOR is 1 or -1).  Return true
290    if successful, false if an overflow occurred.  */
291 static bool
apply_relative_time(parser_control * pc,relative_time rel,int factor)292 apply_relative_time (parser_control *pc, relative_time rel, int factor)
293 {
294   if (factor < 0
295       ? (INT_SUBTRACT_WRAPV (pc->rel.ns, rel.ns, &pc->rel.ns)
296          | INT_SUBTRACT_WRAPV (pc->rel.seconds, rel.seconds, &pc->rel.seconds)
297          | INT_SUBTRACT_WRAPV (pc->rel.minutes, rel.minutes, &pc->rel.minutes)
298          | INT_SUBTRACT_WRAPV (pc->rel.hour, rel.hour, &pc->rel.hour)
299          | INT_SUBTRACT_WRAPV (pc->rel.day, rel.day, &pc->rel.day)
300          | INT_SUBTRACT_WRAPV (pc->rel.month, rel.month, &pc->rel.month)
301          | INT_SUBTRACT_WRAPV (pc->rel.year, rel.year, &pc->rel.year))
302       : (INT_ADD_WRAPV (pc->rel.ns, rel.ns, &pc->rel.ns)
303          | INT_ADD_WRAPV (pc->rel.seconds, rel.seconds, &pc->rel.seconds)
304          | INT_ADD_WRAPV (pc->rel.minutes, rel.minutes, &pc->rel.minutes)
305          | INT_ADD_WRAPV (pc->rel.hour, rel.hour, &pc->rel.hour)
306          | INT_ADD_WRAPV (pc->rel.day, rel.day, &pc->rel.day)
307          | INT_ADD_WRAPV (pc->rel.month, rel.month, &pc->rel.month)
308          | INT_ADD_WRAPV (pc->rel.year, rel.year, &pc->rel.year)))
309     return false;
310   pc->rels_seen = true;
311   return true;
312 }
313 
314 /* Set PC-> hour, minutes, seconds and nanoseconds members from arguments.  */
315 static void
set_hhmmss(parser_control * pc,intmax_t hour,intmax_t minutes,time_t sec,int nsec)316 set_hhmmss (parser_control *pc, intmax_t hour, intmax_t minutes,
317             time_t sec, int nsec)
318 {
319   pc->hour = hour;
320   pc->minutes = minutes;
321   pc->seconds.tv_sec = sec;
322   pc->seconds.tv_nsec = nsec;
323 }
324 
325 /* Return a textual representation of the day ordinal/number values
326    in the parser_control struct (e.g., "last wed", "this tues", "thu").  */
327 static const char *
str_days(parser_control * pc,char * buffer,int n)328 str_days (parser_control *pc, char *buffer, int n)
329 {
330   /* TODO: use relative_time_table for reverse lookup.  */
331   static char const ordinal_values[][11] = {
332      "last",
333      "this",
334      "next/first",
335      "(SECOND)", /* SECOND is commented out in relative_time_table.  */
336      "third",
337      "fourth",
338      "fifth",
339      "sixth",
340      "seventh",
341      "eight",
342      "ninth",
343      "tenth",
344      "eleventh",
345      "twelfth"
346   };
347 
348   static char const days_values[][4] = {
349      "Sun",
350      "Mon",
351      "Tue",
352      "Wed",
353      "Thu",
354      "Fri",
355      "Sat"
356   };
357 
358   int len;
359 
360   /* Don't add an ordinal prefix if the user didn't specify it
361      (e.g., "this wed" vs "wed").  */
362   if (pc->debug_ordinal_day_seen)
363     {
364       /* Use word description if possible (e.g., -1 = last, 3 = third).  */
365       len = (-1 <= pc->day_ordinal && pc->day_ordinal <= 12
366              ? snprintf (buffer, n, "%s", ordinal_values[pc->day_ordinal + 1])
367              : snprintf (buffer, n, "%"PRIdMAX, pc->day_ordinal));
368     }
369   else
370     {
371       buffer[0] = '\0';
372       len = 0;
373     }
374 
375   /* Add the day name */
376   if (0 <= pc->day_number && pc->day_number <= 6 && 0 <= len && len < n)
377     snprintf (buffer + len, n - len, &" %s"[len == 0],
378               days_values[pc->day_number]);
379   else
380     {
381       /* invalid day_number value - should never happen */
382     }
383   return buffer;
384 }
385 
386 /* Convert a time zone to its string representation.  */
387 
388 enum { TIME_ZONE_BUFSIZE = INT_STRLEN_BOUND (intmax_t) + sizeof ":MM:SS" } ;
389 
390 static char const *
time_zone_str(int time_zone,char time_zone_buf[TIME_ZONE_BUFSIZE])391 time_zone_str (int time_zone, char time_zone_buf[TIME_ZONE_BUFSIZE])
392 {
393   char *p = time_zone_buf;
394   char sign = time_zone < 0 ? '-' : '+';
395   int hour = abs (time_zone / (60 * 60));
396   p += sprintf (time_zone_buf, "%c%02d", sign, hour);
397   int offset_from_hour = abs (time_zone % (60 * 60));
398   if (offset_from_hour != 0)
399     {
400       int mm = offset_from_hour / 60;
401       int ss = offset_from_hour % 60;
402       *p++ = ':';
403       *p++ = '0' + mm / 10;
404       *p++ = '0' + mm % 10;
405       if (ss)
406         {
407           *p++ = ':';
408           *p++ = '0' + ss / 10;
409           *p++ = '0' + ss % 10;
410         }
411       *p = '\0';
412     }
413   return time_zone_buf;
414 }
415 
416 /* debugging: print the current time in the parser_control structure.
417    The parser will increment "*_seen" members for those which were parsed.
418    This function will print only newly seen parts.  */
419 static void
debug_print_current_time(char const * item,parser_control * pc)420 debug_print_current_time (char const *item, parser_control *pc)
421 {
422   bool space = false;
423 
424   if (!pc->parse_datetime_debug)
425     return;
426 
427   /* no newline, more items printed below */
428   dbg_printf (_("parsed %s part: "), item);
429 
430   if (pc->dates_seen && !pc->debug_dates_seen)
431     {
432       /*TODO: use pc->year.negative?  */
433       fprintf (stderr, "(Y-M-D) %04"PRIdMAX"-%02"PRIdMAX"-%02"PRIdMAX,
434               pc->year.value, pc->month, pc->day);
435       pc->debug_dates_seen = true;
436       space = true;
437     }
438 
439   if (pc->year_seen != pc->debug_year_seen)
440     {
441       if (space)
442         fputc (' ', stderr);
443       fprintf (stderr, _("year: %04"PRIdMAX), pc->year.value);
444 
445       pc->debug_year_seen = pc->year_seen;
446       space = true;
447     }
448 
449   if (pc->times_seen && !pc->debug_times_seen)
450     {
451       intmax_t sec = pc->seconds.tv_sec;
452       fprintf (stderr, &" %02"PRIdMAX":%02"PRIdMAX":%02"PRIdMAX[!space],
453                pc->hour, pc->minutes, sec);
454       if (pc->seconds.tv_nsec != 0)
455         {
456           int nsec = pc->seconds.tv_nsec;
457           fprintf (stderr, ".%09d", nsec);
458         }
459       if (pc->meridian == MERpm)
460         fputs ("pm", stderr);
461 
462       pc->debug_times_seen = true;
463       space = true;
464     }
465 
466   if (pc->days_seen && !pc->debug_days_seen)
467     {
468       if (space)
469         fputc (' ', stderr);
470       char tmp[DBGBUFSIZE];
471       fprintf (stderr, _("%s (day ordinal=%"PRIdMAX" number=%d)"),
472                str_days (pc, tmp, sizeof tmp),
473                pc->day_ordinal, pc->day_number);
474       pc->debug_days_seen = true;
475       space = true;
476     }
477 
478   /* local zone strings only change the DST settings,
479      not the timezone value.  If seen, inform about the DST.  */
480   if (pc->local_zones_seen && !pc->debug_local_zones_seen)
481     {
482       fprintf (stderr, &" isdst=%d%s"[!space],
483                pc->local_isdst, pc->dsts_seen ? " DST" : "");
484       pc->debug_local_zones_seen = true;
485       space = true;
486     }
487 
488   if (pc->zones_seen && !pc->debug_zones_seen)
489     {
490       char time_zone_buf[TIME_ZONE_BUFSIZE];
491       fprintf (stderr, &" UTC%s"[!space],
492                time_zone_str (pc->time_zone, time_zone_buf));
493       pc->debug_zones_seen = true;
494       space = true;
495     }
496 
497   if (pc->timespec_seen)
498     {
499       intmax_t sec = pc->seconds.tv_sec;
500       if (space)
501         fputc (' ', stderr);
502       fprintf (stderr, _("number of seconds: %"PRIdMAX), sec);
503     }
504 
505   fputc ('\n', stderr);
506 }
507 
508 /* Debugging: print the current relative values.  */
509 
510 static bool
print_rel_part(bool space,intmax_t val,char const * name)511 print_rel_part (bool space, intmax_t val, char const *name)
512 {
513   if (val == 0)
514     return space;
515   fprintf (stderr, &" %+"PRIdMAX" %s"[!space], val, name);
516   return true;
517 }
518 
519 static void
debug_print_relative_time(char const * item,parser_control const * pc)520 debug_print_relative_time (char const *item, parser_control const *pc)
521 {
522   bool space = false;
523 
524   if (!pc->parse_datetime_debug)
525     return;
526 
527   /* no newline, more items printed below */
528   dbg_printf (_("parsed %s part: "), item);
529 
530   if (pc->rel.year == 0 && pc->rel.month == 0 && pc->rel.day == 0
531       && pc->rel.hour == 0 && pc->rel.minutes == 0 && pc->rel.seconds == 0
532       && pc->rel.ns == 0)
533     {
534       /* Special case: relative time of this/today/now */
535       fputs (_("today/this/now\n"), stderr);
536       return;
537     }
538 
539   space = print_rel_part (space, pc->rel.year, "year(s)");
540   space = print_rel_part (space, pc->rel.month, "month(s)");
541   space = print_rel_part (space, pc->rel.day, "day(s)");
542   space = print_rel_part (space, pc->rel.hour, "hour(s)");
543   space = print_rel_part (space, pc->rel.minutes, "minutes");
544   space = print_rel_part (space, pc->rel.seconds, "seconds");
545   print_rel_part (space, pc->rel.ns, "nanoseconds");
546 
547   fputc ('\n', stderr);
548 }
549 
550 
551 
552 %}
553 
554 /* We want a reentrant parser, even if the TZ manipulation and the calls to
555    localtime and gmtime are not reentrant.  */
556 %define api.pure
557 %parse-param { parser_control *pc }
558 %lex-param { parser_control *pc }
559 
560 /* This grammar has 31 shift/reduce conflicts.  */
561 %expect 31
562 
563 %union
564 {
565   intmax_t intval;
566   textint textintval;
567   struct timespec timespec;
568   relative_time rel;
569 }
570 
571 %token <intval> tAGO
572 %token tDST
573 
574 %token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT
575 %token <intval> tDAY_UNIT tDAY_SHIFT
576 
577 %token <intval> tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN
578 %token <intval> tMONTH tORDINAL tZONE
579 
580 %token <textintval> tSNUMBER tUNUMBER
581 %token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER
582 
583 %type <intval> o_colon_minutes
584 %type <timespec> seconds signed_seconds unsigned_seconds
585 
586 %type <rel> relunit relunit_snumber dayshift
587 
588 %%
589 
590 spec:
591     timespec
592   | items
593   ;
594 
595 timespec:
596     '@' seconds
597       {
598         pc->seconds = $2;
599         pc->timespec_seen = true;
600         debug_print_current_time (_("number of seconds"), pc);
601       }
602   ;
603 
604 items:
605     /* empty */
606   | items item
607   ;
608 
609 item:
610     datetime
611       {
612         pc->times_seen++; pc->dates_seen++;
613         debug_print_current_time (_("datetime"), pc);
614       }
615   | time
616       {
617         pc->times_seen++;
618         debug_print_current_time (_("time"), pc);
619       }
620   | local_zone
621       {
622         pc->local_zones_seen++;
623         debug_print_current_time (_("local_zone"), pc);
624       }
625   | zone
626       {
627         pc->zones_seen++;
628         debug_print_current_time (_("zone"), pc);
629       }
630   | date
631       {
632         pc->dates_seen++;
633         debug_print_current_time (_("date"), pc);
634       }
635   | day
636       {
637         pc->days_seen++;
638         debug_print_current_time (_("day"), pc);
639       }
640   | rel
641       {
642         debug_print_relative_time (_("relative"), pc);
643       }
644   | number
645       {
646         debug_print_current_time (_("number"), pc);
647       }
648   | hybrid
649       {
650         debug_print_relative_time (_("hybrid"), pc);
651       }
652   ;
653 
654 datetime:
655     iso_8601_datetime
656   ;
657 
658 iso_8601_datetime:
659     iso_8601_date 'T' iso_8601_time
660   ;
661 
662 time:
663     tUNUMBER tMERIDIAN
664       {
665         set_hhmmss (pc, $1.value, 0, 0, 0);
666         pc->meridian = $2;
667       }
668   | tUNUMBER ':' tUNUMBER tMERIDIAN
669       {
670         set_hhmmss (pc, $1.value, $3.value, 0, 0);
671         pc->meridian = $4;
672       }
673   | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tMERIDIAN
674       {
675         set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
676         pc->meridian = $6;
677       }
678   | iso_8601_time
679   ;
680 
681 iso_8601_time:
682     tUNUMBER zone_offset
683       {
684         set_hhmmss (pc, $1.value, 0, 0, 0);
685         pc->meridian = MER24;
686       }
687   | tUNUMBER ':' tUNUMBER o_zone_offset
688       {
689         set_hhmmss (pc, $1.value, $3.value, 0, 0);
690         pc->meridian = MER24;
691       }
692   | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_zone_offset
693       {
694         set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
695         pc->meridian = MER24;
696       }
697   ;
698 
699 o_zone_offset:
700   /* empty */
701   | zone_offset
702   ;
703 
704 zone_offset:
705     tSNUMBER o_colon_minutes
706       {
707         pc->zones_seen++;
708         if (! time_zone_hhmm (pc, $1, $2)) YYABORT;
709       }
710   ;
711 
712 /* Local zone strings affect only the DST setting, and take effect
713    only if the current TZ setting is relevant.
714 
715    Example 1:
716    'EEST' is parsed as tLOCAL_ZONE, as it relates to the effective TZ:
717         TZ='Europe/Helsinki' date -d '2016-06-30 EEST'
718 
719    Example 2:
720    'EEST' is parsed as tDAYZONE:
721         TZ='Asia/Tokyo' date -d '2016-06-30 EEST'
722 
723    This is implemented by probing the next three calendar quarters
724    of the effective timezone and looking for DST changes -
725    if found, the timezone name (EEST) is inserted into
726    the lexical lookup table with type tLOCAL_ZONE.
727    (Search for 'quarter' comment in  'parse_datetime2'.)
728 */
729 local_zone:
730     tLOCAL_ZONE
731       { pc->local_isdst = $1; }
732   | tLOCAL_ZONE tDST
733       {
734         pc->local_isdst = 1;
735         pc->dsts_seen++;
736       }
737   ;
738 
739 /* Note 'T' is a special case, as it is used as the separator in ISO
740    8601 date and time of day representation.  */
741 zone:
742     tZONE
743       { pc->time_zone = $1; }
744   | 'T'
745       { pc->time_zone = -HOUR (7); }
746   | tZONE relunit_snumber
747       { pc->time_zone = $1;
748         if (! apply_relative_time (pc, $2, 1)) YYABORT;
749         debug_print_relative_time (_("relative"), pc);
750       }
751   | 'T' relunit_snumber
752       { pc->time_zone = -HOUR (7);
753         if (! apply_relative_time (pc, $2, 1)) YYABORT;
754         debug_print_relative_time (_("relative"), pc);
755       }
756   | tZONE tSNUMBER o_colon_minutes
757       { if (! time_zone_hhmm (pc, $2, $3)) YYABORT;
758         if (INT_ADD_WRAPV (pc->time_zone, $1, &pc->time_zone)) YYABORT; }
759   | tDAYZONE
760       { pc->time_zone = $1 + 60 * 60; }
761   | tZONE tDST
762       { pc->time_zone = $1 + 60 * 60; }
763   ;
764 
765 day:
766     tDAY
767       {
768         pc->day_ordinal = 0;
769         pc->day_number = $1;
770       }
771   | tDAY ','
772       {
773         pc->day_ordinal = 0;
774         pc->day_number = $1;
775       }
776   | tORDINAL tDAY
777       {
778         pc->day_ordinal = $1;
779         pc->day_number = $2;
780         pc->debug_ordinal_day_seen = true;
781       }
782   | tUNUMBER tDAY
783       {
784         pc->day_ordinal = $1.value;
785         pc->day_number = $2;
786         pc->debug_ordinal_day_seen = true;
787       }
788   ;
789 
790 date:
791     tUNUMBER '/' tUNUMBER
792       {
793         pc->month = $1.value;
794         pc->day = $3.value;
795       }
796   | tUNUMBER '/' tUNUMBER '/' tUNUMBER
797       {
798         /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
799            otherwise as MM/DD/YY.
800            The goal in recognizing YYYY/MM/DD is solely to support legacy
801            machine-generated dates like those in an RCS log listing.  If
802            you want portability, use the ISO 8601 format.  */
803         if (4 <= $1.digits)
804           {
805             if (pc->parse_datetime_debug)
806               {
807                 intmax_t digits = $1.digits;
808                 dbg_printf (_("warning: value %"PRIdMAX" has %"PRIdMAX" digits. "
809                               "Assuming YYYY/MM/DD\n"),
810                             $1.value, digits);
811               }
812 
813             pc->year = $1;
814             pc->month = $3.value;
815             pc->day = $5.value;
816           }
817         else
818           {
819             if (pc->parse_datetime_debug)
820               dbg_printf (_("warning: value %"PRIdMAX" has less than 4 digits. "
821                             "Assuming MM/DD/YY[YY]\n"),
822                           $1.value);
823 
824             pc->month = $1.value;
825             pc->day = $3.value;
826             pc->year = $5;
827           }
828       }
829   | tUNUMBER tMONTH tSNUMBER
830       {
831         /* E.g., 17-JUN-1992.  */
832         pc->day = $1.value;
833         pc->month = $2;
834         if (INT_SUBTRACT_WRAPV (0, $3.value, &pc->year.value)) YYABORT;
835         pc->year.digits = $3.digits;
836       }
837   | tMONTH tSNUMBER tSNUMBER
838       {
839         /* E.g., JUN-17-1992.  */
840         pc->month = $1;
841         if (INT_SUBTRACT_WRAPV (0, $2.value, &pc->day)) YYABORT;
842         if (INT_SUBTRACT_WRAPV (0, $3.value, &pc->year.value)) YYABORT;
843         pc->year.digits = $3.digits;
844       }
845   | tMONTH tUNUMBER
846       {
847         pc->month = $1;
848         pc->day = $2.value;
849       }
850   | tMONTH tUNUMBER ',' tUNUMBER
851       {
852         pc->month = $1;
853         pc->day = $2.value;
854         pc->year = $4;
855       }
856   | tUNUMBER tMONTH
857       {
858         pc->day = $1.value;
859         pc->month = $2;
860       }
861   | tUNUMBER tMONTH tUNUMBER
862       {
863         pc->day = $1.value;
864         pc->month = $2;
865         pc->year = $3;
866       }
867   | iso_8601_date
868   ;
869 
870 iso_8601_date:
871     tUNUMBER tSNUMBER tSNUMBER
872       {
873         /* ISO 8601 format.  YYYY-MM-DD.  */
874         pc->year = $1;
875         if (INT_SUBTRACT_WRAPV (0, $2.value, &pc->month)) YYABORT;
876         if (INT_SUBTRACT_WRAPV (0, $3.value, &pc->day)) YYABORT;
877       }
878   ;
879 
880 rel:
881     relunit tAGO
882       { if (! apply_relative_time (pc, $1, $2)) YYABORT; }
883   | relunit
884       { if (! apply_relative_time (pc, $1, 1)) YYABORT; }
885   | dayshift
886       { if (! apply_relative_time (pc, $1, 1)) YYABORT; }
887   ;
888 
889 relunit:
890     tORDINAL tYEAR_UNIT
891       { $$ = RELATIVE_TIME_0; $$.year = $1; }
892   | tUNUMBER tYEAR_UNIT
893       { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
894   | tYEAR_UNIT
895       { $$ = RELATIVE_TIME_0; $$.year = 1; }
896   | tORDINAL tMONTH_UNIT
897       { $$ = RELATIVE_TIME_0; $$.month = $1; }
898   | tUNUMBER tMONTH_UNIT
899       { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
900   | tMONTH_UNIT
901       { $$ = RELATIVE_TIME_0; $$.month = 1; }
902   | tORDINAL tDAY_UNIT
903       { $$ = RELATIVE_TIME_0;
904         if (INT_MULTIPLY_WRAPV ($1, $2, &$$.day)) YYABORT; }
905   | tUNUMBER tDAY_UNIT
906       { $$ = RELATIVE_TIME_0;
907         if (INT_MULTIPLY_WRAPV ($1.value, $2, &$$.day)) YYABORT; }
908   | tDAY_UNIT
909       { $$ = RELATIVE_TIME_0; $$.day = $1; }
910   | tORDINAL tHOUR_UNIT
911       { $$ = RELATIVE_TIME_0; $$.hour = $1; }
912   | tUNUMBER tHOUR_UNIT
913       { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
914   | tHOUR_UNIT
915       { $$ = RELATIVE_TIME_0; $$.hour = 1; }
916   | tORDINAL tMINUTE_UNIT
917       { $$ = RELATIVE_TIME_0; $$.minutes = $1; }
918   | tUNUMBER tMINUTE_UNIT
919       { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
920   | tMINUTE_UNIT
921       { $$ = RELATIVE_TIME_0; $$.minutes = 1; }
922   | tORDINAL tSEC_UNIT
923       { $$ = RELATIVE_TIME_0; $$.seconds = $1; }
924   | tUNUMBER tSEC_UNIT
925       { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
926   | tSDECIMAL_NUMBER tSEC_UNIT
927       { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
928   | tUDECIMAL_NUMBER tSEC_UNIT
929       { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
930   | tSEC_UNIT
931       { $$ = RELATIVE_TIME_0; $$.seconds = 1; }
932   | relunit_snumber
933   ;
934 
935 relunit_snumber:
936     tSNUMBER tYEAR_UNIT
937       { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
938   | tSNUMBER tMONTH_UNIT
939       { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
940   | tSNUMBER tDAY_UNIT
941       { $$ = RELATIVE_TIME_0;
942         if (INT_MULTIPLY_WRAPV ($1.value, $2, &$$.day)) YYABORT; }
943   | tSNUMBER tHOUR_UNIT
944       { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
945   | tSNUMBER tMINUTE_UNIT
946       { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
947   | tSNUMBER tSEC_UNIT
948       { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
949   ;
950 
951 dayshift:
952     tDAY_SHIFT
953       { $$ = RELATIVE_TIME_0; $$.day = $1; }
954   ;
955 
956 seconds: signed_seconds | unsigned_seconds;
957 
958 signed_seconds:
959     tSDECIMAL_NUMBER
960   | tSNUMBER
961       { if (time_overflow ($1.value)) YYABORT;
962         $$.tv_sec = $1.value; $$.tv_nsec = 0; }
963   ;
964 
965 unsigned_seconds:
966     tUDECIMAL_NUMBER
967   | tUNUMBER
968       { if (time_overflow ($1.value)) YYABORT;
969         $$.tv_sec = $1.value; $$.tv_nsec = 0; }
970   ;
971 
972 number:
973     tUNUMBER
974       { digits_to_date_time (pc, $1); }
975   ;
976 
977 hybrid:
978     tUNUMBER relunit_snumber
979       {
980         /* Hybrid all-digit and relative offset, so that we accept e.g.,
981            "YYYYMMDD +N days" as well as "YYYYMMDD N days".  */
982         digits_to_date_time (pc, $1);
983         if (! apply_relative_time (pc, $2, 1)) YYABORT;
984       }
985   ;
986 
987 o_colon_minutes:
988     /* empty */
989       { $$ = -1; }
990   | ':' tUNUMBER
991       { $$ = $2.value; }
992   ;
993 
994 %%
995 
996 static table const meridian_table[] =
997 {
998   { "AM",   tMERIDIAN, MERam },
999   { "A.M.", tMERIDIAN, MERam },
1000   { "PM",   tMERIDIAN, MERpm },
1001   { "P.M.", tMERIDIAN, MERpm },
1002   { NULL, 0, 0 }
1003 };
1004 
1005 static table const dst_table[] =
1006 {
1007   { "DST", tDST, 0 }
1008 };
1009 
1010 static table const month_and_day_table[] =
1011 {
1012   { "JANUARY",  tMONTH,  1 },
1013   { "FEBRUARY", tMONTH,  2 },
1014   { "MARCH",    tMONTH,  3 },
1015   { "APRIL",    tMONTH,  4 },
1016   { "MAY",      tMONTH,  5 },
1017   { "JUNE",     tMONTH,  6 },
1018   { "JULY",     tMONTH,  7 },
1019   { "AUGUST",   tMONTH,  8 },
1020   { "SEPTEMBER",tMONTH,  9 },
1021   { "SEPT",     tMONTH,  9 },
1022   { "OCTOBER",  tMONTH, 10 },
1023   { "NOVEMBER", tMONTH, 11 },
1024   { "DECEMBER", tMONTH, 12 },
1025   { "SUNDAY",   tDAY,    0 },
1026   { "MONDAY",   tDAY,    1 },
1027   { "TUESDAY",  tDAY,    2 },
1028   { "TUES",     tDAY,    2 },
1029   { "WEDNESDAY",tDAY,    3 },
1030   { "WEDNES",   tDAY,    3 },
1031   { "THURSDAY", tDAY,    4 },
1032   { "THUR",     tDAY,    4 },
1033   { "THURS",    tDAY,    4 },
1034   { "FRIDAY",   tDAY,    5 },
1035   { "SATURDAY", tDAY,    6 },
1036   { NULL, 0, 0 }
1037 };
1038 
1039 static table const time_units_table[] =
1040 {
1041   { "YEAR",     tYEAR_UNIT,      1 },
1042   { "MONTH",    tMONTH_UNIT,     1 },
1043   { "FORTNIGHT",tDAY_UNIT,      14 },
1044   { "WEEK",     tDAY_UNIT,       7 },
1045   { "DAY",      tDAY_UNIT,       1 },
1046   { "HOUR",     tHOUR_UNIT,      1 },
1047   { "MINUTE",   tMINUTE_UNIT,    1 },
1048   { "MIN",      tMINUTE_UNIT,    1 },
1049   { "SECOND",   tSEC_UNIT,       1 },
1050   { "SEC",      tSEC_UNIT,       1 },
1051   { NULL, 0, 0 }
1052 };
1053 
1054 /* Assorted relative-time words.  */
1055 static table const relative_time_table[] =
1056 {
1057   { "TOMORROW", tDAY_SHIFT,      1 },
1058   { "YESTERDAY",tDAY_SHIFT,     -1 },
1059   { "TODAY",    tDAY_SHIFT,      0 },
1060   { "NOW",      tDAY_SHIFT,      0 },
1061   { "LAST",     tORDINAL,       -1 },
1062   { "THIS",     tORDINAL,        0 },
1063   { "NEXT",     tORDINAL,        1 },
1064   { "FIRST",    tORDINAL,        1 },
1065 /*{ "SECOND",   tORDINAL,        2 }, */
1066   { "THIRD",    tORDINAL,        3 },
1067   { "FOURTH",   tORDINAL,        4 },
1068   { "FIFTH",    tORDINAL,        5 },
1069   { "SIXTH",    tORDINAL,        6 },
1070   { "SEVENTH",  tORDINAL,        7 },
1071   { "EIGHTH",   tORDINAL,        8 },
1072   { "NINTH",    tORDINAL,        9 },
1073   { "TENTH",    tORDINAL,       10 },
1074   { "ELEVENTH", tORDINAL,       11 },
1075   { "TWELFTH",  tORDINAL,       12 },
1076   { "AGO",      tAGO,           -1 },
1077   { "HENCE",    tAGO,            1 },
1078   { NULL, 0, 0 }
1079 };
1080 
1081 /* The universal time zone table.  These labels can be used even for
1082    timestamps that would not otherwise be valid, e.g., GMT timestamps
1083    oin London during summer.  */
1084 static table const universal_time_zone_table[] =
1085 {
1086   { "GMT",      tZONE,     HOUR ( 0) }, /* Greenwich Mean */
1087   { "UT",       tZONE,     HOUR ( 0) }, /* Universal (Coordinated) */
1088   { "UTC",      tZONE,     HOUR ( 0) },
1089   { NULL, 0, 0 }
1090 };
1091 
1092 /* The time zone table.  This table is necessarily incomplete, as time
1093    zone abbreviations are ambiguous; e.g., Australians interpret "EST"
1094    as Eastern time in Australia, not as US Eastern Standard Time.
1095    You cannot rely on parse_datetime to handle arbitrary time zone
1096    abbreviations; use numeric abbreviations like "-0500" instead.  */
1097 static table const time_zone_table[] =
1098 {
1099   { "WET",      tZONE,     HOUR ( 0) }, /* Western European */
1100   { "WEST",     tDAYZONE,  HOUR ( 0) }, /* Western European Summer */
1101   { "BST",      tDAYZONE,  HOUR ( 0) }, /* British Summer */
1102   { "ART",      tZONE,    -HOUR ( 3) }, /* Argentina */
1103   { "BRT",      tZONE,    -HOUR ( 3) }, /* Brazil */
1104   { "BRST",     tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
1105   { "NST",      tZONE,   -(HOUR ( 3) + 30 * 60) }, /* Newfoundland Standard */
1106   { "NDT",      tDAYZONE,-(HOUR ( 3) + 30 * 60) }, /* Newfoundland Daylight */
1107   { "AST",      tZONE,    -HOUR ( 4) }, /* Atlantic Standard */
1108   { "ADT",      tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
1109   { "CLT",      tZONE,    -HOUR ( 4) }, /* Chile */
1110   { "CLST",     tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
1111   { "EST",      tZONE,    -HOUR ( 5) }, /* Eastern Standard */
1112   { "EDT",      tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
1113   { "CST",      tZONE,    -HOUR ( 6) }, /* Central Standard */
1114   { "CDT",      tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
1115   { "MST",      tZONE,    -HOUR ( 7) }, /* Mountain Standard */
1116   { "MDT",      tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
1117   { "PST",      tZONE,    -HOUR ( 8) }, /* Pacific Standard */
1118   { "PDT",      tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
1119   { "AKST",     tZONE,    -HOUR ( 9) }, /* Alaska Standard */
1120   { "AKDT",     tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
1121   { "HST",      tZONE,    -HOUR (10) }, /* Hawaii Standard */
1122   { "HAST",     tZONE,    -HOUR (10) }, /* Hawaii-Aleutian Standard */
1123   { "HADT",     tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
1124   { "SST",      tZONE,    -HOUR (12) }, /* Samoa Standard */
1125   { "WAT",      tZONE,     HOUR ( 1) }, /* West Africa */
1126   { "CET",      tZONE,     HOUR ( 1) }, /* Central European */
1127   { "CEST",     tDAYZONE,  HOUR ( 1) }, /* Central European Summer */
1128   { "MET",      tZONE,     HOUR ( 1) }, /* Middle European */
1129   { "MEZ",      tZONE,     HOUR ( 1) }, /* Middle European */
1130   { "MEST",     tDAYZONE,  HOUR ( 1) }, /* Middle European Summer */
1131   { "MESZ",     tDAYZONE,  HOUR ( 1) }, /* Middle European Summer */
1132   { "EET",      tZONE,     HOUR ( 2) }, /* Eastern European */
1133   { "EEST",     tDAYZONE,  HOUR ( 2) }, /* Eastern European Summer */
1134   { "CAT",      tZONE,     HOUR ( 2) }, /* Central Africa */
1135   { "SAST",     tZONE,     HOUR ( 2) }, /* South Africa Standard */
1136   { "EAT",      tZONE,     HOUR ( 3) }, /* East Africa */
1137   { "MSK",      tZONE,     HOUR ( 3) }, /* Moscow */
1138   { "MSD",      tDAYZONE,  HOUR ( 3) }, /* Moscow Daylight */
1139   { "IST",      tZONE,    (HOUR ( 5) + 30 * 60) }, /* India Standard */
1140   { "SGT",      tZONE,     HOUR ( 8) }, /* Singapore */
1141   { "KST",      tZONE,     HOUR ( 9) }, /* Korea Standard */
1142   { "JST",      tZONE,     HOUR ( 9) }, /* Japan Standard */
1143   { "GST",      tZONE,     HOUR (10) }, /* Guam Standard */
1144   { "NZST",     tZONE,     HOUR (12) }, /* New Zealand Standard */
1145   { "NZDT",     tDAYZONE,  HOUR (12) }, /* New Zealand Daylight */
1146   { NULL, 0, 0 }
1147 };
1148 
1149 /* Military time zone table.
1150 
1151    RFC 822 got these backwards, but RFC 5322 makes the incorrect
1152    treatment optional, so do them the right way here.
1153 
1154    Note 'T' is a special case, as it is used as the separator in ISO
1155    8601 date and time of day representation.  */
1156 static table const military_table[] =
1157 {
1158   { "A", tZONE,  HOUR ( 1) },
1159   { "B", tZONE,  HOUR ( 2) },
1160   { "C", tZONE,  HOUR ( 3) },
1161   { "D", tZONE,  HOUR ( 4) },
1162   { "E", tZONE,  HOUR ( 5) },
1163   { "F", tZONE,  HOUR ( 6) },
1164   { "G", tZONE,  HOUR ( 7) },
1165   { "H", tZONE,  HOUR ( 8) },
1166   { "I", tZONE,  HOUR ( 9) },
1167   { "K", tZONE,  HOUR (10) },
1168   { "L", tZONE,  HOUR (11) },
1169   { "M", tZONE,  HOUR (12) },
1170   { "N", tZONE, -HOUR ( 1) },
1171   { "O", tZONE, -HOUR ( 2) },
1172   { "P", tZONE, -HOUR ( 3) },
1173   { "Q", tZONE, -HOUR ( 4) },
1174   { "R", tZONE, -HOUR ( 5) },
1175   { "S", tZONE, -HOUR ( 6) },
1176   { "T", 'T',    0 },
1177   { "U", tZONE, -HOUR ( 8) },
1178   { "V", tZONE, -HOUR ( 9) },
1179   { "W", tZONE, -HOUR (10) },
1180   { "X", tZONE, -HOUR (11) },
1181   { "Y", tZONE, -HOUR (12) },
1182   { "Z", tZONE,  HOUR ( 0) },
1183   { NULL, 0, 0 }
1184 };
1185 
1186 
1187 
1188 /* Convert a time zone expressed as HH:MM into an integer count of
1189    seconds.  If MM is negative, then S is of the form HHMM and needs
1190    to be picked apart; otherwise, S is of the form HH.  As specified in
1191    https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03, allow
1192    only valid TZ range, and consider first two digits as hours, if no
1193    minutes specified.  Return true if successful.  */
1194 
1195 static bool
time_zone_hhmm(parser_control * pc,textint s,intmax_t mm)1196 time_zone_hhmm (parser_control *pc, textint s, intmax_t mm)
1197 {
1198   intmax_t n_minutes;
1199   bool overflow = false;
1200 
1201   /* If the length of S is 1 or 2 and no minutes are specified,
1202      interpret it as a number of hours.  */
1203   if (s.digits <= 2 && mm < 0)
1204     s.value *= 100;
1205 
1206   if (mm < 0)
1207     n_minutes = (s.value / 100) * 60 + s.value % 100;
1208   else
1209     {
1210       overflow |= INT_MULTIPLY_WRAPV (s.value, 60, &n_minutes);
1211       overflow |= (s.negative
1212                    ? INT_SUBTRACT_WRAPV (n_minutes, mm, &n_minutes)
1213                    : INT_ADD_WRAPV (n_minutes, mm, &n_minutes));
1214     }
1215 
1216   if (overflow || ! (-24 * 60 <= n_minutes && n_minutes <= 24 * 60))
1217     return false;
1218   pc->time_zone = n_minutes * 60;
1219   return true;
1220 }
1221 
1222 static int
to_hour(intmax_t hours,int meridian)1223 to_hour (intmax_t hours, int meridian)
1224 {
1225   switch (meridian)
1226     {
1227     default: /* Pacify GCC.  */
1228     case MER24:
1229       return 0 <= hours && hours < 24 ? hours : -1;
1230     case MERam:
1231       return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
1232     case MERpm:
1233       return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
1234     }
1235 }
1236 
1237 enum { TM_YEAR_BASE = 1900 };
1238 enum { TM_YEAR_BUFSIZE = INT_BUFSIZE_BOUND (int) + 1 };
1239 
1240 /* Convert TM_YEAR, a year minus 1900, to a string that is numerically
1241    correct even if subtracting 1900 would overflow.  */
1242 
1243 static char const *
tm_year_str(int tm_year,char buf[TM_YEAR_BUFSIZE])1244 tm_year_str (int tm_year, char buf[TM_YEAR_BUFSIZE])
1245 {
1246   verify (TM_YEAR_BASE % 100 == 0);
1247   sprintf (buf, &"-%02d%02d"[-TM_YEAR_BASE <= tm_year],
1248            abs (tm_year / 100 + TM_YEAR_BASE / 100),
1249            abs (tm_year % 100));
1250   return buf;
1251 }
1252 
1253 /* Convert a text year number to a year minus 1900, working correctly
1254    even if the input is in the range INT_MAX .. INT_MAX + 1900 - 1.  */
1255 
1256 static bool
to_tm_year(textint textyear,bool debug,int * tm_year)1257 to_tm_year (textint textyear, bool debug, int *tm_year)
1258 {
1259   intmax_t year = textyear.value;
1260 
1261   /* XPG4 suggests that years 00-68 map to 2000-2068, and
1262      years 69-99 map to 1969-1999.  */
1263   if (0 <= year && textyear.digits == 2)
1264     {
1265       year += year < 69 ? 2000 : 1900;
1266       if (debug)
1267         dbg_printf (_("warning: adjusting year value %"PRIdMAX
1268                       " to %"PRIdMAX"\n"),
1269                     textyear.value, year);
1270     }
1271 
1272   if (year < 0
1273       ? INT_SUBTRACT_WRAPV (-TM_YEAR_BASE, year, tm_year)
1274       : INT_SUBTRACT_WRAPV (year, TM_YEAR_BASE, tm_year))
1275     {
1276       if (debug)
1277         dbg_printf (_("error: out-of-range year %"PRIdMAX"\n"), year);
1278       return false;
1279     }
1280 
1281   return true;
1282 }
1283 
1284 static table const * _GL_ATTRIBUTE_PURE
lookup_zone(parser_control const * pc,char const * name)1285 lookup_zone (parser_control const *pc, char const *name)
1286 {
1287   table const *tp;
1288 
1289   for (tp = universal_time_zone_table; tp->name; tp++)
1290     if (strcmp (name, tp->name) == 0)
1291       return tp;
1292 
1293   /* Try local zone abbreviations before those in time_zone_table, as
1294      the local ones are more likely to be right.  */
1295   for (tp = pc->local_time_zone_table; tp->name; tp++)
1296     if (strcmp (name, tp->name) == 0)
1297       return tp;
1298 
1299   for (tp = time_zone_table; tp->name; tp++)
1300     if (strcmp (name, tp->name) == 0)
1301       return tp;
1302 
1303   return NULL;
1304 }
1305 
1306 #if ! HAVE_TM_GMTOFF
1307 /* Yield the difference between *A and *B,
1308    measured in seconds, ignoring leap seconds.
1309    The body of this function is taken directly from the GNU C Library;
1310    see strftime.c.  */
1311 static int
tm_diff(const struct tm * a,const struct tm * b)1312 tm_diff (const struct tm *a, const struct tm *b)
1313 {
1314   /* Compute intervening leap days correctly even if year is negative.
1315      Take care to avoid int overflow in leap day calculations,
1316      but it's OK to assume that A and B are close to each other.  */
1317   int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
1318   int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
1319   int a100 = a4 / 25 - (a4 % 25 < 0);
1320   int b100 = b4 / 25 - (b4 % 25 < 0);
1321   int a400 = SHR (a100, 2);
1322   int b400 = SHR (b100, 2);
1323   int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
1324   int years = a->tm_year - b->tm_year;
1325   int days = (365 * years + intervening_leap_days
1326               + (a->tm_yday - b->tm_yday));
1327   return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
1328                 + (a->tm_min - b->tm_min))
1329           + (a->tm_sec - b->tm_sec));
1330 }
1331 #endif /* ! HAVE_TM_GMTOFF */
1332 
1333 static table const *
lookup_word(parser_control const * pc,char * word)1334 lookup_word (parser_control const *pc, char *word)
1335 {
1336   char *p;
1337   char *q;
1338   ptrdiff_t wordlen;
1339   table const *tp;
1340   bool period_found;
1341   bool abbrev;
1342 
1343   /* Make it uppercase.  */
1344   for (p = word; *p; p++)
1345     *p = c_toupper (to_uchar (*p));
1346 
1347   for (tp = meridian_table; tp->name; tp++)
1348     if (strcmp (word, tp->name) == 0)
1349       return tp;
1350 
1351   /* See if we have an abbreviation for a month.  */
1352   wordlen = strlen (word);
1353   abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
1354 
1355   for (tp = month_and_day_table; tp->name; tp++)
1356     if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
1357       return tp;
1358 
1359   if ((tp = lookup_zone (pc, word)))
1360     return tp;
1361 
1362   if (strcmp (word, dst_table[0].name) == 0)
1363     return dst_table;
1364 
1365   for (tp = time_units_table; tp->name; tp++)
1366     if (strcmp (word, tp->name) == 0)
1367       return tp;
1368 
1369   /* Strip off any plural and try the units table again.  */
1370   if (word[wordlen - 1] == 'S')
1371     {
1372       word[wordlen - 1] = '\0';
1373       for (tp = time_units_table; tp->name; tp++)
1374         if (strcmp (word, tp->name) == 0)
1375           return tp;
1376       word[wordlen - 1] = 'S';  /* For "this" in relative_time_table.  */
1377     }
1378 
1379   for (tp = relative_time_table; tp->name; tp++)
1380     if (strcmp (word, tp->name) == 0)
1381       return tp;
1382 
1383   /* Military time zones.  */
1384   if (wordlen == 1)
1385     for (tp = military_table; tp->name; tp++)
1386       if (word[0] == tp->name[0])
1387         return tp;
1388 
1389   /* Drop out any periods and try the time zone table again.  */
1390   for (period_found = false, p = q = word; (*p = *q); q++)
1391     if (*q == '.')
1392       period_found = true;
1393     else
1394       p++;
1395   if (period_found && (tp = lookup_zone (pc, word)))
1396     return tp;
1397 
1398   return NULL;
1399 }
1400 
1401 static int
yylex(union YYSTYPE * lvalp,parser_control * pc)1402 yylex (union YYSTYPE *lvalp, parser_control *pc)
1403 {
1404   unsigned char c;
1405 
1406   for (;;)
1407     {
1408       while (c = *pc->input, c_isspace (c))
1409         pc->input++;
1410 
1411       if (c_isdigit (c) || c == '-' || c == '+')
1412         {
1413           char const *p;
1414           int sign;
1415           intmax_t value = 0;
1416           if (c == '-' || c == '+')
1417             {
1418               sign = c == '-' ? -1 : 1;
1419               while (c = *++pc->input, c_isspace (c))
1420                 continue;
1421               if (! c_isdigit (c))
1422                 /* skip the '-' sign */
1423                 continue;
1424             }
1425           else
1426             sign = 0;
1427           p = pc->input;
1428 
1429           do
1430             {
1431               if (INT_MULTIPLY_WRAPV (value, 10, &value))
1432                 return '?';
1433               if (INT_ADD_WRAPV (value, sign < 0 ? '0' - c : c - '0', &value))
1434                 return '?';
1435               c = *++p;
1436             }
1437           while (c_isdigit (c));
1438 
1439           if ((c == '.' || c == ',') && c_isdigit (p[1]))
1440             {
1441               time_t s;
1442               int ns;
1443               int digits;
1444 
1445               if (time_overflow (value))
1446                 return '?';
1447               s = value;
1448 
1449               /* Accumulate fraction, to ns precision.  */
1450               p++;
1451               ns = *p++ - '0';
1452               for (digits = 2; digits <= LOG10_BILLION; digits++)
1453                 {
1454                   ns *= 10;
1455                   if (c_isdigit (*p))
1456                     ns += *p++ - '0';
1457                 }
1458 
1459               /* Skip excess digits, truncating toward -Infinity.  */
1460               if (sign < 0)
1461                 for (; c_isdigit (*p); p++)
1462                   if (*p != '0')
1463                     {
1464                       ns++;
1465                       break;
1466                     }
1467               while (c_isdigit (*p))
1468                 p++;
1469 
1470               /* Adjust to the timespec convention, which is that
1471                  tv_nsec is always a positive offset even if tv_sec is
1472                  negative.  */
1473               if (sign < 0 && ns)
1474                 {
1475                   if (s == TYPE_MINIMUM (time_t))
1476                     return '?';
1477                   s--;
1478                   ns = BILLION - ns;
1479                 }
1480 
1481               lvalp->timespec.tv_sec = s;
1482               lvalp->timespec.tv_nsec = ns;
1483               pc->input = p;
1484               return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1485             }
1486           else
1487             {
1488               lvalp->textintval.negative = sign < 0;
1489               lvalp->textintval.value = value;
1490               lvalp->textintval.digits = p - pc->input;
1491               pc->input = p;
1492               return sign ? tSNUMBER : tUNUMBER;
1493             }
1494         }
1495 
1496       if (c_isalpha (c))
1497         {
1498           char buff[20];
1499           char *p = buff;
1500           table const *tp;
1501 
1502           do
1503             {
1504               if (p < buff + sizeof buff - 1)
1505                 *p++ = c;
1506               c = *++pc->input;
1507             }
1508           while (c_isalpha (c) || c == '.');
1509 
1510           *p = '\0';
1511           tp = lookup_word (pc, buff);
1512           if (! tp)
1513             {
1514               if (pc->parse_datetime_debug)
1515                 dbg_printf (_("error: unknown word '%s'\n"), buff);
1516               return '?';
1517             }
1518           lvalp->intval = tp->value;
1519           return tp->type;
1520         }
1521 
1522       if (c != '(')
1523         return to_uchar (*pc->input++);
1524 
1525       ptrdiff_t count = 0;
1526       do
1527         {
1528           c = *pc->input++;
1529           if (c == '\0')
1530             return c;
1531           if (c == '(')
1532             count++;
1533           else if (c == ')')
1534             count--;
1535         }
1536       while (count != 0);
1537     }
1538 }
1539 
1540 /* Do nothing if the parser reports an error.  */
1541 static int
yyerror(parser_control const * pc _GL_UNUSED,char const * s _GL_UNUSED)1542 yyerror (parser_control const *pc _GL_UNUSED,
1543          char const *s _GL_UNUSED)
1544 {
1545   return 0;
1546 }
1547 
1548 /* If *TM0 is the old and *TM1 is the new value of a struct tm after
1549    passing it to mktime_z, return true if it's OK.  It's not OK if
1550    mktime failed or if *TM0 has out-of-range mainline members.
1551    The caller should set TM1->tm_wday to -1 before calling mktime,
1552    as a negative tm_wday is how mktime failure is inferred.  */
1553 
1554 static bool
mktime_ok(struct tm const * tm0,struct tm const * tm1)1555 mktime_ok (struct tm const *tm0, struct tm const *tm1)
1556 {
1557   if (tm1->tm_wday < 0)
1558     return false;
1559 
1560   return ! ((tm0->tm_sec ^ tm1->tm_sec)
1561             | (tm0->tm_min ^ tm1->tm_min)
1562             | (tm0->tm_hour ^ tm1->tm_hour)
1563             | (tm0->tm_mday ^ tm1->tm_mday)
1564             | (tm0->tm_mon ^ tm1->tm_mon)
1565             | (tm0->tm_year ^ tm1->tm_year));
1566 }
1567 
1568 /* Debugging: format a 'struct tm' into a buffer, taking the parser's
1569    timezone information into account (if pc != NULL).  */
1570 static char const *
debug_strfdatetime(struct tm const * tm,parser_control const * pc,char * buf,int n)1571 debug_strfdatetime (struct tm const *tm, parser_control const *pc,
1572                     char *buf, int n)
1573 {
1574   /* TODO:
1575      1. find an optimal way to print date string in a clear and unambiguous
1576         format.  Currently, always add '(Y-M-D)' prefix.
1577         Consider '2016y01m10d'  or 'year(2016) month(01) day(10)'.
1578 
1579         If the user needs debug printing, it means he/she already having
1580         issues with the parsing - better to avoid formats that could
1581         be mis-interpreted (e.g., just YYYY-MM-DD).
1582 
1583      2. Can strftime be used instead?
1584         depends if it is portable and can print invalid dates on all systems.
1585 
1586      3. Print timezone information ?
1587 
1588      4. Print DST information ?
1589 
1590      5. Print nanosecond information ?
1591 
1592      NOTE:
1593      Printed date/time values might not be valid, e.g., '2016-02-31'
1594      or '2016-19-2016' .  These are the values as parsed from the user
1595      string, before validation.
1596   */
1597   int m = nstrftime (buf, n, "(Y-M-D) %Y-%m-%d %H:%M:%S", tm, 0, 0);
1598 
1599   /* If parser_control information was provided (for timezone),
1600      and there's enough space in the buffer, add timezone info.  */
1601   if (pc && m < n && pc->zones_seen)
1602     {
1603       int tz = pc->time_zone;
1604 
1605       /* Account for DST if tLOCAL_ZONE was seen.  */
1606       if (pc->local_zones_seen && !pc->zones_seen && 0 < pc->local_isdst)
1607         tz += 60 * 60;
1608 
1609       char time_zone_buf[TIME_ZONE_BUFSIZE];
1610       snprintf (&buf[m], n - m, " TZ=%s", time_zone_str (tz, time_zone_buf));
1611     }
1612   return buf;
1613 }
1614 
1615 static char const *
debug_strfdate(struct tm const * tm,char * buf,int n)1616 debug_strfdate (struct tm const *tm, char *buf, int n)
1617 {
1618   char tm_year_buf[TM_YEAR_BUFSIZE];
1619   snprintf (buf, n, "(Y-M-D) %s-%02d-%02d",
1620             tm_year_str (tm->tm_year, tm_year_buf),
1621             tm->tm_mon + 1, tm->tm_mday);
1622   return buf;
1623 }
1624 
1625 static char const *
debug_strftime(struct tm const * tm,char * buf,int n)1626 debug_strftime (struct tm const *tm, char *buf, int n)
1627 {
1628   snprintf (buf, n, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
1629   return buf;
1630 }
1631 
1632 /* If mktime_ok failed, display the failed time values,
1633    and provide possible hints.  Example output:
1634 
1635     date: error: invalid date/time value:
1636     date:     user provided time: '(Y-M-D) 2006-04-02 02:45:00'
1637     date:        normalized time: '(Y-M-D) 2006-04-02 03:45:00'
1638     date:                                             __
1639     date:      possible reasons:
1640     date:        non-existing due to daylight-saving time;
1641     date:        numeric values overflow;
1642     date:        missing timezone;
1643  */
1644 static void
debug_mktime_not_ok(struct tm const * tm0,struct tm const * tm1,parser_control const * pc,bool time_zone_seen)1645 debug_mktime_not_ok (struct tm const *tm0, struct tm const *tm1,
1646                      parser_control const *pc, bool time_zone_seen)
1647 {
1648   /* TODO: handle t == -1 (as in 'mktime_ok').  */
1649   char tmp[DBGBUFSIZE];
1650   int i;
1651   const bool eq_sec   = (tm0->tm_sec  == tm1->tm_sec);
1652   const bool eq_min   = (tm0->tm_min  == tm1->tm_min);
1653   const bool eq_hour  = (tm0->tm_hour == tm1->tm_hour);
1654   const bool eq_mday  = (tm0->tm_mday == tm1->tm_mday);
1655   const bool eq_month = (tm0->tm_mon  == tm1->tm_mon);
1656   const bool eq_year  = (tm0->tm_year == tm1->tm_year);
1657 
1658   const bool dst_shift = eq_sec && eq_min && !eq_hour
1659                          && eq_mday && eq_month && eq_year;
1660 
1661   if (!pc->parse_datetime_debug)
1662     return;
1663 
1664   dbg_printf (_("error: invalid date/time value:\n"));
1665   dbg_printf (_("    user provided time: '%s'\n"),
1666               debug_strfdatetime (tm0, pc, tmp, sizeof tmp));
1667   dbg_printf (_("       normalized time: '%s'\n"),
1668               debug_strfdatetime (tm1, pc, tmp, sizeof tmp));
1669   /* The format must be aligned with debug_strfdatetime and the two
1670      DEBUG statements above.  This string is not translated.  */
1671   i = snprintf (tmp, sizeof tmp,
1672                 "                                 %4s %2s %2s %2s %2s %2s",
1673                 eq_year ? "" : "----",
1674                 eq_month ? "" : "--",
1675                 eq_mday ? "" : "--",
1676                 eq_hour ? "" : "--",
1677                 eq_min ? "" : "--",
1678                 eq_sec ? "" : "--");
1679   /* Trim trailing whitespace.  */
1680   if (0 <= i)
1681     {
1682       if (sizeof tmp - 1 < i)
1683         i = sizeof tmp - 1;
1684       while (0 < i && tmp[i - 1] == ' ')
1685         --i;
1686       tmp[i] = '\0';
1687     }
1688   dbg_printf ("%s\n", tmp);
1689 
1690   dbg_printf (_("     possible reasons:\n"));
1691   if (dst_shift)
1692     dbg_printf (_("       non-existing due to daylight-saving time;\n"));
1693   if (!eq_mday && !eq_month)
1694     dbg_printf (_("       invalid day/month combination;\n"));
1695   dbg_printf (_("       numeric values overflow;\n"));
1696   dbg_printf ("       %s\n", (time_zone_seen ? _("incorrect timezone")
1697                               : _("missing timezone")));
1698 }
1699 
1700 /* The original interface: run with debug=false and the default timezone.   */
1701 bool
parse_datetime(struct timespec * result,char const * p,struct timespec const * now)1702 parse_datetime (struct timespec *result, char const *p,
1703                 struct timespec const *now)
1704 {
1705   char const *tzstring = getenv ("TZ");
1706   timezone_t tz = tzalloc (tzstring);
1707   if (!tz)
1708     return false;
1709   bool ok = parse_datetime2 (result, p, now, 0, tz, tzstring);
1710   tzfree (tz);
1711   return ok;
1712 }
1713 
1714 /* Parse a date/time string, storing the resulting time value into *RESULT.
1715    The string itself is pointed to by P.  Return true if successful.
1716    P can be an incomplete or relative time specification; if so, use
1717    *NOW as the basis for the returned time.  Default to timezone
1718    TZDEFAULT, which corresponds to tzalloc (TZSTRING).  */
1719 bool
parse_datetime2(struct timespec * result,char const * p,struct timespec const * now,unsigned int flags,timezone_t tzdefault,char const * tzstring)1720 parse_datetime2 (struct timespec *result, char const *p,
1721                  struct timespec const *now, unsigned int flags,
1722                  timezone_t tzdefault, char const *tzstring)
1723 {
1724   struct tm tm;
1725   struct tm tm0;
1726   char time_zone_buf[TIME_ZONE_BUFSIZE];
1727   char dbg_tm[DBGBUFSIZE];
1728   bool ok = false;
1729   char const *input_sentinel = p + strlen (p);
1730   char *tz1alloc = NULL;
1731 
1732   /* A reasonable upper bound for the size of ordinary TZ strings.
1733      Use heap allocation if TZ's length exceeds this.  */
1734   enum { TZBUFSIZE = 100 };
1735   char tz1buf[TZBUFSIZE];
1736 
1737   struct timespec gettime_buffer;
1738   if (! now)
1739     {
1740       gettime (&gettime_buffer);
1741       now = &gettime_buffer;
1742     }
1743 
1744   time_t Start = now->tv_sec;
1745   int Start_ns = now->tv_nsec;
1746 
1747   unsigned char c;
1748   while (c = *p, c_isspace (c))
1749     p++;
1750 
1751   timezone_t tz = tzdefault;
1752 
1753   /* Store a local copy prior to first "goto".  Without this, a prior use
1754      below of RELATIVE_TIME_0 on the RHS might translate to an assignment-
1755      to-temporary, which would trigger a -Wjump-misses-init warning.  */
1756   const relative_time rel_time_0 = RELATIVE_TIME_0;
1757 
1758   if (strncmp (p, "TZ=\"", 4) == 0)
1759     {
1760       char const *tzbase = p + 4;
1761       ptrdiff_t tzsize = 1;
1762       char const *s;
1763 
1764       for (s = tzbase; *s; s++, tzsize++)
1765         if (*s == '\\')
1766           {
1767             s++;
1768             if (! (*s == '\\' || *s == '"'))
1769               break;
1770           }
1771         else if (*s == '"')
1772           {
1773             timezone_t tz1;
1774             char *tz1string = tz1buf;
1775             char *z;
1776             if (TZBUFSIZE < tzsize)
1777               {
1778                 tz1alloc = malloc (tzsize);
1779                 if (!tz1alloc)
1780                   goto fail;
1781                 tz1string = tz1alloc;
1782               }
1783             z = tz1string;
1784             for (s = tzbase; *s != '"'; s++)
1785               *z++ = *(s += *s == '\\');
1786             *z = '\0';
1787             tz1 = tzalloc (tz1string);
1788             if (!tz1)
1789               goto fail;
1790             tz = tz1;
1791             tzstring = tz1string;
1792 
1793             p = s + 1;
1794             while (c = *p, c_isspace (c))
1795               p++;
1796 
1797             break;
1798           }
1799     }
1800 
1801   struct tm tmp;
1802   if (! localtime_rz (tz, &now->tv_sec, &tmp))
1803     goto fail;
1804 
1805   /* As documented, be careful to treat the empty string just like
1806      a date string of "0".  Without this, an empty string would be
1807      declared invalid when parsed during a DST transition.  */
1808   if (*p == '\0')
1809     p = "0";
1810 
1811   parser_control pc;
1812   pc.input = p;
1813   pc.parse_datetime_debug = (flags & PARSE_DATETIME_DEBUG) != 0;
1814   if (INT_ADD_WRAPV (tmp.tm_year, TM_YEAR_BASE, &pc.year.value))
1815     {
1816       if (pc.parse_datetime_debug)
1817         dbg_printf (_("error: initial year out of range\n"));
1818       goto fail;
1819     }
1820   pc.year.digits = 0;
1821   pc.month = tmp.tm_mon + 1;
1822   pc.day = tmp.tm_mday;
1823   pc.hour = tmp.tm_hour;
1824   pc.minutes = tmp.tm_min;
1825   pc.seconds.tv_sec = tmp.tm_sec;
1826   pc.seconds.tv_nsec = Start_ns;
1827   tm.tm_isdst = tmp.tm_isdst;
1828 
1829   pc.meridian = MER24;
1830   pc.rel = rel_time_0;
1831   pc.timespec_seen = false;
1832   pc.rels_seen = false;
1833   pc.dates_seen = 0;
1834   pc.days_seen = 0;
1835   pc.times_seen = 0;
1836   pc.local_zones_seen = 0;
1837   pc.dsts_seen = 0;
1838   pc.zones_seen = 0;
1839   pc.year_seen = false;
1840   pc.debug_dates_seen = false;
1841   pc.debug_days_seen = false;
1842   pc.debug_times_seen = false;
1843   pc.debug_local_zones_seen = false;
1844   pc.debug_zones_seen = false;
1845   pc.debug_year_seen = false;
1846   pc.debug_ordinal_day_seen = false;
1847 
1848 #if HAVE_STRUCT_TM_TM_ZONE
1849   pc.local_time_zone_table[0].name = tmp.tm_zone;
1850   pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1851   pc.local_time_zone_table[0].value = tmp.tm_isdst;
1852   pc.local_time_zone_table[1].name = NULL;
1853 
1854   /* Probe the names used in the next three calendar quarters, looking
1855      for a tm_isdst different from the one we already have.  */
1856   {
1857     int quarter;
1858     for (quarter = 1; quarter <= 3; quarter++)
1859       {
1860         intmax_t iprobe;
1861         if (INT_ADD_WRAPV (Start, quarter * (90 * 24 * 60 * 60), &iprobe)
1862             || time_overflow (iprobe))
1863           break;
1864         time_t probe = iprobe;
1865         struct tm probe_tm;
1866         if (localtime_rz (tz, &probe, &probe_tm) && probe_tm.tm_zone
1867             && probe_tm.tm_isdst != pc.local_time_zone_table[0].value)
1868           {
1869               {
1870                 pc.local_time_zone_table[1].name = probe_tm.tm_zone;
1871                 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1872                 pc.local_time_zone_table[1].value = probe_tm.tm_isdst;
1873                 pc.local_time_zone_table[2].name = NULL;
1874               }
1875             break;
1876           }
1877       }
1878   }
1879 #else
1880 #if HAVE_TZNAME
1881   {
1882 # if !HAVE_DECL_TZNAME
1883     extern char *tzname[];
1884 # endif
1885     int i;
1886     for (i = 0; i < 2; i++)
1887       {
1888         pc.local_time_zone_table[i].name = tzname[i];
1889         pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1890         pc.local_time_zone_table[i].value = i;
1891       }
1892     pc.local_time_zone_table[i].name = NULL;
1893   }
1894 #else
1895   pc.local_time_zone_table[0].name = NULL;
1896 #endif
1897 #endif
1898 
1899   if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1900       && ! strcmp (pc.local_time_zone_table[0].name,
1901                    pc.local_time_zone_table[1].name))
1902     {
1903       /* This locale uses the same abbreviation for standard and
1904          daylight times.  So if we see that abbreviation, we don't
1905          know whether it's daylight time.  */
1906       pc.local_time_zone_table[0].value = -1;
1907       pc.local_time_zone_table[1].name = NULL;
1908     }
1909 
1910   if (yyparse (&pc) != 0)
1911     {
1912       if (pc.parse_datetime_debug)
1913         dbg_printf ((input_sentinel <= pc.input
1914                      ? _("error: parsing failed\n")
1915                      : _("error: parsing failed, stopped at '%s'\n")),
1916                     pc.input);
1917       goto fail;
1918     }
1919 
1920 
1921   /* Determine effective timezone source.  */
1922 
1923   if (pc.parse_datetime_debug)
1924     {
1925       dbg_printf (_("input timezone: "));
1926 
1927       if (pc.timespec_seen)
1928         fprintf (stderr, _("'@timespec' - always UTC"));
1929       else if (pc.zones_seen)
1930         fprintf (stderr, _("parsed date/time string"));
1931       else if (tzstring)
1932         {
1933           if (tz != tzdefault)
1934             fprintf (stderr, _("TZ=\"%s\" in date string"), tzstring);
1935           else if (STREQ (tzstring, "UTC0"))
1936             {
1937               /* Special case: 'date -u' sets TZ="UTC0".  */
1938               fprintf (stderr, _("TZ=\"UTC0\" environment value or -u"));
1939             }
1940           else
1941             fprintf (stderr, _("TZ=\"%s\" environment value"), tzstring);
1942         }
1943       else
1944         fprintf (stderr, _("system default"));
1945 
1946       /* Account for DST changes if tLOCAL_ZONE was seen.
1947          local timezone only changes DST and is relative to the
1948          default timezone.*/
1949       if (pc.local_zones_seen && !pc.zones_seen && 0 < pc.local_isdst)
1950         fprintf (stderr, ", dst");
1951 
1952       if (pc.zones_seen)
1953         fprintf (stderr, " (%s)", time_zone_str (pc.time_zone, time_zone_buf));
1954 
1955       fputc ('\n', stderr);
1956     }
1957 
1958   if (pc.timespec_seen)
1959     *result = pc.seconds;
1960   else
1961     {
1962       if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1963                | (pc.local_zones_seen + pc.zones_seen)))
1964         {
1965           if (pc.parse_datetime_debug)
1966             {
1967               if (pc.times_seen > 1)
1968                 dbg_printf ("error: seen multiple time parts\n");
1969               if (pc.dates_seen > 1)
1970                 dbg_printf ("error: seen multiple date parts\n");
1971               if (pc.days_seen > 1)
1972                 dbg_printf ("error: seen multiple days parts\n");
1973               if (pc.dsts_seen > 1)
1974                 dbg_printf ("error: seen multiple daylight-saving parts\n");
1975               if ((pc.local_zones_seen + pc.zones_seen) > 1)
1976                 dbg_printf ("error: seen multiple time-zone parts\n");
1977             }
1978           goto fail;
1979         }
1980 
1981       if (! to_tm_year (pc.year, pc.parse_datetime_debug, &tm.tm_year)
1982           || INT_ADD_WRAPV (pc.month, -1, &tm.tm_mon)
1983           || INT_ADD_WRAPV (pc.day, 0, &tm.tm_mday))
1984         {
1985           if (pc.parse_datetime_debug)
1986             dbg_printf (_("error: year, month, or day overflow\n"));
1987           goto fail;
1988         }
1989       if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1990         {
1991           tm.tm_hour = to_hour (pc.hour, pc.meridian);
1992           if (tm.tm_hour < 0)
1993             {
1994               char const *mrd = (pc.meridian == MERam ? "am"
1995                                  : pc.meridian == MERpm ?"pm" : "");
1996               if (pc.parse_datetime_debug)
1997                 dbg_printf (_("error: invalid hour %"PRIdMAX"%s\n"),
1998                             pc.hour, mrd);
1999               goto fail;
2000             }
2001           tm.tm_min = pc.minutes;
2002           tm.tm_sec = pc.seconds.tv_sec;
2003           if (pc.parse_datetime_debug)
2004             dbg_printf ((pc.times_seen
2005                          ? _("using specified time as starting value: '%s'\n")
2006                          : _("using current time as starting value: '%s'\n")),
2007                         debug_strftime (&tm, dbg_tm, sizeof dbg_tm));
2008         }
2009       else
2010         {
2011           tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
2012           pc.seconds.tv_nsec = 0;
2013           if (pc.parse_datetime_debug)
2014             dbg_printf ("warning: using midnight as starting time: 00:00:00\n");
2015         }
2016 
2017       /* Let mktime deduce tm_isdst if we have an absolute timestamp.  */
2018       if (pc.dates_seen | pc.days_seen | pc.times_seen)
2019         tm.tm_isdst = -1;
2020 
2021       /* But if the input explicitly specifies local time with or without
2022          DST, give mktime that information.  */
2023       if (pc.local_zones_seen)
2024         tm.tm_isdst = pc.local_isdst;
2025 
2026       tm0.tm_sec = tm.tm_sec;
2027       tm0.tm_min = tm.tm_min;
2028       tm0.tm_hour = tm.tm_hour;
2029       tm0.tm_mday = tm.tm_mday;
2030       tm0.tm_mon = tm.tm_mon;
2031       tm0.tm_year = tm.tm_year;
2032       tm0.tm_isdst = tm.tm_isdst;
2033       tm.tm_wday = -1;
2034 
2035       Start = mktime_z (tz, &tm);
2036 
2037       if (! mktime_ok (&tm0, &tm))
2038         {
2039           bool repaired = false;
2040           bool time_zone_seen = pc.zones_seen != 0;
2041           if (time_zone_seen)
2042             {
2043               /* Guard against falsely reporting errors near the time_t
2044                  boundaries when parsing times in other time zones.  For
2045                  example, suppose the input string "1969-12-31 23:00:00 -0100",
2046                  the current time zone is 8 hours ahead of UTC, and the min
2047                  time_t value is 1970-01-01 00:00:00 UTC.  Then the min
2048                  localtime value is 1970-01-01 08:00:00, and mktime will
2049                  therefore fail on 1969-12-31 23:00:00.  To work around the
2050                  problem, set the time zone to 1 hour behind UTC temporarily
2051                  by setting TZ="XXX1:00" and try mktime again.  */
2052 
2053               char tz2buf[sizeof "XXX" - 1 + TIME_ZONE_BUFSIZE];
2054               tz2buf[0] = tz2buf[1] = tz2buf[2] = 'X';
2055               time_zone_str (pc.time_zone, &tz2buf[3]);
2056               timezone_t tz2 = tzalloc (tz2buf);
2057               if (!tz2)
2058                 {
2059                   if (pc.parse_datetime_debug)
2060                     dbg_printf (_("error: tzalloc (\"%s\") failed\n"), tz2buf);
2061                   goto fail;
2062                 }
2063               tm.tm_sec = tm0.tm_sec;
2064               tm.tm_min = tm0.tm_min;
2065               tm.tm_hour = tm0.tm_hour;
2066               tm.tm_mday = tm0.tm_mday;
2067               tm.tm_mon = tm0.tm_mon;
2068               tm.tm_year = tm0.tm_year;
2069               tm.tm_isdst = tm0.tm_isdst;
2070               tm.tm_wday = -1;
2071               Start = mktime_z (tz2, &tm);
2072               repaired = mktime_ok (&tm0, &tm);
2073               tzfree (tz2);
2074             }
2075 
2076           if (! repaired)
2077             {
2078               debug_mktime_not_ok (&tm0, &tm, &pc, time_zone_seen);
2079               goto fail;
2080             }
2081         }
2082 
2083       char dbg_ord[DBGBUFSIZE];
2084 
2085       if (pc.days_seen && ! pc.dates_seen)
2086         {
2087           intmax_t dayincr;
2088           if (INT_MULTIPLY_WRAPV ((pc.day_ordinal
2089                                    - (0 < pc.day_ordinal
2090                                       && tm.tm_wday != pc.day_number)),
2091                                   7, &dayincr)
2092               || INT_ADD_WRAPV ((pc.day_number - tm.tm_wday + 7) % 7,
2093                                 dayincr, &dayincr)
2094               || INT_ADD_WRAPV (dayincr, tm.tm_mday, &tm.tm_mday))
2095             Start = -1;
2096           else
2097             {
2098               tm.tm_isdst = -1;
2099               Start = mktime_z (tz, &tm);
2100             }
2101 
2102           if (Start == (time_t) -1)
2103             {
2104               if (pc.parse_datetime_debug)
2105                 dbg_printf (_("error: day '%s' "
2106                               "(day ordinal=%"PRIdMAX" number=%d) "
2107                               "resulted in an invalid date: '%s'\n"),
2108                             str_days (&pc, dbg_ord, sizeof dbg_ord),
2109                             pc.day_ordinal, pc.day_number,
2110                             debug_strfdatetime (&tm, &pc, dbg_tm,
2111                                                 sizeof dbg_tm));
2112               goto fail;
2113             }
2114 
2115           if (pc.parse_datetime_debug)
2116             dbg_printf (_("new start date: '%s' is '%s'\n"),
2117                         str_days (&pc, dbg_ord, sizeof dbg_ord),
2118                         debug_strfdatetime (&tm, &pc, dbg_tm, sizeof dbg_tm));
2119 
2120         }
2121 
2122       if (pc.parse_datetime_debug)
2123         {
2124           if (!pc.dates_seen && !pc.days_seen)
2125             dbg_printf (_("using current date as starting value: '%s'\n"),
2126                         debug_strfdate (&tm, dbg_tm, sizeof dbg_tm));
2127 
2128           if (pc.days_seen && pc.dates_seen)
2129             dbg_printf (_("warning: day (%s) ignored when explicit dates "
2130                           "are given\n"),
2131                         str_days (&pc, dbg_ord, sizeof dbg_ord));
2132 
2133           dbg_printf (_("starting date/time: '%s'\n"),
2134                       debug_strfdatetime (&tm, &pc, dbg_tm, sizeof dbg_tm));
2135         }
2136 
2137       /* Add relative date.  */
2138       if (pc.rel.year | pc.rel.month | pc.rel.day)
2139         {
2140           if (pc.parse_datetime_debug)
2141             {
2142               if ((pc.rel.year != 0 || pc.rel.month != 0) && tm.tm_mday != 15)
2143                 dbg_printf (_("warning: when adding relative months/years, "
2144                               "it is recommended to specify the 15th of the "
2145                               "months\n"));
2146 
2147               if (pc.rel.day != 0 && tm.tm_hour != 12)
2148                 dbg_printf (_("warning: when adding relative days, "
2149                               "it is recommended to specify noon\n"));
2150             }
2151 
2152           int year, month, day;
2153           if (INT_ADD_WRAPV (tm.tm_year, pc.rel.year, &year)
2154               || INT_ADD_WRAPV (tm.tm_mon, pc.rel.month, &month)
2155               || INT_ADD_WRAPV (tm.tm_mday, pc.rel.day, &day))
2156             {
2157               if (pc.parse_datetime_debug)
2158                 dbg_printf (_("error: %s:%d\n"), __FILE__, __LINE__);
2159               goto fail;
2160             }
2161           tm.tm_year = year;
2162           tm.tm_mon = month;
2163           tm.tm_mday = day;
2164           tm.tm_hour = tm0.tm_hour;
2165           tm.tm_min = tm0.tm_min;
2166           tm.tm_sec = tm0.tm_sec;
2167           tm.tm_isdst = tm0.tm_isdst;
2168           Start = mktime_z (tz, &tm);
2169           if (Start == (time_t) -1)
2170             {
2171               if (pc.parse_datetime_debug)
2172                 dbg_printf (_("error: adding relative date resulted "
2173                               "in an invalid date: '%s'\n"),
2174                             debug_strfdatetime (&tm, &pc, dbg_tm,
2175                                                 sizeof dbg_tm));
2176               goto fail;
2177             }
2178 
2179           if (pc.parse_datetime_debug)
2180             {
2181               dbg_printf (_("after date adjustment "
2182                             "(%+"PRIdMAX" years, %+"PRIdMAX" months, "
2183                             "%+"PRIdMAX" days),\n"),
2184                           pc.rel.year, pc.rel.month, pc.rel.day);
2185               dbg_printf (_("    new date/time = '%s'\n"),
2186                           debug_strfdatetime (&tm, &pc, dbg_tm,
2187                                               sizeof dbg_tm));
2188 
2189               /* Warn about crossing DST due to time adjustment.
2190                  Example: https://bugs.gnu.org/8357
2191                  env TZ=Europe/Helsinki \
2192                    date --debug \
2193                         -d 'Mon Mar 28 00:36:07 2011 EEST 1 day ago'
2194 
2195                  This case is different than DST changes due to time adjustment,
2196                  i.e., "1 day ago" vs "24 hours ago" are calculated in different
2197                  places.
2198 
2199                  'tm0.tm_isdst' contains the DST of the input date,
2200                  'tm.tm_isdst' is the normalized result after calling
2201                  mktime (&tm).
2202               */
2203               if (tm0.tm_isdst != -1 && tm.tm_isdst != tm0.tm_isdst)
2204                 dbg_printf (_("warning: daylight saving time changed after "
2205                               "date adjustment\n"));
2206 
2207               /* Warn if the user did not ask to adjust days but mday changed,
2208                  or
2209                  user did not ask to adjust months/days but the month changed.
2210 
2211                  Example for first case:
2212                  2016-05-31 + 1 month => 2016-06-31 => 2016-07-01.
2213                  User asked to adjust month, but the day changed from 31 to 01.
2214 
2215                  Example for second case:
2216                  2016-02-29 + 1 year => 2017-02-29 => 2017-03-01.
2217                  User asked to adjust year, but the month changed from 02 to 03.
2218               */
2219               if (pc.rel.day == 0
2220                   && (tm.tm_mday != day
2221                       || (pc.rel.month == 0 && tm.tm_mon != month)))
2222                 {
2223                   dbg_printf (_("warning: month/year adjustment resulted in "
2224                                 "shifted dates:\n"));
2225                   char tm_year_buf[TM_YEAR_BUFSIZE];
2226                   dbg_printf (_("     adjusted Y M D: %s %02d %02d\n"),
2227                               tm_year_str (year, tm_year_buf), month + 1, day);
2228                   dbg_printf (_("   normalized Y M D: %s %02d %02d\n"),
2229                               tm_year_str (tm.tm_year, tm_year_buf),
2230                               tm.tm_mon + 1, tm.tm_mday);
2231                 }
2232             }
2233 
2234         }
2235 
2236       /* The only "output" of this if-block is an updated Start value,
2237          so this block must follow others that clobber Start.  */
2238       if (pc.zones_seen)
2239         {
2240           intmax_t delta = pc.time_zone, t1;
2241           bool overflow = false;
2242 #ifdef HAVE_TM_GMTOFF
2243           long int utcoff = tm.tm_gmtoff;
2244 #else
2245           time_t t = Start;
2246           struct tm gmt;
2247           int utcoff = (gmtime_r (&t, &gmt)
2248                         ? tm_diff (&tm, &gmt)
2249                         : (overflow = true, 0));
2250 #endif
2251           overflow |= INT_SUBTRACT_WRAPV (delta, utcoff, &delta);
2252           overflow |= INT_SUBTRACT_WRAPV (Start, delta, &t1);
2253           if (overflow || time_overflow (t1))
2254             {
2255               if (pc.parse_datetime_debug)
2256                 dbg_printf (_("error: timezone %d caused time_t overflow\n"),
2257                             pc.time_zone);
2258               goto fail;
2259             }
2260           Start = t1;
2261         }
2262 
2263       if (pc.parse_datetime_debug)
2264         {
2265           intmax_t Starti = Start;
2266           dbg_printf (_("'%s' = %"PRIdMAX" epoch-seconds\n"),
2267                       debug_strfdatetime (&tm, &pc, dbg_tm, sizeof dbg_tm),
2268                       Starti);
2269         }
2270 
2271 
2272       /* Add relative hours, minutes, and seconds.  On hosts that support
2273          leap seconds, ignore the possibility of leap seconds; e.g.,
2274          "+ 10 minutes" adds 600 seconds, even if one of them is a
2275          leap second.  Typically this is not what the user wants, but it's
2276          too hard to do it the other way, because the time zone indicator
2277          must be applied before relative times, and if mktime is applied
2278          again the time zone will be lost.  */
2279       {
2280         intmax_t orig_ns = pc.seconds.tv_nsec;
2281         intmax_t sum_ns = orig_ns + pc.rel.ns;
2282         int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
2283         int d4 = (sum_ns - normalized_ns) / BILLION;
2284         intmax_t d1, t1, d2, t2, t3, t4;
2285         if (INT_MULTIPLY_WRAPV (pc.rel.hour, 60 * 60, &d1)
2286             || INT_ADD_WRAPV (Start, d1, &t1)
2287             || INT_MULTIPLY_WRAPV (pc.rel.minutes, 60, &d2)
2288             || INT_ADD_WRAPV (t1, d2, &t2)
2289             || INT_ADD_WRAPV (t2, pc.rel.seconds, &t3)
2290             || INT_ADD_WRAPV (t3, d4, &t4)
2291             || time_overflow (t4))
2292           {
2293             if (pc.parse_datetime_debug)
2294               dbg_printf (_("error: adding relative time caused an "
2295                             "overflow\n"));
2296             goto fail;
2297           }
2298 
2299         result->tv_sec = t4;
2300         result->tv_nsec = normalized_ns;
2301 
2302         if (pc.parse_datetime_debug
2303             && (pc.rel.hour | pc.rel.minutes | pc.rel.seconds | pc.rel.ns))
2304           {
2305             dbg_printf (_("after time adjustment (%+"PRIdMAX" hours, "
2306                           "%+"PRIdMAX" minutes, "
2307                           "%+"PRIdMAX" seconds, %+d ns),\n"),
2308                         pc.rel.hour, pc.rel.minutes, pc.rel.seconds,
2309                         pc.rel.ns);
2310             dbg_printf (_("    new time = %"PRIdMAX" epoch-seconds\n"), t4);
2311 
2312             /* Warn about crossing DST due to time adjustment.
2313                Example: https://bugs.gnu.org/8357
2314                env TZ=Europe/Helsinki           \
2315                date --debug                                             \
2316                -d 'Mon Mar 28 00:36:07 2011 EEST 24 hours ago'
2317 
2318                This case is different than DST changes due to days adjustment,
2319                i.e., "1 day ago" vs "24 hours ago" are calculated in different
2320                places.
2321 
2322                'tm.tm_isdst' contains the date after date adjustment.  */
2323             struct tm lmt;
2324             if (tm.tm_isdst != -1 && localtime_rz (tz, &result->tv_sec, &lmt)
2325                 && tm.tm_isdst != lmt.tm_isdst)
2326               dbg_printf (_("warning: daylight saving time changed after "
2327                             "time adjustment\n"));
2328           }
2329       }
2330     }
2331 
2332   if (pc.parse_datetime_debug)
2333     {
2334       /* Special case: using 'date -u' simply set TZ=UTC0 */
2335       if (! tzstring)
2336         dbg_printf (_("timezone: system default\n"));
2337       else if (STREQ (tzstring, "UTC0"))
2338         dbg_printf (_("timezone: Universal Time\n"));
2339       else
2340         dbg_printf (_("timezone: TZ=\"%s\" environment value\n"), tzstring);
2341 
2342       intmax_t sec = result->tv_sec;
2343       int nsec = result->tv_nsec;
2344       dbg_printf (_("final: %"PRIdMAX".%09d (epoch-seconds)\n"),
2345                   sec, nsec);
2346 
2347       struct tm gmt, lmt;
2348       bool got_utc = !!gmtime_r (&result->tv_sec, &gmt);
2349       if (got_utc)
2350         dbg_printf (_("final: %s (UTC)\n"),
2351                     debug_strfdatetime (&gmt, NULL,
2352                                         dbg_tm, sizeof dbg_tm));
2353       if (localtime_rz (tz, &result->tv_sec, &lmt))
2354         {
2355 #ifdef HAVE_TM_GMTOFF
2356           bool got_utcoff = true;
2357           long int utcoff = lmt.tm_gmtoff;
2358 #else
2359           bool got_utcoff = got_utc;
2360           int utcoff;
2361           if (got_utcoff)
2362             utcoff = tm_diff (&lmt, &gmt);
2363 #endif
2364           if (got_utcoff)
2365             dbg_printf (_("final: %s (UTC%s)\n"),
2366                         debug_strfdatetime (&lmt, NULL, dbg_tm, sizeof dbg_tm),
2367                         time_zone_str (utcoff, time_zone_buf));
2368           else
2369             dbg_printf (_("final: %s (unknown time zone offset)\n"),
2370                         debug_strfdatetime (&lmt, NULL, dbg_tm, sizeof dbg_tm));
2371         }
2372     }
2373 
2374   ok = true;
2375 
2376  fail:
2377   if (tz != tzdefault)
2378     tzfree (tz);
2379   free (tz1alloc);
2380   return ok;
2381 }
2382 
2383 #if TEST
2384 
2385 int
main(int ac,char ** av)2386 main (int ac, char **av)
2387 {
2388   char buff[BUFSIZ];
2389 
2390   printf ("Enter date, or blank line to exit.\n\t> ");
2391   fflush (stdout);
2392 
2393   buff[BUFSIZ - 1] = '\0';
2394   while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
2395     {
2396       struct timespec d;
2397       struct tm const *tm;
2398       if (! parse_datetime (&d, buff, NULL))
2399         printf ("Bad format - couldn't convert.\n");
2400       else if (! (tm = localtime (&d.tv_sec)))
2401         {
2402           intmax_t sec = d.tv_sec;
2403           printf ("localtime (%"PRIdMAX") failed\n", sec);
2404         }
2405       else
2406         {
2407           int ns = d.tv_nsec;
2408           char tm_year_buf[TM_YEAR_BUFSIZE];
2409           printf ("%s-%02d-%02d %02d:%02d:%02d.%09d\n",
2410                   tm_year_str (tm->tm_year, tm_year_buf),
2411                   tm->tm_mon + 1, tm->tm_mday,
2412                   tm->tm_hour, tm->tm_min, tm->tm_sec, ns);
2413         }
2414       printf ("\t> ");
2415       fflush (stdout);
2416     }
2417   return 0;
2418 }
2419 #endif /* TEST */
2420