1 %{
2 /* Parse a string into an internal timestamp.
3 
4    Copyright (C) 1999-2000, 2002-2021 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 and 2020 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 "idx.h"
39 #include "intprops.h"
40 #include "timespec.h"
41 #include "verify.h"
42 #include "strftime.h"
43 
44 /* There's no need to extend the stack, so there's no need to involve
45    alloca.  */
46 #define YYSTACK_USE_ALLOCA 0
47 
48 /* Tell Bison how much stack space is needed.  20 should be plenty for
49    this grammar, which is not right recursive.  Beware setting it too
50    high, since that might cause problems on machines whose
51    implementations have lame stack-overflow checking.  */
52 #define YYMAXDEPTH 20
53 #define YYINITDEPTH YYMAXDEPTH
54 
55 /* Since the code of parse-datetime.y is not included in the Emacs executable
56    itself, there is no need to #define static in this file.  Even if
57    the code were included in the Emacs executable, it probably
58    wouldn't do any harm to #undef it here; this will only cause
59    problems if we try to write to a static variable, which I don't
60    think this code needs to do.  */
61 #ifdef emacs
62 # undef static
63 #endif
64 
65 #include <inttypes.h>
66 #include <c-ctype.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   idx_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   idx_t dates_seen;
217   idx_t days_seen;
218   idx_t local_zones_seen;
219   idx_t dsts_seen;
220   idx_t times_seen;
221   idx_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   idx_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 = pc->input;
1414           int sign;
1415           if (c == '-' || c == '+')
1416             {
1417               sign = c == '-' ? -1 : 1;
1418               while (c = *(pc->input = ++p), c_isspace (c))
1419                 continue;
1420               if (! c_isdigit (c))
1421                 /* skip the '-' sign */
1422                 continue;
1423             }
1424           else
1425             sign = 0;
1426 
1427           time_t value = 0;
1428           do
1429             {
1430               if (INT_MULTIPLY_WRAPV (value, 10, &value))
1431                 return '?';
1432               if (INT_ADD_WRAPV (value, sign < 0 ? '0' - c : c - '0', &value))
1433                 return '?';
1434               c = *++p;
1435             }
1436           while (c_isdigit (c));
1437 
1438           if ((c == '.' || c == ',') && c_isdigit (p[1]))
1439             {
1440               time_t s = value;
1441               int digits;
1442 
1443               /* Accumulate fraction, to ns precision.  */
1444               p++;
1445               int ns = *p++ - '0';
1446               for (digits = 2; digits <= LOG10_BILLION; digits++)
1447                 {
1448                   ns *= 10;
1449                   if (c_isdigit (*p))
1450                     ns += *p++ - '0';
1451                 }
1452 
1453               /* Skip excess digits, truncating toward -Infinity.  */
1454               if (sign < 0)
1455                 for (; c_isdigit (*p); p++)
1456                   if (*p != '0')
1457                     {
1458                       ns++;
1459                       break;
1460                     }
1461               while (c_isdigit (*p))
1462                 p++;
1463 
1464               /* Adjust to the timespec convention, which is that
1465                  tv_nsec is always a positive offset even if tv_sec is
1466                  negative.  */
1467               if (sign < 0 && ns)
1468                 {
1469                   if (INT_SUBTRACT_WRAPV (s, 1, &s))
1470                     return '?';
1471                   ns = BILLION - ns;
1472                 }
1473 
1474               lvalp->timespec.tv_sec = s;
1475               lvalp->timespec.tv_nsec = ns;
1476               pc->input = p;
1477               return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1478             }
1479           else
1480             {
1481               lvalp->textintval.negative = sign < 0;
1482               lvalp->textintval.value = value;
1483               lvalp->textintval.digits = p - pc->input;
1484               pc->input = p;
1485               return sign ? tSNUMBER : tUNUMBER;
1486             }
1487         }
1488 
1489       if (c_isalpha (c))
1490         {
1491           char buff[20];
1492           char *p = buff;
1493           table const *tp;
1494 
1495           do
1496             {
1497               if (p < buff + sizeof buff - 1)
1498                 *p++ = c;
1499               c = *++pc->input;
1500             }
1501           while (c_isalpha (c) || c == '.');
1502 
1503           *p = '\0';
1504           tp = lookup_word (pc, buff);
1505           if (! tp)
1506             {
1507               if (pc->parse_datetime_debug)
1508                 dbg_printf (_("error: unknown word '%s'\n"), buff);
1509               return '?';
1510             }
1511           lvalp->intval = tp->value;
1512           return tp->type;
1513         }
1514 
1515       if (c != '(')
1516         return to_uchar (*pc->input++);
1517 
1518       idx_t count = 0;
1519       do
1520         {
1521           c = *pc->input++;
1522           if (c == '\0')
1523             return c;
1524           if (c == '(')
1525             count++;
1526           else if (c == ')')
1527             count--;
1528         }
1529       while (count != 0);
1530     }
1531 }
1532 
1533 /* Do nothing if the parser reports an error.  */
1534 static int
yyerror(parser_control const * pc _GL_UNUSED,char const * s _GL_UNUSED)1535 yyerror (parser_control const *pc _GL_UNUSED,
1536          char const *s _GL_UNUSED)
1537 {
1538   return 0;
1539 }
1540 
1541 /* If *TM0 is the old and *TM1 is the new value of a struct tm after
1542    passing it to mktime_z, return true if it's OK.  It's not OK if
1543    mktime failed or if *TM0 has out-of-range mainline members.
1544    The caller should set TM1->tm_wday to -1 before calling mktime,
1545    as a negative tm_wday is how mktime failure is inferred.  */
1546 
1547 static bool
mktime_ok(struct tm const * tm0,struct tm const * tm1)1548 mktime_ok (struct tm const *tm0, struct tm const *tm1)
1549 {
1550   if (tm1->tm_wday < 0)
1551     return false;
1552 
1553   return ! ((tm0->tm_sec ^ tm1->tm_sec)
1554             | (tm0->tm_min ^ tm1->tm_min)
1555             | (tm0->tm_hour ^ tm1->tm_hour)
1556             | (tm0->tm_mday ^ tm1->tm_mday)
1557             | (tm0->tm_mon ^ tm1->tm_mon)
1558             | (tm0->tm_year ^ tm1->tm_year));
1559 }
1560 
1561 /* Debugging: format a 'struct tm' into a buffer, taking the parser's
1562    timezone information into account (if pc != NULL).  */
1563 static char const *
debug_strfdatetime(struct tm const * tm,parser_control const * pc,char * buf,int n)1564 debug_strfdatetime (struct tm const *tm, parser_control const *pc,
1565                     char *buf, int n)
1566 {
1567   /* TODO:
1568      1. find an optimal way to print date string in a clear and unambiguous
1569         format.  Currently, always add '(Y-M-D)' prefix.
1570         Consider '2016y01m10d'  or 'year(2016) month(01) day(10)'.
1571 
1572         If the user needs debug printing, it means he/she already having
1573         issues with the parsing - better to avoid formats that could
1574         be mis-interpreted (e.g., just YYYY-MM-DD).
1575 
1576      2. Can strftime be used instead?
1577         depends if it is portable and can print invalid dates on all systems.
1578 
1579      3. Print timezone information ?
1580 
1581      4. Print DST information ?
1582 
1583      5. Print nanosecond information ?
1584 
1585      NOTE:
1586      Printed date/time values might not be valid, e.g., '2016-02-31'
1587      or '2016-19-2016' .  These are the values as parsed from the user
1588      string, before validation.
1589   */
1590   int m = nstrftime (buf, n, "(Y-M-D) %Y-%m-%d %H:%M:%S", tm, 0, 0);
1591 
1592   /* If parser_control information was provided (for timezone),
1593      and there's enough space in the buffer, add timezone info.  */
1594   if (pc && m < n && pc->zones_seen)
1595     {
1596       int tz = pc->time_zone;
1597 
1598       /* Account for DST if tLOCAL_ZONE was seen.  */
1599       if (pc->local_zones_seen && !pc->zones_seen && 0 < pc->local_isdst)
1600         tz += 60 * 60;
1601 
1602       char time_zone_buf[TIME_ZONE_BUFSIZE];
1603       snprintf (&buf[m], n - m, " TZ=%s", time_zone_str (tz, time_zone_buf));
1604     }
1605   return buf;
1606 }
1607 
1608 static char const *
debug_strfdate(struct tm const * tm,char * buf,int n)1609 debug_strfdate (struct tm const *tm, char *buf, int n)
1610 {
1611   char tm_year_buf[TM_YEAR_BUFSIZE];
1612   snprintf (buf, n, "(Y-M-D) %s-%02d-%02d",
1613             tm_year_str (tm->tm_year, tm_year_buf),
1614             tm->tm_mon + 1, tm->tm_mday);
1615   return buf;
1616 }
1617 
1618 static char const *
debug_strftime(struct tm const * tm,char * buf,int n)1619 debug_strftime (struct tm const *tm, char *buf, int n)
1620 {
1621   snprintf (buf, n, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
1622   return buf;
1623 }
1624 
1625 /* If mktime_ok failed, display the failed time values,
1626    and provide possible hints.  Example output:
1627 
1628     date: error: invalid date/time value:
1629     date:     user provided time: '(Y-M-D) 2006-04-02 02:45:00'
1630     date:        normalized time: '(Y-M-D) 2006-04-02 03:45:00'
1631     date:                                             __
1632     date:      possible reasons:
1633     date:        non-existing due to daylight-saving time;
1634     date:        numeric values overflow;
1635     date:        missing timezone;
1636  */
1637 static void
debug_mktime_not_ok(struct tm const * tm0,struct tm const * tm1,parser_control const * pc,bool time_zone_seen)1638 debug_mktime_not_ok (struct tm const *tm0, struct tm const *tm1,
1639                      parser_control const *pc, bool time_zone_seen)
1640 {
1641   /* TODO: handle t == -1 (as in 'mktime_ok').  */
1642   char tmp[DBGBUFSIZE];
1643   int i;
1644   const bool eq_sec   = (tm0->tm_sec  == tm1->tm_sec);
1645   const bool eq_min   = (tm0->tm_min  == tm1->tm_min);
1646   const bool eq_hour  = (tm0->tm_hour == tm1->tm_hour);
1647   const bool eq_mday  = (tm0->tm_mday == tm1->tm_mday);
1648   const bool eq_month = (tm0->tm_mon  == tm1->tm_mon);
1649   const bool eq_year  = (tm0->tm_year == tm1->tm_year);
1650 
1651   const bool dst_shift = eq_sec && eq_min && !eq_hour
1652                          && eq_mday && eq_month && eq_year;
1653 
1654   if (!pc->parse_datetime_debug)
1655     return;
1656 
1657   dbg_printf (_("error: invalid date/time value:\n"));
1658   dbg_printf (_("    user provided time: '%s'\n"),
1659               debug_strfdatetime (tm0, pc, tmp, sizeof tmp));
1660   dbg_printf (_("       normalized time: '%s'\n"),
1661               debug_strfdatetime (tm1, pc, tmp, sizeof tmp));
1662   /* The format must be aligned with debug_strfdatetime and the two
1663      DEBUG statements above.  This string is not translated.  */
1664   i = snprintf (tmp, sizeof tmp,
1665                 "                                 %4s %2s %2s %2s %2s %2s",
1666                 eq_year ? "" : "----",
1667                 eq_month ? "" : "--",
1668                 eq_mday ? "" : "--",
1669                 eq_hour ? "" : "--",
1670                 eq_min ? "" : "--",
1671                 eq_sec ? "" : "--");
1672   /* Trim trailing whitespace.  */
1673   if (0 <= i)
1674     {
1675       if (sizeof tmp - 1 < i)
1676         i = sizeof tmp - 1;
1677       while (0 < i && tmp[i - 1] == ' ')
1678         --i;
1679       tmp[i] = '\0';
1680     }
1681   dbg_printf ("%s\n", tmp);
1682 
1683   dbg_printf (_("     possible reasons:\n"));
1684   if (dst_shift)
1685     dbg_printf (_("       non-existing due to daylight-saving time;\n"));
1686   if (!eq_mday && !eq_month)
1687     dbg_printf (_("       invalid day/month combination;\n"));
1688   dbg_printf (_("       numeric values overflow;\n"));
1689   dbg_printf ("       %s\n", (time_zone_seen ? _("incorrect timezone")
1690                               : _("missing timezone")));
1691 }
1692 
1693 /* The original interface: run with debug=false and the default timezone.   */
1694 bool
parse_datetime(struct timespec * result,char const * p,struct timespec const * now)1695 parse_datetime (struct timespec *result, char const *p,
1696                 struct timespec const *now)
1697 {
1698   char const *tzstring = getenv ("TZ");
1699   timezone_t tz = tzalloc (tzstring);
1700   if (!tz)
1701     return false;
1702   bool ok = parse_datetime2 (result, p, now, 0, tz, tzstring);
1703   tzfree (tz);
1704   return ok;
1705 }
1706 
1707 /* Parse a date/time string, storing the resulting time value into *RESULT.
1708    The string itself is pointed to by P.  Return true if successful.
1709    P can be an incomplete or relative time specification; if so, use
1710    *NOW as the basis for the returned time.  Default to timezone
1711    TZDEFAULT, which corresponds to tzalloc (TZSTRING).  */
1712 bool
parse_datetime2(struct timespec * result,char const * p,struct timespec const * now,unsigned int flags,timezone_t tzdefault,char const * tzstring)1713 parse_datetime2 (struct timespec *result, char const *p,
1714                  struct timespec const *now, unsigned int flags,
1715                  timezone_t tzdefault, char const *tzstring)
1716 {
1717   struct tm tm;
1718   struct tm tm0;
1719   char time_zone_buf[TIME_ZONE_BUFSIZE];
1720   char dbg_tm[DBGBUFSIZE];
1721   bool ok = false;
1722   char const *input_sentinel = p + strlen (p);
1723   char *tz1alloc = NULL;
1724 
1725   /* A reasonable upper bound for the size of ordinary TZ strings.
1726      Use heap allocation if TZ's length exceeds this.  */
1727   enum { TZBUFSIZE = 100 };
1728   char tz1buf[TZBUFSIZE];
1729 
1730   struct timespec gettime_buffer;
1731   if (! now)
1732     {
1733       gettime (&gettime_buffer);
1734       now = &gettime_buffer;
1735     }
1736 
1737   time_t Start = now->tv_sec;
1738   int Start_ns = now->tv_nsec;
1739 
1740   unsigned char c;
1741   while (c = *p, c_isspace (c))
1742     p++;
1743 
1744   timezone_t tz = tzdefault;
1745 
1746   /* Store a local copy prior to first "goto".  Without this, a prior use
1747      below of RELATIVE_TIME_0 on the RHS might translate to an assignment-
1748      to-temporary, which would trigger a -Wjump-misses-init warning.  */
1749   const relative_time rel_time_0 = RELATIVE_TIME_0;
1750 
1751   if (strncmp (p, "TZ=\"", 4) == 0)
1752     {
1753       char const *tzbase = p + 4;
1754       idx_t tzsize = 1;
1755       char const *s;
1756 
1757       for (s = tzbase; *s; s++, tzsize++)
1758         if (*s == '\\')
1759           {
1760             s++;
1761             if (! (*s == '\\' || *s == '"'))
1762               break;
1763           }
1764         else if (*s == '"')
1765           {
1766             timezone_t tz1;
1767             char *tz1string = tz1buf;
1768             char *z;
1769             if (TZBUFSIZE < tzsize)
1770               {
1771                 tz1alloc = malloc (tzsize);
1772                 if (!tz1alloc)
1773                   goto fail;
1774                 tz1string = tz1alloc;
1775               }
1776             z = tz1string;
1777             for (s = tzbase; *s != '"'; s++)
1778               *z++ = *(s += *s == '\\');
1779             *z = '\0';
1780             tz1 = tzalloc (tz1string);
1781             if (!tz1)
1782               goto fail;
1783             tz = tz1;
1784             tzstring = tz1string;
1785 
1786             p = s + 1;
1787             while (c = *p, c_isspace (c))
1788               p++;
1789 
1790             break;
1791           }
1792     }
1793 
1794   struct tm tmp;
1795   if (! localtime_rz (tz, &now->tv_sec, &tmp))
1796     goto fail;
1797 
1798   /* As documented, be careful to treat the empty string just like
1799      a date string of "0".  Without this, an empty string would be
1800      declared invalid when parsed during a DST transition.  */
1801   if (*p == '\0')
1802     p = "0";
1803 
1804   parser_control pc;
1805   pc.input = p;
1806   pc.parse_datetime_debug = (flags & PARSE_DATETIME_DEBUG) != 0;
1807   if (INT_ADD_WRAPV (tmp.tm_year, TM_YEAR_BASE, &pc.year.value))
1808     {
1809       if (pc.parse_datetime_debug)
1810         dbg_printf (_("error: initial year out of range\n"));
1811       goto fail;
1812     }
1813   pc.year.digits = 0;
1814   pc.month = tmp.tm_mon + 1;
1815   pc.day = tmp.tm_mday;
1816   pc.hour = tmp.tm_hour;
1817   pc.minutes = tmp.tm_min;
1818   pc.seconds.tv_sec = tmp.tm_sec;
1819   pc.seconds.tv_nsec = Start_ns;
1820   tm.tm_isdst = tmp.tm_isdst;
1821 
1822   pc.meridian = MER24;
1823   pc.rel = rel_time_0;
1824   pc.timespec_seen = false;
1825   pc.rels_seen = false;
1826   pc.dates_seen = 0;
1827   pc.days_seen = 0;
1828   pc.times_seen = 0;
1829   pc.local_zones_seen = 0;
1830   pc.dsts_seen = 0;
1831   pc.zones_seen = 0;
1832   pc.year_seen = false;
1833   pc.debug_dates_seen = false;
1834   pc.debug_days_seen = false;
1835   pc.debug_times_seen = false;
1836   pc.debug_local_zones_seen = false;
1837   pc.debug_zones_seen = false;
1838   pc.debug_year_seen = false;
1839   pc.debug_ordinal_day_seen = false;
1840 
1841 #if HAVE_STRUCT_TM_TM_ZONE
1842   pc.local_time_zone_table[0].name = tmp.tm_zone;
1843   pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1844   pc.local_time_zone_table[0].value = tmp.tm_isdst;
1845   pc.local_time_zone_table[1].name = NULL;
1846 
1847   /* Probe the names used in the next three calendar quarters, looking
1848      for a tm_isdst different from the one we already have.  */
1849   {
1850     int quarter;
1851     for (quarter = 1; quarter <= 3; quarter++)
1852       {
1853         time_t probe;
1854         if (INT_ADD_WRAPV (Start, quarter * (90 * 24 * 60 * 60), &probe))
1855           break;
1856         struct tm probe_tm;
1857         if (localtime_rz (tz, &probe, &probe_tm) && probe_tm.tm_zone
1858             && probe_tm.tm_isdst != pc.local_time_zone_table[0].value)
1859           {
1860               {
1861                 pc.local_time_zone_table[1].name = probe_tm.tm_zone;
1862                 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1863                 pc.local_time_zone_table[1].value = probe_tm.tm_isdst;
1864                 pc.local_time_zone_table[2].name = NULL;
1865               }
1866             break;
1867           }
1868       }
1869   }
1870 #else
1871 #if HAVE_TZNAME
1872   {
1873 # if !HAVE_DECL_TZNAME
1874     extern char *tzname[];
1875 # endif
1876     int i;
1877     for (i = 0; i < 2; i++)
1878       {
1879         pc.local_time_zone_table[i].name = tzname[i];
1880         pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1881         pc.local_time_zone_table[i].value = i;
1882       }
1883     pc.local_time_zone_table[i].name = NULL;
1884   }
1885 #else
1886   pc.local_time_zone_table[0].name = NULL;
1887 #endif
1888 #endif
1889 
1890   if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1891       && ! strcmp (pc.local_time_zone_table[0].name,
1892                    pc.local_time_zone_table[1].name))
1893     {
1894       /* This locale uses the same abbreviation for standard and
1895          daylight times.  So if we see that abbreviation, we don't
1896          know whether it's daylight time.  */
1897       pc.local_time_zone_table[0].value = -1;
1898       pc.local_time_zone_table[1].name = NULL;
1899     }
1900 
1901   if (yyparse (&pc) != 0)
1902     {
1903       if (pc.parse_datetime_debug)
1904         dbg_printf ((input_sentinel <= pc.input
1905                      ? _("error: parsing failed\n")
1906                      : _("error: parsing failed, stopped at '%s'\n")),
1907                     pc.input);
1908       goto fail;
1909     }
1910 
1911 
1912   /* Determine effective timezone source.  */
1913 
1914   if (pc.parse_datetime_debug)
1915     {
1916       dbg_printf (_("input timezone: "));
1917 
1918       if (pc.timespec_seen)
1919         fprintf (stderr, _("'@timespec' - always UTC"));
1920       else if (pc.zones_seen)
1921         fprintf (stderr, _("parsed date/time string"));
1922       else if (tzstring)
1923         {
1924           if (tz != tzdefault)
1925             fprintf (stderr, _("TZ=\"%s\" in date string"), tzstring);
1926           else if (STREQ (tzstring, "UTC0"))
1927             {
1928               /* Special case: 'date -u' sets TZ="UTC0".  */
1929               fprintf (stderr, _("TZ=\"UTC0\" environment value or -u"));
1930             }
1931           else
1932             fprintf (stderr, _("TZ=\"%s\" environment value"), tzstring);
1933         }
1934       else
1935         fprintf (stderr, _("system default"));
1936 
1937       /* Account for DST changes if tLOCAL_ZONE was seen.
1938          local timezone only changes DST and is relative to the
1939          default timezone.*/
1940       if (pc.local_zones_seen && !pc.zones_seen && 0 < pc.local_isdst)
1941         fprintf (stderr, ", dst");
1942 
1943       if (pc.zones_seen)
1944         fprintf (stderr, " (%s)", time_zone_str (pc.time_zone, time_zone_buf));
1945 
1946       fputc ('\n', stderr);
1947     }
1948 
1949   if (pc.timespec_seen)
1950     *result = pc.seconds;
1951   else
1952     {
1953       if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1954                | (pc.local_zones_seen + pc.zones_seen)))
1955         {
1956           if (pc.parse_datetime_debug)
1957             {
1958               if (pc.times_seen > 1)
1959                 dbg_printf ("error: seen multiple time parts\n");
1960               if (pc.dates_seen > 1)
1961                 dbg_printf ("error: seen multiple date parts\n");
1962               if (pc.days_seen > 1)
1963                 dbg_printf ("error: seen multiple days parts\n");
1964               if (pc.dsts_seen > 1)
1965                 dbg_printf ("error: seen multiple daylight-saving parts\n");
1966               if ((pc.local_zones_seen + pc.zones_seen) > 1)
1967                 dbg_printf ("error: seen multiple time-zone parts\n");
1968             }
1969           goto fail;
1970         }
1971 
1972       if (! to_tm_year (pc.year, pc.parse_datetime_debug, &tm.tm_year)
1973           || INT_ADD_WRAPV (pc.month, -1, &tm.tm_mon)
1974           || INT_ADD_WRAPV (pc.day, 0, &tm.tm_mday))
1975         {
1976           if (pc.parse_datetime_debug)
1977             dbg_printf (_("error: year, month, or day overflow\n"));
1978           goto fail;
1979         }
1980       if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1981         {
1982           tm.tm_hour = to_hour (pc.hour, pc.meridian);
1983           if (tm.tm_hour < 0)
1984             {
1985               char const *mrd = (pc.meridian == MERam ? "am"
1986                                  : pc.meridian == MERpm ?"pm" : "");
1987               if (pc.parse_datetime_debug)
1988                 dbg_printf (_("error: invalid hour %"PRIdMAX"%s\n"),
1989                             pc.hour, mrd);
1990               goto fail;
1991             }
1992           tm.tm_min = pc.minutes;
1993           tm.tm_sec = pc.seconds.tv_sec;
1994           if (pc.parse_datetime_debug)
1995             dbg_printf ((pc.times_seen
1996                          ? _("using specified time as starting value: '%s'\n")
1997                          : _("using current time as starting value: '%s'\n")),
1998                         debug_strftime (&tm, dbg_tm, sizeof dbg_tm));
1999         }
2000       else
2001         {
2002           tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
2003           pc.seconds.tv_nsec = 0;
2004           if (pc.parse_datetime_debug)
2005             dbg_printf ("warning: using midnight as starting time: 00:00:00\n");
2006         }
2007 
2008       /* Let mktime deduce tm_isdst if we have an absolute timestamp.  */
2009       if (pc.dates_seen | pc.days_seen | pc.times_seen)
2010         tm.tm_isdst = -1;
2011 
2012       /* But if the input explicitly specifies local time with or without
2013          DST, give mktime that information.  */
2014       if (pc.local_zones_seen)
2015         tm.tm_isdst = pc.local_isdst;
2016 
2017       tm0.tm_sec = tm.tm_sec;
2018       tm0.tm_min = tm.tm_min;
2019       tm0.tm_hour = tm.tm_hour;
2020       tm0.tm_mday = tm.tm_mday;
2021       tm0.tm_mon = tm.tm_mon;
2022       tm0.tm_year = tm.tm_year;
2023       tm0.tm_isdst = tm.tm_isdst;
2024       tm.tm_wday = -1;
2025 
2026       Start = mktime_z (tz, &tm);
2027 
2028       if (! mktime_ok (&tm0, &tm))
2029         {
2030           bool repaired = false;
2031           bool time_zone_seen = pc.zones_seen != 0;
2032           if (time_zone_seen)
2033             {
2034               /* Guard against falsely reporting errors near the time_t
2035                  boundaries when parsing times in other time zones.  For
2036                  example, suppose the input string "1969-12-31 23:00:00 -0100",
2037                  the current time zone is 8 hours ahead of UTC, and the min
2038                  time_t value is 1970-01-01 00:00:00 UTC.  Then the min
2039                  localtime value is 1970-01-01 08:00:00, and mktime will
2040                  therefore fail on 1969-12-31 23:00:00.  To work around the
2041                  problem, set the time zone to 1 hour behind UTC temporarily
2042                  by setting TZ="XXX1:00" and try mktime again.  */
2043 
2044               char tz2buf[sizeof "XXX" - 1 + TIME_ZONE_BUFSIZE];
2045               tz2buf[0] = tz2buf[1] = tz2buf[2] = 'X';
2046               time_zone_str (pc.time_zone, &tz2buf[3]);
2047               timezone_t tz2 = tzalloc (tz2buf);
2048               if (!tz2)
2049                 {
2050                   if (pc.parse_datetime_debug)
2051                     dbg_printf (_("error: tzalloc (\"%s\") failed\n"), tz2buf);
2052                   goto fail;
2053                 }
2054               tm.tm_sec = tm0.tm_sec;
2055               tm.tm_min = tm0.tm_min;
2056               tm.tm_hour = tm0.tm_hour;
2057               tm.tm_mday = tm0.tm_mday;
2058               tm.tm_mon = tm0.tm_mon;
2059               tm.tm_year = tm0.tm_year;
2060               tm.tm_isdst = tm0.tm_isdst;
2061               tm.tm_wday = -1;
2062               Start = mktime_z (tz2, &tm);
2063               repaired = mktime_ok (&tm0, &tm);
2064               tzfree (tz2);
2065             }
2066 
2067           if (! repaired)
2068             {
2069               debug_mktime_not_ok (&tm0, &tm, &pc, time_zone_seen);
2070               goto fail;
2071             }
2072         }
2073 
2074       char dbg_ord[DBGBUFSIZE];
2075 
2076       if (pc.days_seen && ! pc.dates_seen)
2077         {
2078           intmax_t dayincr;
2079           if (INT_MULTIPLY_WRAPV ((pc.day_ordinal
2080                                    - (0 < pc.day_ordinal
2081                                       && tm.tm_wday != pc.day_number)),
2082                                   7, &dayincr)
2083               || INT_ADD_WRAPV ((pc.day_number - tm.tm_wday + 7) % 7,
2084                                 dayincr, &dayincr)
2085               || INT_ADD_WRAPV (dayincr, tm.tm_mday, &tm.tm_mday))
2086             Start = -1;
2087           else
2088             {
2089               tm.tm_isdst = -1;
2090               Start = mktime_z (tz, &tm);
2091             }
2092 
2093           if (Start == (time_t) -1)
2094             {
2095               if (pc.parse_datetime_debug)
2096                 dbg_printf (_("error: day '%s' "
2097                               "(day ordinal=%"PRIdMAX" number=%d) "
2098                               "resulted in an invalid date: '%s'\n"),
2099                             str_days (&pc, dbg_ord, sizeof dbg_ord),
2100                             pc.day_ordinal, pc.day_number,
2101                             debug_strfdatetime (&tm, &pc, dbg_tm,
2102                                                 sizeof dbg_tm));
2103               goto fail;
2104             }
2105 
2106           if (pc.parse_datetime_debug)
2107             dbg_printf (_("new start date: '%s' is '%s'\n"),
2108                         str_days (&pc, dbg_ord, sizeof dbg_ord),
2109                         debug_strfdatetime (&tm, &pc, dbg_tm, sizeof dbg_tm));
2110 
2111         }
2112 
2113       if (pc.parse_datetime_debug)
2114         {
2115           if (!pc.dates_seen && !pc.days_seen)
2116             dbg_printf (_("using current date as starting value: '%s'\n"),
2117                         debug_strfdate (&tm, dbg_tm, sizeof dbg_tm));
2118 
2119           if (pc.days_seen && pc.dates_seen)
2120             dbg_printf (_("warning: day (%s) ignored when explicit dates "
2121                           "are given\n"),
2122                         str_days (&pc, dbg_ord, sizeof dbg_ord));
2123 
2124           dbg_printf (_("starting date/time: '%s'\n"),
2125                       debug_strfdatetime (&tm, &pc, dbg_tm, sizeof dbg_tm));
2126         }
2127 
2128       /* Add relative date.  */
2129       if (pc.rel.year | pc.rel.month | pc.rel.day)
2130         {
2131           if (pc.parse_datetime_debug)
2132             {
2133               if ((pc.rel.year != 0 || pc.rel.month != 0) && tm.tm_mday != 15)
2134                 dbg_printf (_("warning: when adding relative months/years, "
2135                               "it is recommended to specify the 15th of the "
2136                               "months\n"));
2137 
2138               if (pc.rel.day != 0 && tm.tm_hour != 12)
2139                 dbg_printf (_("warning: when adding relative days, "
2140                               "it is recommended to specify noon\n"));
2141             }
2142 
2143           int year, month, day;
2144           if (INT_ADD_WRAPV (tm.tm_year, pc.rel.year, &year)
2145               || INT_ADD_WRAPV (tm.tm_mon, pc.rel.month, &month)
2146               || INT_ADD_WRAPV (tm.tm_mday, pc.rel.day, &day))
2147             {
2148               if (pc.parse_datetime_debug)
2149                 dbg_printf (_("error: %s:%d\n"), __FILE__, __LINE__);
2150               goto fail;
2151             }
2152           tm.tm_year = year;
2153           tm.tm_mon = month;
2154           tm.tm_mday = day;
2155           tm.tm_hour = tm0.tm_hour;
2156           tm.tm_min = tm0.tm_min;
2157           tm.tm_sec = tm0.tm_sec;
2158           tm.tm_isdst = tm0.tm_isdst;
2159           Start = mktime_z (tz, &tm);
2160           if (Start == (time_t) -1)
2161             {
2162               if (pc.parse_datetime_debug)
2163                 dbg_printf (_("error: adding relative date resulted "
2164                               "in an invalid date: '%s'\n"),
2165                             debug_strfdatetime (&tm, &pc, dbg_tm,
2166                                                 sizeof dbg_tm));
2167               goto fail;
2168             }
2169 
2170           if (pc.parse_datetime_debug)
2171             {
2172               dbg_printf (_("after date adjustment "
2173                             "(%+"PRIdMAX" years, %+"PRIdMAX" months, "
2174                             "%+"PRIdMAX" days),\n"),
2175                           pc.rel.year, pc.rel.month, pc.rel.day);
2176               dbg_printf (_("    new date/time = '%s'\n"),
2177                           debug_strfdatetime (&tm, &pc, dbg_tm,
2178                                               sizeof dbg_tm));
2179 
2180               /* Warn about crossing DST due to time adjustment.
2181                  Example: https://bugs.gnu.org/8357
2182                  env TZ=Europe/Helsinki \
2183                    date --debug \
2184                         -d 'Mon Mar 28 00:36:07 2011 EEST 1 day ago'
2185 
2186                  This case is different than DST changes due to time adjustment,
2187                  i.e., "1 day ago" vs "24 hours ago" are calculated in different
2188                  places.
2189 
2190                  'tm0.tm_isdst' contains the DST of the input date,
2191                  'tm.tm_isdst' is the normalized result after calling
2192                  mktime (&tm).
2193               */
2194               if (tm0.tm_isdst != -1 && tm.tm_isdst != tm0.tm_isdst)
2195                 dbg_printf (_("warning: daylight saving time changed after "
2196                               "date adjustment\n"));
2197 
2198               /* Warn if the user did not ask to adjust days but mday changed,
2199                  or
2200                  user did not ask to adjust months/days but the month changed.
2201 
2202                  Example for first case:
2203                  2016-05-31 + 1 month => 2016-06-31 => 2016-07-01.
2204                  User asked to adjust month, but the day changed from 31 to 01.
2205 
2206                  Example for second case:
2207                  2016-02-29 + 1 year => 2017-02-29 => 2017-03-01.
2208                  User asked to adjust year, but the month changed from 02 to 03.
2209               */
2210               if (pc.rel.day == 0
2211                   && (tm.tm_mday != day
2212                       || (pc.rel.month == 0 && tm.tm_mon != month)))
2213                 {
2214                   dbg_printf (_("warning: month/year adjustment resulted in "
2215                                 "shifted dates:\n"));
2216                   char tm_year_buf[TM_YEAR_BUFSIZE];
2217                   dbg_printf (_("     adjusted Y M D: %s %02d %02d\n"),
2218                               tm_year_str (year, tm_year_buf), month + 1, day);
2219                   dbg_printf (_("   normalized Y M D: %s %02d %02d\n"),
2220                               tm_year_str (tm.tm_year, tm_year_buf),
2221                               tm.tm_mon + 1, tm.tm_mday);
2222                 }
2223             }
2224 
2225         }
2226 
2227       /* The only "output" of this if-block is an updated Start value,
2228          so this block must follow others that clobber Start.  */
2229       if (pc.zones_seen)
2230         {
2231           bool overflow = false;
2232 #ifdef HAVE_TM_GMTOFF
2233           long int utcoff = tm.tm_gmtoff;
2234 #else
2235           time_t t = Start;
2236           struct tm gmt;
2237           int utcoff = (gmtime_r (&t, &gmt)
2238                         ? tm_diff (&tm, &gmt)
2239                         : (overflow = true, 0));
2240 #endif
2241           intmax_t delta;
2242           overflow |= INT_SUBTRACT_WRAPV (pc.time_zone, utcoff, &delta);
2243           time_t t1;
2244           overflow |= INT_SUBTRACT_WRAPV (Start, delta, &t1);
2245           if (overflow)
2246             {
2247               if (pc.parse_datetime_debug)
2248                 dbg_printf (_("error: timezone %d caused time_t overflow\n"),
2249                             pc.time_zone);
2250               goto fail;
2251             }
2252           Start = t1;
2253         }
2254 
2255       if (pc.parse_datetime_debug)
2256         {
2257           intmax_t Starti = Start;
2258           dbg_printf (_("'%s' = %"PRIdMAX" epoch-seconds\n"),
2259                       debug_strfdatetime (&tm, &pc, dbg_tm, sizeof dbg_tm),
2260                       Starti);
2261         }
2262 
2263 
2264       /* Add relative hours, minutes, and seconds.  On hosts that support
2265          leap seconds, ignore the possibility of leap seconds; e.g.,
2266          "+ 10 minutes" adds 600 seconds, even if one of them is a
2267          leap second.  Typically this is not what the user wants, but it's
2268          too hard to do it the other way, because the time zone indicator
2269          must be applied before relative times, and if mktime is applied
2270          again the time zone will be lost.  */
2271       {
2272         intmax_t orig_ns = pc.seconds.tv_nsec;
2273         intmax_t sum_ns = orig_ns + pc.rel.ns;
2274         int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
2275         int d4 = (sum_ns - normalized_ns) / BILLION;
2276         intmax_t d1, t1, d2, t2, t3;
2277         time_t t4;
2278         if (INT_MULTIPLY_WRAPV (pc.rel.hour, 60 * 60, &d1)
2279             || INT_ADD_WRAPV (Start, d1, &t1)
2280             || INT_MULTIPLY_WRAPV (pc.rel.minutes, 60, &d2)
2281             || INT_ADD_WRAPV (t1, d2, &t2)
2282             || INT_ADD_WRAPV (t2, pc.rel.seconds, &t3)
2283             || INT_ADD_WRAPV (t3, d4, &t4))
2284           {
2285             if (pc.parse_datetime_debug)
2286               dbg_printf (_("error: adding relative time caused an "
2287                             "overflow\n"));
2288             goto fail;
2289           }
2290 
2291         result->tv_sec = t4;
2292         result->tv_nsec = normalized_ns;
2293 
2294         if (pc.parse_datetime_debug
2295             && (pc.rel.hour | pc.rel.minutes | pc.rel.seconds | pc.rel.ns))
2296           {
2297             dbg_printf (_("after time adjustment (%+"PRIdMAX" hours, "
2298                           "%+"PRIdMAX" minutes, "
2299                           "%+"PRIdMAX" seconds, %+d ns),\n"),
2300                         pc.rel.hour, pc.rel.minutes, pc.rel.seconds,
2301                         pc.rel.ns);
2302             intmax_t t4i = t4;
2303             dbg_printf (_("    new time = %"PRIdMAX" epoch-seconds\n"), t4i);
2304 
2305             /* Warn about crossing DST due to time adjustment.
2306                Example: https://bugs.gnu.org/8357
2307                env TZ=Europe/Helsinki           \
2308                date --debug                                             \
2309                -d 'Mon Mar 28 00:36:07 2011 EEST 24 hours ago'
2310 
2311                This case is different than DST changes due to days adjustment,
2312                i.e., "1 day ago" vs "24 hours ago" are calculated in different
2313                places.
2314 
2315                'tm.tm_isdst' contains the date after date adjustment.  */
2316             struct tm lmt;
2317             if (tm.tm_isdst != -1 && localtime_rz (tz, &result->tv_sec, &lmt)
2318                 && tm.tm_isdst != lmt.tm_isdst)
2319               dbg_printf (_("warning: daylight saving time changed after "
2320                             "time adjustment\n"));
2321           }
2322       }
2323     }
2324 
2325   if (pc.parse_datetime_debug)
2326     {
2327       /* Special case: using 'date -u' simply set TZ=UTC0 */
2328       if (! tzstring)
2329         dbg_printf (_("timezone: system default\n"));
2330       else if (STREQ (tzstring, "UTC0"))
2331         dbg_printf (_("timezone: Universal Time\n"));
2332       else
2333         dbg_printf (_("timezone: TZ=\"%s\" environment value\n"), tzstring);
2334 
2335       intmax_t sec = result->tv_sec;
2336       int nsec = result->tv_nsec;
2337       dbg_printf (_("final: %"PRIdMAX".%09d (epoch-seconds)\n"),
2338                   sec, nsec);
2339 
2340       struct tm gmt, lmt;
2341       bool got_utc = !!gmtime_r (&result->tv_sec, &gmt);
2342       if (got_utc)
2343         dbg_printf (_("final: %s (UTC)\n"),
2344                     debug_strfdatetime (&gmt, NULL,
2345                                         dbg_tm, sizeof dbg_tm));
2346       if (localtime_rz (tz, &result->tv_sec, &lmt))
2347         {
2348 #ifdef HAVE_TM_GMTOFF
2349           bool got_utcoff = true;
2350           long int utcoff = lmt.tm_gmtoff;
2351 #else
2352           bool got_utcoff = got_utc;
2353           int utcoff;
2354           if (got_utcoff)
2355             utcoff = tm_diff (&lmt, &gmt);
2356 #endif
2357           if (got_utcoff)
2358             dbg_printf (_("final: %s (UTC%s)\n"),
2359                         debug_strfdatetime (&lmt, NULL, dbg_tm, sizeof dbg_tm),
2360                         time_zone_str (utcoff, time_zone_buf));
2361           else
2362             dbg_printf (_("final: %s (unknown time zone offset)\n"),
2363                         debug_strfdatetime (&lmt, NULL, dbg_tm, sizeof dbg_tm));
2364         }
2365     }
2366 
2367   ok = true;
2368 
2369  fail:
2370   if (tz != tzdefault)
2371     tzfree (tz);
2372   free (tz1alloc);
2373   return ok;
2374 }
2375 
2376 #if TEST
2377 
2378 int
main(int ac,char ** av)2379 main (int ac, char **av)
2380 {
2381   char buff[BUFSIZ];
2382 
2383   printf ("Enter date, or blank line to exit.\n\t> ");
2384   fflush (stdout);
2385 
2386   buff[BUFSIZ - 1] = '\0';
2387   while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
2388     {
2389       struct timespec d;
2390       struct tm const *tm;
2391       if (! parse_datetime (&d, buff, NULL))
2392         printf ("Bad format - couldn't convert.\n");
2393       else if (! (tm = localtime (&d.tv_sec)))
2394         {
2395           intmax_t sec = d.tv_sec;
2396           printf ("localtime (%"PRIdMAX") failed\n", sec);
2397         }
2398       else
2399         {
2400           int ns = d.tv_nsec;
2401           char tm_year_buf[TM_YEAR_BUFSIZE];
2402           printf ("%s-%02d-%02d %02d:%02d:%02d.%09d\n",
2403                   tm_year_str (tm->tm_year, tm_year_buf),
2404                   tm->tm_mon + 1, tm->tm_mday,
2405                   tm->tm_hour, tm->tm_min, tm->tm_sec, ns);
2406         }
2407       printf ("\t> ");
2408       fflush (stdout);
2409     }
2410   return 0;
2411 }
2412 #endif /* TEST */
2413