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