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