exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / parse-datetime.y
blob447a943db10d810a847e7dc504f10eff8eea85ff
1 %{
2 /* Parse a string into an internal timestamp.
4 Copyright (C) 1999-2000, 2002-2024 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 and 2020 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 "idx.h"
39 #include "intprops.h"
40 #include "timespec.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 #include <inttypes.h>
55 #include <c-ctype.h>
56 #include <stdarg.h>
57 #include <stdckdint.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
62 #include "gettext.h"
64 #define _(str) gettext (str)
66 /* Bison's skeleton tests _STDLIB_H, while some stdlib.h headers
67 use _STDLIB_H_ as witness. Map the latter to the one bison uses. */
68 /* FIXME: this is temporary. Remove when we have a mechanism to ensure
69 that the version we're using is fixed, too. */
70 #ifdef _STDLIB_H_
71 # undef _STDLIB_H
72 # define _STDLIB_H 1
73 #endif
75 /* Shift A right by B bits portably, by dividing A by 2**B and
76 truncating towards minus infinity. A and B should be free of side
77 effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
78 INT_BITS is the number of useful bits in an int. GNU code can
79 assume that INT_BITS is at least 32.
81 ISO C99 says that A >> B is implementation-defined if A < 0. Some
82 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
83 right in the usual way when A < 0, so SHR falls back on division if
84 ordinary A >> B doesn't seem to be the usual signed shift. */
85 #define SHR(a, b) \
86 (-1 >> 1 == -1 \
87 ? (a) >> (b) \
88 : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
90 #define HOUR(x) (60 * 60 * (x))
92 #define STREQ(a, b) (strcmp (a, b) == 0)
94 /* Verify that time_t is an integer as POSIX requires, and that every
95 time_t value fits in intmax_t. Please file a bug report if these
96 assumptions are false on your platform. */
97 static_assert (TYPE_IS_INTEGER (time_t));
98 static_assert (!TYPE_SIGNED (time_t) || INTMAX_MIN <= TYPE_MINIMUM (time_t));
99 static_assert (TYPE_MAXIMUM (time_t) <= INTMAX_MAX);
101 /* True if N is out of range for time_t. */
102 static bool
103 time_overflow (intmax_t n)
105 return ! ((TYPE_SIGNED (time_t) ? TYPE_MINIMUM (time_t) <= n : 0 <= n)
106 && n <= TYPE_MAXIMUM (time_t));
109 /* Convert a possibly-signed character to an unsigned character. This is
110 a bit safer than casting to unsigned char, since it catches some type
111 errors that the cast doesn't. */
112 static unsigned char to_uchar (char ch) { return ch; }
114 static void _GL_ATTRIBUTE_FORMAT ((__printf__, 1, 2))
115 dbg_printf (char const *msg, ...)
117 va_list args;
118 /* TODO: use gnulib's 'program_name' instead? */
119 fputs ("date: ", stderr);
121 va_start (args, msg);
122 vfprintf (stderr, msg, args);
123 va_end (args);
127 /* An integer value, and the number of digits in its textual
128 representation. */
129 typedef struct
131 bool negative;
132 intmax_t value;
133 idx_t digits;
134 } textint;
136 /* An entry in the lexical lookup table. */
137 typedef struct
139 char const *name;
140 int type;
141 int value;
142 } table;
144 /* Meridian: am, pm, or 24-hour style. */
145 enum { MERam, MERpm, MER24 };
147 /* A reasonable upper bound for the buffer used in debug output. */
148 enum { DBGBUFSIZE = 100 };
150 enum { BILLION = 1000000000, LOG10_BILLION = 9 };
152 /* Relative times. */
153 typedef struct
155 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
156 intmax_t year;
157 intmax_t month;
158 intmax_t day;
159 intmax_t hour;
160 intmax_t minutes;
161 intmax_t seconds;
162 int ns;
163 } relative_time;
165 #if HAVE_COMPOUND_LITERALS
166 # define RELATIVE_TIME_0 ((relative_time) { 0, 0, 0, 0, 0, 0, 0 })
167 #else
168 static relative_time const RELATIVE_TIME_0;
169 #endif
171 /* Information passed to and from the parser. */
172 typedef struct
174 /* The input string remaining to be parsed. */
175 const char *input;
177 /* N, if this is the Nth Tuesday. */
178 intmax_t day_ordinal;
180 /* Day of week; Sunday is 0. */
181 int day_number;
183 /* tm_isdst flag for the local zone. */
184 int local_isdst;
186 /* Time zone, in seconds east of UT. */
187 int time_zone;
189 /* Style used for time. */
190 int meridian;
192 /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds. */
193 textint year;
194 intmax_t month;
195 intmax_t day;
196 intmax_t hour;
197 intmax_t minutes;
198 struct timespec seconds; /* includes nanoseconds */
200 /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
201 relative_time rel;
203 /* Presence or counts of nonterminals of various flavors parsed so far. */
204 bool timespec_seen;
205 bool rels_seen;
206 idx_t dates_seen;
207 idx_t days_seen;
208 idx_t J_zones_seen;
209 idx_t local_zones_seen;
210 idx_t dsts_seen;
211 idx_t times_seen;
212 idx_t zones_seen;
213 bool year_seen;
215 #ifdef GNULIB_PARSE_DATETIME2
216 /* Print debugging output to stderr. */
217 bool parse_datetime_debug;
218 #endif
220 /* Which of the 'seen' parts have been printed when debugging. */
221 bool debug_dates_seen;
222 bool debug_days_seen;
223 bool debug_local_zones_seen;
224 bool debug_times_seen;
225 bool debug_zones_seen;
226 bool debug_year_seen;
228 /* The user specified explicit ordinal day value. */
229 bool debug_ordinal_day_seen;
231 /* Table of local time zone abbreviations, terminated by a null entry. */
232 table local_time_zone_table[3];
233 } parser_control;
235 static bool
236 debugging (parser_control const *pc)
238 #ifdef GNULIB_PARSE_DATETIME2
239 return pc->parse_datetime_debug;
240 #else
241 return false;
242 #endif
245 union YYSTYPE;
246 static int yylex (union YYSTYPE *, parser_control *);
247 static void yyerror (parser_control const *, char const *);
248 static bool time_zone_hhmm (parser_control *, textint, intmax_t);
250 /* Extract into *PC any date and time info from a string of digits
251 of the form e.g., YYYYMMDD, YYMMDD, HHMM, HH (and sometimes YYY,
252 YYYY, ...). */
253 static void
254 digits_to_date_time (parser_control *pc, textint text_int)
256 if (pc->dates_seen && ! pc->year.digits
257 && ! pc->rels_seen && (pc->times_seen || 2 < text_int.digits))
259 pc->year_seen = true;
260 pc->year = text_int;
262 else
264 if (4 < text_int.digits)
266 pc->dates_seen++;
267 pc->day = text_int.value % 100;
268 pc->month = (text_int.value / 100) % 100;
269 pc->year.value = text_int.value / 10000;
270 pc->year.digits = text_int.digits - 4;
272 else
274 pc->times_seen++;
275 if (text_int.digits <= 2)
277 pc->hour = text_int.value;
278 pc->minutes = 0;
280 else
282 pc->hour = text_int.value / 100;
283 pc->minutes = text_int.value % 100;
285 pc->seconds = (struct timespec) {0};
286 pc->meridian = MER24;
291 /* Increment PC->rel by FACTOR * REL (FACTOR is 1 or -1). Return true
292 if successful, false if an overflow occurred. */
293 static bool
294 apply_relative_time (parser_control *pc, relative_time rel, int factor)
296 if (factor < 0
297 ? (ckd_sub (&pc->rel.ns, pc->rel.ns, rel.ns)
298 | ckd_sub (&pc->rel.seconds, pc->rel.seconds, rel.seconds)
299 | ckd_sub (&pc->rel.minutes, pc->rel.minutes, rel.minutes)
300 | ckd_sub (&pc->rel.hour, pc->rel.hour, rel.hour)
301 | ckd_sub (&pc->rel.day, pc->rel.day, rel.day)
302 | ckd_sub (&pc->rel.month, pc->rel.month, rel.month)
303 | ckd_sub (&pc->rel.year, pc->rel.year, rel.year))
304 : (ckd_add (&pc->rel.ns, pc->rel.ns, rel.ns)
305 | ckd_add (&pc->rel.seconds, pc->rel.seconds, rel.seconds)
306 | ckd_add (&pc->rel.minutes, pc->rel.minutes, rel.minutes)
307 | ckd_add (&pc->rel.hour, pc->rel.hour, rel.hour)
308 | ckd_add (&pc->rel.day, pc->rel.day, rel.day)
309 | ckd_add (&pc->rel.month, pc->rel.month, rel.month)
310 | ckd_add (&pc->rel.year, pc->rel.year, rel.year)))
311 return false;
312 pc->rels_seen = true;
313 return true;
316 /* Set PC-> hour, minutes, seconds and nanoseconds members from arguments. */
317 static void
318 set_hhmmss (parser_control *pc, intmax_t hour, intmax_t minutes,
319 time_t sec, int nsec)
321 pc->hour = hour;
322 pc->minutes = minutes;
323 pc->seconds = (struct timespec) { .tv_sec = sec, .tv_nsec = nsec };
326 /* Return a textual representation of the day ordinal/number values
327 in the parser_control struct (e.g., "last wed", "this tues", "thu"). */
328 static const char *
329 str_days (parser_control *pc, char *buffer, int n)
331 /* TODO: use relative_time_table for reverse lookup. */
332 static char const ordinal_values[][11] = {
333 "last",
334 "this",
335 "next/first",
336 "(SECOND)", /* SECOND is commented out in relative_time_table. */
337 "third",
338 "fourth",
339 "fifth",
340 "sixth",
341 "seventh",
342 "eight",
343 "ninth",
344 "tenth",
345 "eleventh",
346 "twelfth"
349 static char const days_values[][4] = {
350 "Sun",
351 "Mon",
352 "Tue",
353 "Wed",
354 "Thu",
355 "Fri",
356 "Sat"
359 int len;
361 /* Don't add an ordinal prefix if the user didn't specify it
362 (e.g., "this wed" vs "wed"). */
363 if (pc->debug_ordinal_day_seen)
365 /* Use word description if possible (e.g., -1 = last, 3 = third). */
366 len = (-1 <= pc->day_ordinal && pc->day_ordinal <= 12
367 ? snprintf (buffer, n, "%s", ordinal_values[pc->day_ordinal + 1])
368 : snprintf (buffer, n, "%"PRIdMAX, pc->day_ordinal));
370 else
372 buffer[0] = '\0';
373 len = 0;
376 /* Add the day name */
377 if (0 <= pc->day_number && pc->day_number <= 6 && 0 <= len && len < n)
378 snprintf (buffer + len, n - len, &" %s"[len == 0],
379 days_values[pc->day_number]);
380 else
382 /* invalid day_number value - should never happen */
384 return buffer;
387 /* Convert a time zone to its string representation. */
389 enum { TIME_ZONE_BUFSIZE = INT_STRLEN_BOUND (intmax_t) + sizeof ":MM:SS" } ;
391 static char const *
392 time_zone_str (int time_zone, char time_zone_buf[TIME_ZONE_BUFSIZE])
394 char *p = time_zone_buf;
395 char sign = time_zone < 0 ? '-' : '+';
396 int hour = abs (time_zone / (60 * 60));
397 p += sprintf (time_zone_buf, "%c%02d", sign, hour);
398 int offset_from_hour = abs (time_zone % (60 * 60));
399 if (offset_from_hour != 0)
401 int mm = offset_from_hour / 60;
402 int ss = offset_from_hour % 60;
403 *p++ = ':';
404 *p++ = '0' + mm / 10;
405 *p++ = '0' + mm % 10;
406 if (ss)
408 *p++ = ':';
409 *p++ = '0' + ss / 10;
410 *p++ = '0' + ss % 10;
412 *p = '\0';
414 return time_zone_buf;
417 /* debugging: print the current time in the parser_control structure.
418 The parser will increment "*_seen" members for those which were parsed.
419 This function will print only newly seen parts. */
420 static void
421 debug_print_current_time (char const *item, parser_control *pc)
423 bool space = false;
425 if (!debugging (pc))
426 return;
428 /* no newline, more items printed below */
429 dbg_printf (_("parsed %s part: "), item);
431 if (pc->dates_seen && !pc->debug_dates_seen)
433 /*TODO: use pc->year.negative? */
434 fprintf (stderr, "(Y-M-D) %04"PRIdMAX"-%02"PRIdMAX"-%02"PRIdMAX,
435 pc->year.value, pc->month, pc->day);
436 pc->debug_dates_seen = true;
437 space = true;
440 if (pc->year_seen != pc->debug_year_seen)
442 if (space)
443 fputc (' ', stderr);
444 fprintf (stderr, _("year: %04"PRIdMAX), pc->year.value);
446 pc->debug_year_seen = pc->year_seen;
447 space = true;
450 if (pc->times_seen && !pc->debug_times_seen)
452 intmax_t sec = pc->seconds.tv_sec;
453 fprintf (stderr, &" %02"PRIdMAX":%02"PRIdMAX":%02"PRIdMAX[!space],
454 pc->hour, pc->minutes, sec);
455 if (pc->seconds.tv_nsec != 0)
457 int nsec = pc->seconds.tv_nsec;
458 fprintf (stderr, ".%09d", nsec);
460 if (pc->meridian == MERpm)
461 fputs ("pm", stderr);
463 pc->debug_times_seen = true;
464 space = true;
467 if (pc->days_seen && !pc->debug_days_seen)
469 if (space)
470 fputc (' ', stderr);
471 char tmp[DBGBUFSIZE];
472 fprintf (stderr, _("%s (day ordinal=%"PRIdMAX" number=%d)"),
473 str_days (pc, tmp, sizeof tmp),
474 pc->day_ordinal, pc->day_number);
475 pc->debug_days_seen = true;
476 space = true;
479 /* local zone strings only change the DST settings,
480 not the timezone value. If seen, inform about the DST. */
481 if (pc->local_zones_seen && !pc->debug_local_zones_seen)
483 fprintf (stderr, &" isdst=%d%s"[!space],
484 pc->local_isdst, pc->dsts_seen ? " DST" : "");
485 pc->debug_local_zones_seen = true;
486 space = true;
489 if (pc->zones_seen && !pc->debug_zones_seen)
491 char time_zone_buf[TIME_ZONE_BUFSIZE];
492 fprintf (stderr, &" UTC%s"[!space],
493 time_zone_str (pc->time_zone, time_zone_buf));
494 pc->debug_zones_seen = true;
495 space = true;
498 if (pc->timespec_seen)
500 intmax_t sec = pc->seconds.tv_sec;
501 if (space)
502 fputc (' ', stderr);
503 fprintf (stderr, _("number of seconds: %"PRIdMAX), sec);
506 fputc ('\n', stderr);
509 /* Debugging: print the current relative values. */
511 static bool
512 print_rel_part (bool space, intmax_t val, char const *name)
514 if (val == 0)
515 return space;
516 fprintf (stderr, &" %+"PRIdMAX" %s"[!space], val, name);
517 return true;
520 static void
521 debug_print_relative_time (char const *item, parser_control const *pc)
523 bool space = false;
525 if (!debugging (pc))
526 return;
528 /* no newline, more items printed below */
529 dbg_printf (_("parsed %s part: "), item);
531 if (pc->rel.year == 0 && pc->rel.month == 0 && pc->rel.day == 0
532 && pc->rel.hour == 0 && pc->rel.minutes == 0 && pc->rel.seconds == 0
533 && pc->rel.ns == 0)
535 /* Special case: relative time of this/today/now */
536 fputs (_("today/this/now\n"), stderr);
537 return;
540 space = print_rel_part (space, pc->rel.year, "year(s)");
541 space = print_rel_part (space, pc->rel.month, "month(s)");
542 space = print_rel_part (space, pc->rel.day, "day(s)");
543 space = print_rel_part (space, pc->rel.hour, "hour(s)");
544 space = print_rel_part (space, pc->rel.minutes, "minutes");
545 space = print_rel_part (space, pc->rel.seconds, "seconds");
546 print_rel_part (space, pc->rel.ns, "nanoseconds");
548 fputc ('\n', stderr);
555 /* We want a reentrant parser, even if the TZ manipulation and the calls to
556 localtime and gmtime are not reentrant. */
557 %define api.pure
558 %parse-param { parser_control *pc }
559 %lex-param { parser_control *pc }
561 /* This grammar has 31 shift/reduce conflicts. */
562 %expect 31
564 %union
566 intmax_t intval;
567 textint textintval;
568 struct timespec timespec;
569 relative_time rel;
572 %token <intval> tAGO
573 %token tDST
575 %token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT
576 %token <intval> tDAY_UNIT tDAY_SHIFT
578 %token <intval> tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN
579 %token <intval> tMONTH tORDINAL tZONE
581 %token <textintval> tSNUMBER tUNUMBER
582 %token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER
584 %type <intval> o_colon_minutes
585 %type <timespec> seconds signed_seconds unsigned_seconds
587 %type <rel> relunit relunit_snumber dayshift
591 spec:
592 timespec
593 | items
596 timespec:
597 '@' seconds
599 pc->seconds = $2;
600 pc->timespec_seen = true;
601 debug_print_current_time (_("number of seconds"), pc);
605 items:
606 /* empty */
607 | items item
610 item:
611 datetime
613 pc->times_seen++; pc->dates_seen++;
614 debug_print_current_time (_("datetime"), pc);
616 | time
618 pc->times_seen++;
619 debug_print_current_time (_("time"), pc);
621 | local_zone
623 pc->local_zones_seen++;
624 debug_print_current_time (_("local_zone"), pc);
626 | 'J'
628 pc->J_zones_seen++;
629 debug_print_current_time ("J", pc);
631 | zone
633 pc->zones_seen++;
634 debug_print_current_time (_("zone"), pc);
636 | date
638 pc->dates_seen++;
639 debug_print_current_time (_("date"), pc);
641 | day
643 pc->days_seen++;
644 debug_print_current_time (_("day"), pc);
646 | rel
648 debug_print_relative_time (_("relative"), pc);
650 | number
652 debug_print_current_time (_("number"), pc);
654 | hybrid
656 debug_print_relative_time (_("hybrid"), pc);
660 datetime:
661 iso_8601_datetime
664 iso_8601_datetime:
665 iso_8601_date 'T' iso_8601_time
668 time:
669 tUNUMBER tMERIDIAN
671 set_hhmmss (pc, $1.value, 0, 0, 0);
672 pc->meridian = $2;
674 | tUNUMBER ':' tUNUMBER tMERIDIAN
676 set_hhmmss (pc, $1.value, $3.value, 0, 0);
677 pc->meridian = $4;
679 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tMERIDIAN
681 set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
682 pc->meridian = $6;
684 | iso_8601_time
687 iso_8601_time:
688 tUNUMBER zone_offset
690 set_hhmmss (pc, $1.value, 0, 0, 0);
691 pc->meridian = MER24;
693 | tUNUMBER ':' tUNUMBER o_zone_offset
695 set_hhmmss (pc, $1.value, $3.value, 0, 0);
696 pc->meridian = MER24;
698 | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_zone_offset
700 set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
701 pc->meridian = MER24;
705 o_zone_offset:
706 /* empty */
707 | zone_offset
710 zone_offset:
711 tSNUMBER o_colon_minutes
713 pc->zones_seen++;
714 if (! time_zone_hhmm (pc, $1, $2)) YYABORT;
718 /* Local zone strings affect only the DST setting, and take effect
719 only if the current TZ setting is relevant.
721 Example 1:
722 'EEST' is parsed as tLOCAL_ZONE, as it relates to the effective TZ:
723 TZ='Europe/Helsinki' date -d '2016-06-30 EEST'
725 Example 2:
726 'EEST' is parsed as tDAYZONE:
727 TZ='Asia/Tokyo' date -d '2016-06-30 EEST'
729 This is implemented by probing the next three calendar quarters
730 of the effective timezone and looking for DST changes -
731 if found, the timezone name (EEST) is inserted into
732 the lexical lookup table with type tLOCAL_ZONE.
733 (Search for 'quarter' comment in 'parse_datetime2'.)
735 local_zone:
736 tLOCAL_ZONE
737 { pc->local_isdst = $1; }
738 | tLOCAL_ZONE tDST
740 pc->local_isdst = 1;
741 pc->dsts_seen++;
745 /* Note 'T' is a special case, as it is used as the separator in ISO
746 8601 date and time of day representation. */
747 zone:
748 tZONE
749 { pc->time_zone = $1; }
750 | 'T'
751 { pc->time_zone = -HOUR (7); }
752 | tZONE relunit_snumber
753 { pc->time_zone = $1;
754 if (! apply_relative_time (pc, $2, 1)) YYABORT;
755 debug_print_relative_time (_("relative"), pc);
757 | 'T' relunit_snumber
758 { pc->time_zone = -HOUR (7);
759 if (! apply_relative_time (pc, $2, 1)) YYABORT;
760 debug_print_relative_time (_("relative"), pc);
762 | tZONE tSNUMBER o_colon_minutes
763 { if (! time_zone_hhmm (pc, $2, $3)) YYABORT;
764 if (ckd_add (&pc->time_zone, pc->time_zone, $1)) YYABORT; }
765 | tDAYZONE
766 { pc->time_zone = $1 + 60 * 60; }
767 | tZONE tDST
768 { pc->time_zone = $1 + 60 * 60; }
771 day:
772 tDAY
774 pc->day_ordinal = 0;
775 pc->day_number = $1;
777 | tDAY ','
779 pc->day_ordinal = 0;
780 pc->day_number = $1;
782 | tORDINAL tDAY
784 pc->day_ordinal = $1;
785 pc->day_number = $2;
786 pc->debug_ordinal_day_seen = true;
788 | tUNUMBER tDAY
790 pc->day_ordinal = $1.value;
791 pc->day_number = $2;
792 pc->debug_ordinal_day_seen = true;
796 date:
797 tUNUMBER '/' tUNUMBER
799 pc->month = $1.value;
800 pc->day = $3.value;
802 | tUNUMBER '/' tUNUMBER '/' tUNUMBER
804 /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
805 otherwise as MM/DD/YY.
806 The goal in recognizing YYYY/MM/DD is solely to support legacy
807 machine-generated dates like those in an RCS log listing. If
808 you want portability, use the ISO 8601 format. */
809 if (4 <= $1.digits)
811 if (debugging (pc))
813 intmax_t digits = $1.digits;
814 dbg_printf (_("warning: value %"PRIdMAX" has %"PRIdMAX" digits. "
815 "Assuming YYYY/MM/DD\n"),
816 $1.value, digits);
819 pc->year = $1;
820 pc->month = $3.value;
821 pc->day = $5.value;
823 else
825 if (debugging (pc))
826 dbg_printf (_("warning: value %"PRIdMAX" has less than 4 digits. "
827 "Assuming MM/DD/YY[YY]\n"),
828 $1.value);
830 pc->month = $1.value;
831 pc->day = $3.value;
832 pc->year = $5;
835 | tUNUMBER tMONTH tSNUMBER
837 /* E.g., 17-JUN-1992. */
838 pc->day = $1.value;
839 pc->month = $2;
840 if (ckd_sub (&pc->year.value, 0, $3.value)) YYABORT;
841 pc->year.digits = $3.digits;
843 | tMONTH tSNUMBER tSNUMBER
845 /* E.g., JUN-17-1992. */
846 pc->month = $1;
847 if (ckd_sub (&pc->day, 0, $2.value)) YYABORT;
848 if (ckd_sub (&pc->year.value, 0, $3.value)) YYABORT;
849 pc->year.digits = $3.digits;
851 | tMONTH tUNUMBER
853 pc->month = $1;
854 pc->day = $2.value;
856 | tMONTH tUNUMBER ',' tUNUMBER
858 pc->month = $1;
859 pc->day = $2.value;
860 pc->year = $4;
862 | tUNUMBER tMONTH
864 pc->day = $1.value;
865 pc->month = $2;
867 | tUNUMBER tMONTH tUNUMBER
869 pc->day = $1.value;
870 pc->month = $2;
871 pc->year = $3;
873 | iso_8601_date
876 iso_8601_date:
877 tUNUMBER tSNUMBER tSNUMBER
879 /* ISO 8601 format. YYYY-MM-DD. */
880 pc->year = $1;
881 if (ckd_sub (&pc->month, 0, $2.value)) YYABORT;
882 if (ckd_sub (&pc->day, 0, $3.value)) YYABORT;
886 rel:
887 relunit tAGO
888 { if (! apply_relative_time (pc, $1, $2)) YYABORT; }
889 | relunit
890 { if (! apply_relative_time (pc, $1, 1)) YYABORT; }
891 | dayshift
892 { if (! apply_relative_time (pc, $1, 1)) YYABORT; }
895 relunit:
896 tORDINAL tYEAR_UNIT
897 { $$ = RELATIVE_TIME_0; $$.year = $1; }
898 | tUNUMBER tYEAR_UNIT
899 { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
900 | tYEAR_UNIT
901 { $$ = RELATIVE_TIME_0; $$.year = 1; }
902 | tORDINAL tMONTH_UNIT
903 { $$ = RELATIVE_TIME_0; $$.month = $1; }
904 | tUNUMBER tMONTH_UNIT
905 { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
906 | tMONTH_UNIT
907 { $$ = RELATIVE_TIME_0; $$.month = 1; }
908 | tORDINAL tDAY_UNIT
909 { $$ = RELATIVE_TIME_0;
910 if (ckd_mul (&$$.day, $1, $2)) YYABORT; }
911 | tUNUMBER tDAY_UNIT
912 { $$ = RELATIVE_TIME_0;
913 if (ckd_mul (&$$.day, $1.value, $2)) YYABORT; }
914 | tDAY_UNIT
915 { $$ = RELATIVE_TIME_0; $$.day = $1; }
916 | tORDINAL tHOUR_UNIT
917 { $$ = RELATIVE_TIME_0; $$.hour = $1; }
918 | tUNUMBER tHOUR_UNIT
919 { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
920 | tHOUR_UNIT
921 { $$ = RELATIVE_TIME_0; $$.hour = 1; }
922 | tORDINAL tMINUTE_UNIT
923 { $$ = RELATIVE_TIME_0; $$.minutes = $1; }
924 | tUNUMBER tMINUTE_UNIT
925 { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
926 | tMINUTE_UNIT
927 { $$ = RELATIVE_TIME_0; $$.minutes = 1; }
928 | tORDINAL tSEC_UNIT
929 { $$ = RELATIVE_TIME_0; $$.seconds = $1; }
930 | tUNUMBER tSEC_UNIT
931 { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
932 | tSDECIMAL_NUMBER tSEC_UNIT
933 { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
934 | tUDECIMAL_NUMBER tSEC_UNIT
935 { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
936 | tSEC_UNIT
937 { $$ = RELATIVE_TIME_0; $$.seconds = 1; }
938 | relunit_snumber
941 relunit_snumber:
942 tSNUMBER tYEAR_UNIT
943 { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
944 | tSNUMBER tMONTH_UNIT
945 { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
946 | tSNUMBER tDAY_UNIT
947 { $$ = RELATIVE_TIME_0;
948 if (ckd_mul (&$$.day, $1.value, $2)) YYABORT; }
949 | tSNUMBER tHOUR_UNIT
950 { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
951 | tSNUMBER tMINUTE_UNIT
952 { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
953 | tSNUMBER tSEC_UNIT
954 { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
957 dayshift:
958 tDAY_SHIFT
959 { $$ = RELATIVE_TIME_0; $$.day = $1; }
962 seconds: signed_seconds | unsigned_seconds;
964 signed_seconds:
965 tSDECIMAL_NUMBER
966 | tSNUMBER
967 { if (time_overflow ($1.value)) YYABORT;
968 $$ = (struct timespec) { .tv_sec = $1.value }; }
971 unsigned_seconds:
972 tUDECIMAL_NUMBER
973 | tUNUMBER
974 { if (time_overflow ($1.value)) YYABORT;
975 $$ = (struct timespec) { .tv_sec = $1.value }; }
978 number:
979 tUNUMBER
980 { digits_to_date_time (pc, $1); }
983 hybrid:
984 tUNUMBER relunit_snumber
986 /* Hybrid all-digit and relative offset, so that we accept e.g.,
987 "YYYYMMDD +N days" as well as "YYYYMMDD N days". */
988 digits_to_date_time (pc, $1);
989 if (! apply_relative_time (pc, $2, 1)) YYABORT;
993 o_colon_minutes:
994 /* empty */
995 { $$ = -1; }
996 | ':' tUNUMBER
997 { $$ = $2.value; }
1002 static table const meridian_table[] =
1004 { "AM", tMERIDIAN, MERam },
1005 { "A.M.", tMERIDIAN, MERam },
1006 { "PM", tMERIDIAN, MERpm },
1007 { "P.M.", tMERIDIAN, MERpm },
1008 { NULL, 0, 0 }
1011 static table const dst_table[] =
1013 { "DST", tDST, 0 }
1016 static table const month_and_day_table[] =
1018 { "JANUARY", tMONTH, 1 },
1019 { "FEBRUARY", tMONTH, 2 },
1020 { "MARCH", tMONTH, 3 },
1021 { "APRIL", tMONTH, 4 },
1022 { "MAY", tMONTH, 5 },
1023 { "JUNE", tMONTH, 6 },
1024 { "JULY", tMONTH, 7 },
1025 { "AUGUST", tMONTH, 8 },
1026 { "SEPTEMBER",tMONTH, 9 },
1027 { "SEPT", tMONTH, 9 },
1028 { "OCTOBER", tMONTH, 10 },
1029 { "NOVEMBER", tMONTH, 11 },
1030 { "DECEMBER", tMONTH, 12 },
1031 { "SUNDAY", tDAY, 0 },
1032 { "MONDAY", tDAY, 1 },
1033 { "TUESDAY", tDAY, 2 },
1034 { "TUES", tDAY, 2 },
1035 { "WEDNESDAY",tDAY, 3 },
1036 { "WEDNES", tDAY, 3 },
1037 { "THURSDAY", tDAY, 4 },
1038 { "THUR", tDAY, 4 },
1039 { "THURS", tDAY, 4 },
1040 { "FRIDAY", tDAY, 5 },
1041 { "SATURDAY", tDAY, 6 },
1042 { NULL, 0, 0 }
1045 static table const time_units_table[] =
1047 { "YEAR", tYEAR_UNIT, 1 },
1048 { "MONTH", tMONTH_UNIT, 1 },
1049 { "FORTNIGHT",tDAY_UNIT, 14 },
1050 { "WEEK", tDAY_UNIT, 7 },
1051 { "DAY", tDAY_UNIT, 1 },
1052 { "HOUR", tHOUR_UNIT, 1 },
1053 { "MINUTE", tMINUTE_UNIT, 1 },
1054 { "MIN", tMINUTE_UNIT, 1 },
1055 { "SECOND", tSEC_UNIT, 1 },
1056 { "SEC", tSEC_UNIT, 1 },
1057 { NULL, 0, 0 }
1060 /* Assorted relative-time words. */
1061 static table const relative_time_table[] =
1063 { "TOMORROW", tDAY_SHIFT, 1 },
1064 { "YESTERDAY",tDAY_SHIFT, -1 },
1065 { "TODAY", tDAY_SHIFT, 0 },
1066 { "NOW", tDAY_SHIFT, 0 },
1067 { "LAST", tORDINAL, -1 },
1068 { "THIS", tORDINAL, 0 },
1069 { "NEXT", tORDINAL, 1 },
1070 { "FIRST", tORDINAL, 1 },
1071 /*{ "SECOND", tORDINAL, 2 }, */
1072 { "THIRD", tORDINAL, 3 },
1073 { "FOURTH", tORDINAL, 4 },
1074 { "FIFTH", tORDINAL, 5 },
1075 { "SIXTH", tORDINAL, 6 },
1076 { "SEVENTH", tORDINAL, 7 },
1077 { "EIGHTH", tORDINAL, 8 },
1078 { "NINTH", tORDINAL, 9 },
1079 { "TENTH", tORDINAL, 10 },
1080 { "ELEVENTH", tORDINAL, 11 },
1081 { "TWELFTH", tORDINAL, 12 },
1082 { "AGO", tAGO, -1 },
1083 { "HENCE", tAGO, 1 },
1084 { NULL, 0, 0 }
1087 /* The universal time zone table. These labels can be used even for
1088 timestamps that would not otherwise be valid, e.g., GMT timestamps
1089 oin London during summer. */
1090 static table const universal_time_zone_table[] =
1092 { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
1093 { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
1094 { "UTC", tZONE, HOUR ( 0) },
1095 { NULL, 0, 0 }
1098 /* The time zone table. This table is necessarily incomplete, as time
1099 zone abbreviations are ambiguous; e.g., Australians interpret "EST"
1100 as Eastern time in Australia, not as US Eastern Standard Time.
1101 You cannot rely on parse_datetime to handle arbitrary time zone
1102 abbreviations; use numeric abbreviations like "-0500" instead. */
1103 static table const time_zone_table[] =
1105 { "WET", tZONE, HOUR ( 0) }, /* Western European */
1106 { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
1107 { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
1108 { "ART", tZONE, -HOUR ( 3) }, /* Argentina */
1109 { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
1110 { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
1111 { "NST", tZONE, -(HOUR ( 3) + 30 * 60) }, /* Newfoundland Standard */
1112 { "NDT", tDAYZONE,-(HOUR ( 3) + 30 * 60) }, /* Newfoundland Daylight */
1113 { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
1114 { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
1115 { "CLT", tZONE, -HOUR ( 4) }, /* Chile */
1116 { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
1117 { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
1118 { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
1119 { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
1120 { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
1121 { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
1122 { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
1123 { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
1124 { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
1125 { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
1126 { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
1127 { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
1128 { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
1129 { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
1130 { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
1131 { "WAT", tZONE, HOUR ( 1) }, /* West Africa */
1132 { "CET", tZONE, HOUR ( 1) }, /* Central European */
1133 { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
1134 { "MET", tZONE, HOUR ( 1) }, /* Middle European */
1135 { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
1136 { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
1137 { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
1138 { "EET", tZONE, HOUR ( 2) }, /* Eastern European */
1139 { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
1140 { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
1141 { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
1142 { "EAT", tZONE, HOUR ( 3) }, /* East Africa */
1143 { "MSK", tZONE, HOUR ( 3) }, /* Moscow */
1144 { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
1145 { "IST", tZONE, (HOUR ( 5) + 30 * 60) }, /* India Standard */
1146 { "SGT", tZONE, HOUR ( 8) }, /* Singapore */
1147 { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
1148 { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
1149 { "GST", tZONE, HOUR (10) }, /* Guam Standard */
1150 { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
1151 { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
1152 { NULL, 0, 0 }
1155 /* Military time zone table.
1157 RFC 822 got these backwards, but RFC 5322 makes the incorrect
1158 treatment optional, so do them the right way here.
1160 'J' is special, as it is local time.
1161 'T' is also special, as it is the separator in ISO
1162 8601 date and time of day representation. */
1163 static table const military_table[] =
1165 { "A", tZONE, HOUR ( 1) },
1166 { "B", tZONE, HOUR ( 2) },
1167 { "C", tZONE, HOUR ( 3) },
1168 { "D", tZONE, HOUR ( 4) },
1169 { "E", tZONE, HOUR ( 5) },
1170 { "F", tZONE, HOUR ( 6) },
1171 { "G", tZONE, HOUR ( 7) },
1172 { "H", tZONE, HOUR ( 8) },
1173 { "I", tZONE, HOUR ( 9) },
1174 { "J", 'J', 0 },
1175 { "K", tZONE, HOUR (10) },
1176 { "L", tZONE, HOUR (11) },
1177 { "M", tZONE, HOUR (12) },
1178 { "N", tZONE, -HOUR ( 1) },
1179 { "O", tZONE, -HOUR ( 2) },
1180 { "P", tZONE, -HOUR ( 3) },
1181 { "Q", tZONE, -HOUR ( 4) },
1182 { "R", tZONE, -HOUR ( 5) },
1183 { "S", tZONE, -HOUR ( 6) },
1184 { "T", 'T', 0 },
1185 { "U", tZONE, -HOUR ( 8) },
1186 { "V", tZONE, -HOUR ( 9) },
1187 { "W", tZONE, -HOUR (10) },
1188 { "X", tZONE, -HOUR (11) },
1189 { "Y", tZONE, -HOUR (12) },
1190 { "Z", tZONE, HOUR ( 0) },
1191 { NULL, 0, 0 }
1196 /* Convert a time zone expressed as HH:MM into an integer count of
1197 seconds. If MM is negative, then S is of the form HHMM and needs
1198 to be picked apart; otherwise, S is of the form HH. As specified in
1199 https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03, allow
1200 only valid TZ range, and consider first two digits as hours, if no
1201 minutes specified. Return true if successful. */
1203 static bool
1204 time_zone_hhmm (parser_control *pc, textint s, intmax_t mm)
1206 intmax_t n_minutes;
1207 bool overflow = false;
1209 /* If the length of S is 1 or 2 and no minutes are specified,
1210 interpret it as a number of hours. */
1211 if (s.digits <= 2 && mm < 0)
1212 s.value *= 100;
1214 if (mm < 0)
1215 n_minutes = (s.value / 100) * 60 + s.value % 100;
1216 else
1218 overflow |= ckd_mul (&n_minutes, s.value, 60);
1219 overflow |= (s.negative
1220 ? ckd_sub (&n_minutes, n_minutes, mm)
1221 : ckd_add (&n_minutes, n_minutes, mm));
1224 if (overflow || ! (-24 * 60 <= n_minutes && n_minutes <= 24 * 60))
1225 return false;
1226 pc->time_zone = n_minutes * 60;
1227 return true;
1230 static int
1231 to_hour (intmax_t hours, int meridian)
1233 switch (meridian)
1235 default: /* Pacify GCC. */
1236 case MER24:
1237 return 0 <= hours && hours < 24 ? hours : -1;
1238 case MERam:
1239 return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
1240 case MERpm:
1241 return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
1245 enum { TM_YEAR_BASE = 1900 };
1246 enum { TM_YEAR_BUFSIZE = INT_BUFSIZE_BOUND (int) + 1 };
1248 /* Convert TM_YEAR, a year minus 1900, to a string that is numerically
1249 correct even if subtracting 1900 would overflow. */
1251 static char const *
1252 tm_year_str (int tm_year, char buf[TM_YEAR_BUFSIZE])
1254 static_assert (TM_YEAR_BASE % 100 == 0);
1255 sprintf (buf, &"-%02d%02d"[-TM_YEAR_BASE <= tm_year],
1256 abs (tm_year / 100 + TM_YEAR_BASE / 100),
1257 abs (tm_year % 100));
1258 return buf;
1261 /* Convert a text year number to a year minus 1900, working correctly
1262 even if the input is in the range INT_MAX .. INT_MAX + 1900 - 1. */
1264 static bool
1265 to_tm_year (textint textyear, bool debug, int *tm_year)
1267 intmax_t year = textyear.value;
1269 /* XPG4 suggests that years 00-68 map to 2000-2068, and
1270 years 69-99 map to 1969-1999. */
1271 if (0 <= year && textyear.digits == 2)
1273 year += year < 69 ? 2000 : 1900;
1274 if (debug)
1275 dbg_printf (_("warning: adjusting year value %"PRIdMAX
1276 " to %"PRIdMAX"\n"),
1277 textyear.value, year);
1280 if (year < 0
1281 ? ckd_sub (tm_year, -TM_YEAR_BASE, year)
1282 : ckd_sub (tm_year, year, TM_YEAR_BASE))
1284 if (debug)
1285 dbg_printf (_("error: out-of-range year %"PRIdMAX"\n"), year);
1286 return false;
1289 return true;
1292 static table const * _GL_ATTRIBUTE_PURE
1293 lookup_zone (parser_control const *pc, char const *name)
1295 table const *tp;
1297 for (tp = universal_time_zone_table; tp->name; tp++)
1298 if (strcmp (name, tp->name) == 0)
1299 return tp;
1301 /* Try local zone abbreviations before those in time_zone_table, as
1302 the local ones are more likely to be right. */
1303 for (tp = pc->local_time_zone_table; tp->name; tp++)
1304 if (strcmp (name, tp->name) == 0)
1305 return tp;
1307 for (tp = time_zone_table; tp->name; tp++)
1308 if (strcmp (name, tp->name) == 0)
1309 return tp;
1311 return NULL;
1314 #if ! HAVE_TM_GMTOFF
1315 /* Yield the difference between *A and *B,
1316 measured in seconds, ignoring leap seconds.
1317 The body of this function is taken directly from the GNU C Library;
1318 see strftime.c. */
1319 static int
1320 tm_diff (const struct tm *a, const struct tm *b)
1322 /* Compute intervening leap days correctly even if year is negative.
1323 Take care to avoid int overflow in leap day calculations,
1324 but it's OK to assume that A and B are close to each other. */
1325 int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
1326 int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
1327 int a100 = a4 / 25 - (a4 % 25 < 0);
1328 int b100 = b4 / 25 - (b4 % 25 < 0);
1329 int a400 = SHR (a100, 2);
1330 int b400 = SHR (b100, 2);
1331 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
1332 int years = a->tm_year - b->tm_year;
1333 int days = (365 * years + intervening_leap_days
1334 + (a->tm_yday - b->tm_yday));
1335 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
1336 + (a->tm_min - b->tm_min))
1337 + (a->tm_sec - b->tm_sec));
1339 #endif /* ! HAVE_TM_GMTOFF */
1341 static table const *
1342 lookup_word (parser_control const *pc, char *word)
1344 char *p;
1345 char *q;
1346 idx_t wordlen;
1347 table const *tp;
1348 bool period_found;
1349 bool abbrev;
1351 /* Make it uppercase. */
1352 for (p = word; *p; p++)
1353 *p = c_toupper (to_uchar (*p));
1355 for (tp = meridian_table; tp->name; tp++)
1356 if (strcmp (word, tp->name) == 0)
1357 return tp;
1359 /* See if we have an abbreviation for a month. */
1360 wordlen = strlen (word);
1361 abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
1363 for (tp = month_and_day_table; tp->name; tp++)
1364 if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
1365 return tp;
1367 if ((tp = lookup_zone (pc, word)))
1368 return tp;
1370 if (strcmp (word, dst_table[0].name) == 0)
1371 return dst_table;
1373 for (tp = time_units_table; tp->name; tp++)
1374 if (strcmp (word, tp->name) == 0)
1375 return tp;
1377 /* Strip off any plural and try the units table again. */
1378 if (word[wordlen - 1] == 'S')
1380 word[wordlen - 1] = '\0';
1381 for (tp = time_units_table; tp->name; tp++)
1382 if (strcmp (word, tp->name) == 0)
1383 return tp;
1384 word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
1387 for (tp = relative_time_table; tp->name; tp++)
1388 if (strcmp (word, tp->name) == 0)
1389 return tp;
1391 /* Military time zones. */
1392 if (wordlen == 1)
1393 for (tp = military_table; tp->name; tp++)
1394 if (word[0] == tp->name[0])
1395 return tp;
1397 /* Drop out any periods and try the time zone table again. */
1398 for (period_found = false, p = q = word; (*p = *q); q++)
1399 if (*q == '.')
1400 period_found = true;
1401 else
1402 p++;
1403 if (period_found && (tp = lookup_zone (pc, word)))
1404 return tp;
1406 return NULL;
1409 static int
1410 yylex (union YYSTYPE *lvalp, parser_control *pc)
1412 unsigned char c;
1414 for (;;)
1416 while (c = *pc->input, c_isspace (c))
1417 pc->input++;
1419 if (c_isdigit (c) || c == '-' || c == '+')
1421 char const *p = pc->input;
1422 int sign;
1423 if (c == '-' || c == '+')
1425 sign = c == '-' ? -1 : 1;
1426 while (c = *(pc->input = ++p), c_isspace (c))
1427 continue;
1428 if (! c_isdigit (c))
1429 /* skip the '-' sign */
1430 continue;
1432 else
1433 sign = 0;
1435 time_t value = 0;
1438 if (ckd_mul (&value, value, 10))
1439 return '?';
1440 if (ckd_add (&value, value, sign < 0 ? '0' - c : c - '0'))
1441 return '?';
1442 c = *++p;
1444 while (c_isdigit (c));
1446 if ((c == '.' || c == ',') && c_isdigit (p[1]))
1448 time_t s = value;
1449 int digits;
1451 /* Accumulate fraction, to ns precision. */
1452 p++;
1453 int ns = *p++ - '0';
1454 for (digits = 2; digits <= LOG10_BILLION; digits++)
1456 ns *= 10;
1457 if (c_isdigit (*p))
1458 ns += *p++ - '0';
1461 /* Skip excess digits, truncating toward -Infinity. */
1462 if (sign < 0)
1463 for (; c_isdigit (*p); p++)
1464 if (*p != '0')
1466 ns++;
1467 break;
1469 while (c_isdigit (*p))
1470 p++;
1472 /* Adjust to the timespec convention, which is that
1473 tv_nsec is always a positive offset even if tv_sec is
1474 negative. */
1475 if (sign < 0 && ns)
1477 if (ckd_sub (&s, s, 1))
1478 return '?';
1479 ns = BILLION - ns;
1482 lvalp->timespec = (struct timespec) { .tv_sec = s,
1483 .tv_nsec = ns };
1484 pc->input = p;
1485 return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1487 else
1489 lvalp->textintval.negative = sign < 0;
1490 lvalp->textintval.value = value;
1491 lvalp->textintval.digits = p - pc->input;
1492 pc->input = p;
1493 return sign ? tSNUMBER : tUNUMBER;
1497 if (c_isalpha (c))
1499 char buff[20];
1500 char *p = buff;
1501 table const *tp;
1505 if (p < buff + sizeof buff - 1)
1506 *p++ = c;
1507 c = *++pc->input;
1509 while (c_isalpha (c) || c == '.');
1511 *p = '\0';
1512 tp = lookup_word (pc, buff);
1513 if (! tp)
1515 if (debugging (pc))
1516 dbg_printf (_("error: unknown word '%s'\n"), buff);
1517 return '?';
1519 lvalp->intval = tp->value;
1520 return tp->type;
1523 if (c != '(')
1524 return to_uchar (*pc->input++);
1526 idx_t count = 0;
1529 c = *pc->input++;
1530 if (c == '\0')
1531 return c;
1532 if (c == '(')
1533 count++;
1534 else if (c == ')')
1535 count--;
1537 while (count != 0);
1541 /* Do nothing if the parser reports an error. */
1542 static void
1543 yyerror (_GL_UNUSED parser_control const *pc,
1544 _GL_UNUSED char const *s)
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. */
1554 static bool
1555 mktime_ok (struct tm const *tm0, struct tm const *tm1)
1557 if (tm1->tm_wday < 0)
1558 return false;
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));
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 *
1571 debug_strfdatetime (struct tm const *tm, parser_control const *pc,
1572 char *buf, int n)
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)'.
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).
1583 2. Can strftime be used instead?
1584 depends if it is portable and can print invalid dates on all systems.
1586 3. Print timezone information ?
1588 4. Print DST information ?
1590 5. Print nanosecond information ?
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.
1597 int m = nstrftime (buf, n, "(Y-M-D) %Y-%m-%d %H:%M:%S", tm, 0, 0);
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)
1603 int tz = pc->time_zone;
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;
1609 char time_zone_buf[TIME_ZONE_BUFSIZE];
1610 snprintf (&buf[m], n - m, " TZ=%s", time_zone_str (tz, time_zone_buf));
1612 return buf;
1615 static char const *
1616 debug_strfdate (struct tm const *tm, char *buf, int n)
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;
1625 static char const *
1626 debug_strftime (struct tm const *tm, char *buf, int n)
1628 snprintf (buf, n, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
1629 return buf;
1632 /* If mktime_ok failed, display the failed time values,
1633 and provide possible hints. Example output:
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: nonexistent due to daylight-saving time;
1641 date: numeric values overflow;
1642 date: missing timezone;
1644 static void
1645 debug_mktime_not_ok (struct tm const *tm0, struct tm const *tm1,
1646 parser_control const *pc, bool time_zone_seen)
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);
1658 const bool dst_shift = eq_sec && eq_min && !eq_hour
1659 && eq_mday && eq_month && eq_year;
1661 if (!debugging (pc))
1662 return;
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)
1682 if (sizeof tmp - 1 < i)
1683 i = sizeof tmp - 1;
1684 while (0 < i && tmp[i - 1] == ' ')
1685 --i;
1686 tmp[i] = '\0';
1688 dbg_printf ("%s\n", tmp);
1690 dbg_printf (_(" possible reasons:\n"));
1691 if (dst_shift)
1692 dbg_printf (_(" nonexistent 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")));
1700 /* Parse a date/time string, storing the resulting time value into *RESULT.
1701 The string itself is pointed to by P. Return true if successful.
1702 P can be an incomplete or relative time specification; if so, use
1703 *NOW as the basis for the returned time. Default to timezone
1704 TZDEFAULT, which corresponds to tzalloc (TZSTRING). */
1705 static bool
1706 parse_datetime_body (struct timespec *result, char const *p,
1707 struct timespec const *now, unsigned int flags,
1708 timezone_t tzdefault, char const *tzstring)
1710 struct tm tm;
1711 struct tm tm0;
1712 char time_zone_buf[TIME_ZONE_BUFSIZE];
1713 char dbg_tm[DBGBUFSIZE];
1714 bool ok = false;
1715 char const *input_sentinel = p + strlen (p);
1716 char *tz1alloc = NULL;
1718 /* A reasonable upper bound for the size of ordinary TZ strings.
1719 Use heap allocation if TZ's length exceeds this. */
1720 enum { TZBUFSIZE = 100 };
1721 char tz1buf[TZBUFSIZE];
1723 struct timespec gettime_buffer;
1724 if (! now)
1726 gettime (&gettime_buffer);
1727 now = &gettime_buffer;
1730 time_t Start = now->tv_sec;
1731 int Start_ns = now->tv_nsec;
1733 unsigned char c;
1734 while (c = *p, c_isspace (c))
1735 p++;
1737 timezone_t tz = tzdefault;
1739 /* Store a local copy prior to first "goto". Without this, a prior use
1740 below of RELATIVE_TIME_0 on the RHS might translate to an assignment-
1741 to-temporary, which would trigger a -Wjump-misses-init warning. */
1742 const relative_time rel_time_0 = RELATIVE_TIME_0;
1744 if (strncmp (p, "TZ=\"", 4) == 0)
1746 char const *tzbase = p + 4;
1747 idx_t tzsize = 1;
1748 char const *s;
1750 for (s = tzbase; *s; s++, tzsize++)
1751 if (*s == '\\')
1753 s++;
1754 if (! (*s == '\\' || *s == '"'))
1755 break;
1757 else if (*s == '"')
1759 timezone_t tz1;
1760 char *tz1string = tz1buf;
1761 char *z;
1762 if (TZBUFSIZE < tzsize)
1764 tz1alloc = malloc (tzsize);
1765 if (!tz1alloc)
1766 goto fail;
1767 tz1string = tz1alloc;
1769 z = tz1string;
1770 for (s = tzbase; *s != '"'; s++)
1771 *z++ = *(s += *s == '\\');
1772 *z = '\0';
1773 tz1 = tzalloc (tz1string);
1774 if (!tz1)
1775 goto fail;
1776 tz = tz1;
1777 tzstring = tz1string;
1779 p = s + 1;
1780 while (c = *p, c_isspace (c))
1781 p++;
1783 break;
1787 struct tm tmp;
1788 if (! localtime_rz (tz, &now->tv_sec, &tmp))
1789 goto fail;
1791 /* As documented, be careful to treat the empty string just like
1792 a date string of "0". Without this, an empty string would be
1793 declared invalid when parsed during a DST transition. */
1794 if (*p == '\0')
1795 p = "0";
1797 parser_control pc;
1798 pc.input = p;
1799 #ifdef GNULIB_PARSE_DATETIME2
1800 pc.parse_datetime_debug = (flags & PARSE_DATETIME_DEBUG) != 0;
1801 #endif
1802 if (ckd_add (&pc.year.value, tmp.tm_year, TM_YEAR_BASE))
1804 if (debugging (&pc))
1805 dbg_printf (_("error: initial year out of range\n"));
1806 goto fail;
1808 pc.year.digits = 0;
1809 pc.month = tmp.tm_mon + 1;
1810 pc.day = tmp.tm_mday;
1811 pc.hour = tmp.tm_hour;
1812 pc.minutes = tmp.tm_min;
1813 pc.seconds = (struct timespec) { .tv_sec = tmp.tm_sec, .tv_nsec = Start_ns };
1814 tm.tm_isdst = tmp.tm_isdst;
1816 pc.meridian = MER24;
1817 pc.rel = rel_time_0;
1818 pc.timespec_seen = false;
1819 pc.rels_seen = false;
1820 pc.dates_seen = 0;
1821 pc.days_seen = 0;
1822 pc.times_seen = 0;
1823 pc.J_zones_seen = 0;
1824 pc.local_zones_seen = 0;
1825 pc.dsts_seen = 0;
1826 pc.zones_seen = 0;
1827 pc.year_seen = false;
1828 pc.debug_dates_seen = false;
1829 pc.debug_days_seen = false;
1830 pc.debug_times_seen = false;
1831 pc.debug_local_zones_seen = false;
1832 pc.debug_zones_seen = false;
1833 pc.debug_year_seen = false;
1834 pc.debug_ordinal_day_seen = false;
1836 #if HAVE_STRUCT_TM_TM_ZONE
1837 pc.local_time_zone_table[0].name = tmp.tm_zone;
1838 pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1839 pc.local_time_zone_table[0].value = tmp.tm_isdst;
1840 pc.local_time_zone_table[1].name = NULL;
1842 /* Probe the names used in the next three calendar quarters, looking
1843 for a tm_isdst different from the one we already have. */
1845 int quarter;
1846 for (quarter = 1; quarter <= 3; quarter++)
1848 time_t probe;
1849 if (ckd_add (&probe, Start, quarter * (90 * 24 * 60 * 60)))
1850 break;
1851 struct tm probe_tm;
1852 if (localtime_rz (tz, &probe, &probe_tm) && probe_tm.tm_zone
1853 && probe_tm.tm_isdst != pc.local_time_zone_table[0].value)
1856 pc.local_time_zone_table[1].name = probe_tm.tm_zone;
1857 pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1858 pc.local_time_zone_table[1].value = probe_tm.tm_isdst;
1859 pc.local_time_zone_table[2].name = NULL;
1861 break;
1865 #else
1866 #if HAVE_TZNAME
1868 # if !HAVE_DECL_TZNAME
1869 extern char *tzname[];
1870 # endif
1871 int i;
1872 for (i = 0; i < 2; i++)
1874 pc.local_time_zone_table[i].name = tzname[i];
1875 pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1876 pc.local_time_zone_table[i].value = i;
1878 pc.local_time_zone_table[i].name = NULL;
1880 #else
1881 pc.local_time_zone_table[0].name = NULL;
1882 #endif
1883 #endif
1885 if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
1886 && ! strcmp (pc.local_time_zone_table[0].name,
1887 pc.local_time_zone_table[1].name))
1889 /* This locale uses the same abbreviation for standard and
1890 daylight times. So if we see that abbreviation, we don't
1891 know whether it's daylight time. */
1892 pc.local_time_zone_table[0].value = -1;
1893 pc.local_time_zone_table[1].name = NULL;
1896 if (yyparse (&pc) != 0)
1898 if (debugging (&pc))
1899 dbg_printf ((input_sentinel <= pc.input
1900 ? _("error: parsing failed\n")
1901 : _("error: parsing failed, stopped at '%s'\n")),
1902 pc.input);
1903 goto fail;
1907 /* Determine effective timezone source. */
1909 if (debugging (&pc))
1911 dbg_printf (_("input timezone: "));
1913 if (pc.timespec_seen)
1914 fprintf (stderr, _("'@timespec' - always UTC"));
1915 else if (pc.zones_seen)
1916 fprintf (stderr, _("parsed date/time string"));
1917 else if (tzstring)
1919 if (tz != tzdefault)
1920 fprintf (stderr, _("TZ=\"%s\" in date string"), tzstring);
1921 else if (STREQ (tzstring, "UTC0"))
1923 /* Special case: 'date -u' sets TZ="UTC0". */
1924 fprintf (stderr, _("TZ=\"UTC0\" environment value or -u"));
1926 else
1927 fprintf (stderr, _("TZ=\"%s\" environment value"), tzstring);
1929 else
1930 fprintf (stderr, _("system default"));
1932 /* Account for DST changes if tLOCAL_ZONE was seen.
1933 local timezone only changes DST and is relative to the
1934 default timezone.*/
1935 if (pc.local_zones_seen && !pc.zones_seen && 0 < pc.local_isdst)
1936 fprintf (stderr, ", dst");
1938 if (pc.zones_seen)
1939 fprintf (stderr, " (%s)", time_zone_str (pc.time_zone, time_zone_buf));
1941 fputc ('\n', stderr);
1944 if (pc.timespec_seen)
1945 *result = pc.seconds;
1946 else
1948 if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1949 | (pc.J_zones_seen + pc.local_zones_seen + pc.zones_seen)))
1951 if (debugging (&pc))
1953 if (pc.times_seen > 1)
1954 dbg_printf ("error: seen multiple time parts\n");
1955 if (pc.dates_seen > 1)
1956 dbg_printf ("error: seen multiple date parts\n");
1957 if (pc.days_seen > 1)
1958 dbg_printf ("error: seen multiple days parts\n");
1959 if (pc.dsts_seen > 1)
1960 dbg_printf ("error: seen multiple daylight-saving parts\n");
1961 if ((pc.J_zones_seen + pc.local_zones_seen + pc.zones_seen) > 1)
1962 dbg_printf ("error: seen multiple time-zone parts\n");
1964 goto fail;
1967 if (! to_tm_year (pc.year, debugging (&pc), &tm.tm_year)
1968 || ckd_add (&tm.tm_mon, pc.month, -1)
1969 || ckd_add (&tm.tm_mday, pc.day, 0))
1971 if (debugging (&pc))
1972 dbg_printf (_("error: year, month, or day overflow\n"));
1973 goto fail;
1975 if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
1977 tm.tm_hour = to_hour (pc.hour, pc.meridian);
1978 if (tm.tm_hour < 0)
1980 char const *mrd = (pc.meridian == MERam ? "am"
1981 : pc.meridian == MERpm ?"pm" : "");
1982 if (debugging (&pc))
1983 dbg_printf (_("error: invalid hour %"PRIdMAX"%s\n"),
1984 pc.hour, mrd);
1985 goto fail;
1987 tm.tm_min = pc.minutes;
1988 tm.tm_sec = pc.seconds.tv_sec;
1989 if (debugging (&pc))
1990 dbg_printf ((pc.times_seen
1991 ? _("using specified time as starting value: '%s'\n")
1992 : _("using current time as starting value: '%s'\n")),
1993 debug_strftime (&tm, dbg_tm, sizeof dbg_tm));
1995 else
1997 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1998 pc.seconds.tv_nsec = 0;
1999 if (debugging (&pc))
2000 dbg_printf ("warning: using midnight as starting time: 00:00:00\n");
2003 /* Let mktime deduce tm_isdst if we have an absolute timestamp. */
2004 if (pc.dates_seen | pc.days_seen | pc.times_seen)
2005 tm.tm_isdst = -1;
2007 /* But if the input explicitly specifies local time with or without
2008 DST, give mktime that information. */
2009 if (pc.local_zones_seen)
2010 tm.tm_isdst = pc.local_isdst;
2012 tm0.tm_sec = tm.tm_sec;
2013 tm0.tm_min = tm.tm_min;
2014 tm0.tm_hour = tm.tm_hour;
2015 tm0.tm_mday = tm.tm_mday;
2016 tm0.tm_mon = tm.tm_mon;
2017 tm0.tm_year = tm.tm_year;
2018 tm0.tm_isdst = tm.tm_isdst;
2019 tm.tm_wday = -1;
2021 Start = mktime_z (tz, &tm);
2023 if (! mktime_ok (&tm0, &tm))
2025 bool repaired = false;
2026 bool time_zone_seen = pc.zones_seen != 0;
2027 if (time_zone_seen)
2029 /* Guard against falsely reporting errors near the time_t
2030 boundaries when parsing times in other time zones. For
2031 example, suppose the input string "1969-12-31 23:00:00 -0100",
2032 the current time zone is 8 hours ahead of UTC, and the min
2033 time_t value is 1970-01-01 00:00:00 UTC. Then the min
2034 localtime value is 1970-01-01 08:00:00, and mktime will
2035 therefore fail on 1969-12-31 23:00:00. To work around the
2036 problem, set the time zone to 1 hour behind UTC temporarily
2037 by setting TZ="XXX1:00" and try mktime again. */
2039 char tz2buf[sizeof "XXX" - 1 + TIME_ZONE_BUFSIZE];
2040 tz2buf[0] = tz2buf[1] = tz2buf[2] = 'X';
2041 time_zone_str (pc.time_zone, &tz2buf[3]);
2042 timezone_t tz2 = tzalloc (tz2buf);
2043 if (!tz2)
2045 if (debugging (&pc))
2046 dbg_printf (_("error: tzalloc (\"%s\") failed\n"), tz2buf);
2047 goto fail;
2049 tm.tm_sec = tm0.tm_sec;
2050 tm.tm_min = tm0.tm_min;
2051 tm.tm_hour = tm0.tm_hour;
2052 tm.tm_mday = tm0.tm_mday;
2053 tm.tm_mon = tm0.tm_mon;
2054 tm.tm_year = tm0.tm_year;
2055 tm.tm_isdst = tm0.tm_isdst;
2056 tm.tm_wday = -1;
2057 Start = mktime_z (tz2, &tm);
2058 repaired = mktime_ok (&tm0, &tm);
2059 tzfree (tz2);
2062 if (! repaired)
2064 debug_mktime_not_ok (&tm0, &tm, &pc, time_zone_seen);
2065 goto fail;
2069 char dbg_ord[DBGBUFSIZE];
2071 if (pc.days_seen && ! pc.dates_seen)
2073 intmax_t dayincr;
2074 tm.tm_yday = -1;
2075 intmax_t day_ordinal = (pc.day_ordinal
2076 - (0 < pc.day_ordinal
2077 && tm.tm_wday != pc.day_number));
2078 if (! (ckd_mul (&dayincr, day_ordinal, 7)
2079 || ckd_add (&dayincr, (pc.day_number - tm.tm_wday + 7) % 7,
2080 dayincr)
2081 || ckd_add (&tm.tm_mday, dayincr, tm.tm_mday)))
2083 tm.tm_isdst = -1;
2084 Start = mktime_z (tz, &tm);
2087 if (tm.tm_yday < 0)
2089 if (debugging (&pc))
2090 dbg_printf (_("error: day '%s' "
2091 "(day ordinal=%"PRIdMAX" number=%d) "
2092 "resulted in an invalid date: '%s'\n"),
2093 str_days (&pc, dbg_ord, sizeof dbg_ord),
2094 pc.day_ordinal, pc.day_number,
2095 debug_strfdatetime (&tm, &pc, dbg_tm,
2096 sizeof dbg_tm));
2097 goto fail;
2100 if (debugging (&pc))
2101 dbg_printf (_("new start date: '%s' is '%s'\n"),
2102 str_days (&pc, dbg_ord, sizeof dbg_ord),
2103 debug_strfdatetime (&tm, &pc, dbg_tm, sizeof dbg_tm));
2107 if (debugging (&pc))
2109 if (!pc.dates_seen && !pc.days_seen)
2110 dbg_printf (_("using current date as starting value: '%s'\n"),
2111 debug_strfdate (&tm, dbg_tm, sizeof dbg_tm));
2113 if (pc.days_seen && pc.dates_seen)
2114 dbg_printf (_("warning: day (%s) ignored when explicit dates "
2115 "are given\n"),
2116 str_days (&pc, dbg_ord, sizeof dbg_ord));
2118 dbg_printf (_("starting date/time: '%s'\n"),
2119 debug_strfdatetime (&tm, &pc, dbg_tm, sizeof dbg_tm));
2122 /* Add relative date. */
2123 if (pc.rel.year | pc.rel.month | pc.rel.day)
2125 if (debugging (&pc))
2127 if ((pc.rel.year != 0 || pc.rel.month != 0) && tm.tm_mday != 15)
2128 dbg_printf (_("warning: when adding relative months/years, "
2129 "it is recommended to specify the 15th of the "
2130 "months\n"));
2132 if (pc.rel.day != 0 && tm.tm_hour != 12)
2133 dbg_printf (_("warning: when adding relative days, "
2134 "it is recommended to specify noon\n"));
2137 int year, month, day;
2138 if (ckd_add (&year, tm.tm_year, pc.rel.year)
2139 || ckd_add (&month, tm.tm_mon, pc.rel.month)
2140 || ckd_add (&day, tm.tm_mday, pc.rel.day))
2142 if (debugging (&pc))
2143 dbg_printf (_("error: %s:%d\n"), __FILE__, __LINE__);
2144 goto fail;
2146 tm.tm_year = year;
2147 tm.tm_mon = month;
2148 tm.tm_mday = day;
2149 tm.tm_hour = tm0.tm_hour;
2150 tm.tm_min = tm0.tm_min;
2151 tm.tm_sec = tm0.tm_sec;
2152 tm.tm_isdst = tm0.tm_isdst;
2153 tm.tm_wday = -1;
2154 Start = mktime_z (tz, &tm);
2155 if (tm.tm_wday < 0)
2157 if (debugging (&pc))
2158 dbg_printf (_("error: adding relative date resulted "
2159 "in an invalid date: '%s'\n"),
2160 debug_strfdatetime (&tm, &pc, dbg_tm,
2161 sizeof dbg_tm));
2162 goto fail;
2165 if (debugging (&pc))
2167 dbg_printf (_("after date adjustment "
2168 "(%+"PRIdMAX" years, %+"PRIdMAX" months, "
2169 "%+"PRIdMAX" days),\n"),
2170 pc.rel.year, pc.rel.month, pc.rel.day);
2171 dbg_printf (_(" new date/time = '%s'\n"),
2172 debug_strfdatetime (&tm, &pc, dbg_tm,
2173 sizeof dbg_tm));
2175 /* Warn about crossing DST due to time adjustment.
2176 Example: https://bugs.gnu.org/8357
2177 env TZ=Europe/Helsinki \
2178 date --debug \
2179 -d 'Mon Mar 28 00:36:07 2011 EEST 1 day ago'
2181 This case is different than DST changes due to time adjustment,
2182 i.e., "1 day ago" vs "24 hours ago" are calculated in different
2183 places.
2185 'tm0.tm_isdst' contains the DST of the input date,
2186 'tm.tm_isdst' is the normalized result after calling
2187 mktime (&tm).
2189 if (tm0.tm_isdst != -1 && tm.tm_isdst != tm0.tm_isdst)
2190 dbg_printf (_("warning: daylight saving time changed after "
2191 "date adjustment\n"));
2193 /* Warn if the user did not ask to adjust days but mday changed,
2195 user did not ask to adjust months/days but the month changed.
2197 Example for first case:
2198 2016-05-31 + 1 month => 2016-06-31 => 2016-07-01.
2199 User asked to adjust month, but the day changed from 31 to 01.
2201 Example for second case:
2202 2016-02-29 + 1 year => 2017-02-29 => 2017-03-01.
2203 User asked to adjust year, but the month changed from 02 to 03.
2205 if (pc.rel.day == 0
2206 && (tm.tm_mday != day
2207 || (pc.rel.month == 0 && tm.tm_mon != month)))
2209 dbg_printf (_("warning: month/year adjustment resulted in "
2210 "shifted dates:\n"));
2211 char tm_year_buf[TM_YEAR_BUFSIZE];
2212 dbg_printf (_(" adjusted Y M D: %s %02d %02d\n"),
2213 tm_year_str (year, tm_year_buf), month + 1, day);
2214 dbg_printf (_(" normalized Y M D: %s %02d %02d\n"),
2215 tm_year_str (tm.tm_year, tm_year_buf),
2216 tm.tm_mon + 1, tm.tm_mday);
2222 /* The only "output" of this if-block is an updated Start value,
2223 so this block must follow others that clobber Start. */
2224 if (pc.zones_seen)
2226 bool overflow = false;
2227 #ifdef HAVE_TM_GMTOFF
2228 long int utcoff = tm.tm_gmtoff;
2229 #else
2230 time_t t = Start;
2231 struct tm gmt;
2232 int utcoff = (gmtime_r (&t, &gmt)
2233 ? tm_diff (&tm, &gmt)
2234 : (overflow = true, 0));
2235 #endif
2236 intmax_t delta;
2237 overflow |= ckd_sub (&delta, pc.time_zone, utcoff);
2238 time_t t1;
2239 overflow |= ckd_sub (&t1, Start, delta);
2240 if (overflow)
2242 if (debugging (&pc))
2243 dbg_printf (_("error: timezone %d caused time_t overflow\n"),
2244 pc.time_zone);
2245 goto fail;
2247 Start = t1;
2250 if (debugging (&pc))
2252 intmax_t Starti = Start;
2253 dbg_printf (_("'%s' = %"PRIdMAX" epoch-seconds\n"),
2254 debug_strfdatetime (&tm, &pc, dbg_tm, sizeof dbg_tm),
2255 Starti);
2259 /* Add relative hours, minutes, and seconds. On hosts that support
2260 leap seconds, ignore the possibility of leap seconds; e.g.,
2261 "+ 10 minutes" adds 600 seconds, even if one of them is a
2262 leap second. Typically this is not what the user wants, but it's
2263 too hard to do it the other way, because the time zone indicator
2264 must be applied before relative times, and if mktime is applied
2265 again the time zone will be lost. */
2267 intmax_t orig_ns = pc.seconds.tv_nsec;
2268 intmax_t sum_ns = orig_ns + pc.rel.ns;
2269 int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
2270 int d4 = (sum_ns - normalized_ns) / BILLION;
2271 intmax_t d1, t1, d2, t2, t3;
2272 time_t t4;
2273 if (ckd_mul (&d1, pc.rel.hour, 60 * 60)
2274 || ckd_add (&t1, Start, d1)
2275 || ckd_mul (&d2, pc.rel.minutes, 60)
2276 || ckd_add (&t2, t1, d2)
2277 || ckd_add (&t3, t2, pc.rel.seconds)
2278 || ckd_add (&t4, t3, d4))
2280 if (debugging (&pc))
2281 dbg_printf (_("error: adding relative time caused an "
2282 "overflow\n"));
2283 goto fail;
2286 result->tv_sec = t4;
2287 result->tv_nsec = normalized_ns;
2289 if (debugging (&pc)
2290 && (pc.rel.hour | pc.rel.minutes | pc.rel.seconds | pc.rel.ns))
2292 dbg_printf (_("after time adjustment (%+"PRIdMAX" hours, "
2293 "%+"PRIdMAX" minutes, "
2294 "%+"PRIdMAX" seconds, %+d ns),\n"),
2295 pc.rel.hour, pc.rel.minutes, pc.rel.seconds,
2296 pc.rel.ns);
2297 intmax_t t4i = t4;
2298 dbg_printf (_(" new time = %"PRIdMAX" epoch-seconds\n"), t4i);
2300 /* Warn about crossing DST due to time adjustment.
2301 Example: https://bugs.gnu.org/8357
2302 env TZ=Europe/Helsinki \
2303 date --debug \
2304 -d 'Mon Mar 28 00:36:07 2011 EEST 24 hours ago'
2306 This case is different than DST changes due to days adjustment,
2307 i.e., "1 day ago" vs "24 hours ago" are calculated in different
2308 places.
2310 'tm.tm_isdst' contains the date after date adjustment. */
2311 struct tm lmt;
2312 if (tm.tm_isdst != -1 && localtime_rz (tz, &result->tv_sec, &lmt)
2313 && tm.tm_isdst != lmt.tm_isdst)
2314 dbg_printf (_("warning: daylight saving time changed after "
2315 "time adjustment\n"));
2320 if (debugging (&pc))
2322 /* Special case: using 'date -u' simply set TZ=UTC0 */
2323 if (! tzstring)
2324 dbg_printf (_("timezone: system default\n"));
2325 else if (STREQ (tzstring, "UTC0"))
2326 dbg_printf (_("timezone: Universal Time\n"));
2327 else
2328 dbg_printf (_("timezone: TZ=\"%s\" environment value\n"), tzstring);
2330 intmax_t sec = result->tv_sec;
2331 int nsec = result->tv_nsec;
2332 dbg_printf (_("final: %"PRIdMAX".%09d (epoch-seconds)\n"),
2333 sec, nsec);
2335 struct tm gmt, lmt;
2336 bool got_utc = !!gmtime_r (&result->tv_sec, &gmt);
2337 if (got_utc)
2338 dbg_printf (_("final: %s (UTC)\n"),
2339 debug_strfdatetime (&gmt, NULL,
2340 dbg_tm, sizeof dbg_tm));
2341 if (localtime_rz (tz, &result->tv_sec, &lmt))
2343 #ifdef HAVE_TM_GMTOFF
2344 bool got_utcoff = true;
2345 long int utcoff = lmt.tm_gmtoff;
2346 #else
2347 bool got_utcoff = got_utc;
2348 int utcoff;
2349 if (got_utcoff)
2350 utcoff = tm_diff (&lmt, &gmt);
2351 #endif
2352 if (got_utcoff)
2353 dbg_printf (_("final: %s (UTC%s)\n"),
2354 debug_strfdatetime (&lmt, NULL, dbg_tm, sizeof dbg_tm),
2355 time_zone_str (utcoff, time_zone_buf));
2356 else
2357 dbg_printf (_("final: %s (unknown time zone offset)\n"),
2358 debug_strfdatetime (&lmt, NULL, dbg_tm, sizeof dbg_tm));
2362 ok = true;
2364 fail:
2365 if (tz != tzdefault)
2366 tzfree (tz);
2367 free (tz1alloc);
2368 return ok;
2371 #ifdef GNULIB_PARSE_DATETIME2
2372 /* Parse a date/time string, storing the resulting time value into *RESULT.
2373 The string itself is pointed to by P. Return true if successful.
2374 P can be an incomplete or relative time specification; if so, use
2375 *NOW as the basis for the returned time. Default to timezone
2376 TZDEFAULT, which corresponds to tzalloc (TZSTRING). */
2377 bool
2378 parse_datetime2 (struct timespec *result, char const *p,
2379 struct timespec const *now, unsigned int flags,
2380 timezone_t tzdefault, char const *tzstring)
2382 return parse_datetime_body (result, p, now, flags, tzdefault, tzstring);
2384 #endif
2387 /* The plain interface: run with debug=false and the default timezone. */
2388 bool
2389 parse_datetime (struct timespec *result, char const *p,
2390 struct timespec const *now)
2392 char const *tzstring = getenv ("TZ");
2393 timezone_t tz = tzalloc (tzstring);
2394 if (!tz)
2395 return false;
2396 bool ok = parse_datetime_body (result, p, now, 0, tz, tzstring);
2397 tzfree (tz);
2398 return ok;
2401 #if TEST
2404 main (int ac, char **av)
2406 char buff[BUFSIZ];
2408 printf ("Enter date, or blank line to exit.\n\t> ");
2409 fflush (stdout);
2411 buff[BUFSIZ - 1] = '\0';
2412 while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
2414 struct timespec d;
2415 struct tm const *tm;
2416 if (! parse_datetime (&d, buff, NULL))
2417 printf ("Bad format - couldn't convert.\n");
2418 else if (! (tm = localtime (&d.tv_sec)))
2420 intmax_t sec = d.tv_sec;
2421 printf ("localtime (%"PRIdMAX") failed\n", sec);
2423 else
2425 int ns = d.tv_nsec;
2426 char tm_year_buf[TM_YEAR_BUFSIZE];
2427 printf ("%s-%02d-%02d %02d:%02d:%02d.%09d\n",
2428 tm_year_str (tm->tm_year, tm_year_buf),
2429 tm->tm_mon + 1, tm->tm_mday,
2430 tm->tm_hour, tm->tm_min, tm->tm_sec, ns);
2432 printf ("\t> ");
2433 fflush (stdout);
2435 return 0;
2437 #endif /* TEST */