diff options
Diffstat (limited to 'lib/parse-datetime.y')
-rw-r--r-- | lib/parse-datetime.y | 305 |
1 files changed, 165 insertions, 140 deletions
diff --git a/lib/parse-datetime.y b/lib/parse-datetime.y index b8a832fcd8..447a943db1 100644 --- a/lib/parse-datetime.y +++ b/lib/parse-datetime.y @@ -1,11 +1,11 @@ %{ /* Parse a string into an internal timestamp. - Copyright (C) 1999-2000, 2002-2021 Free Software Foundation, Inc. + Copyright (C) 1999-2000, 2002-2024 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or + the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -38,7 +38,6 @@ #include "idx.h" #include "intprops.h" #include "timespec.h" -#include "verify.h" #include "strftime.h" /* There's no need to extend the stack, so there's no need to involve @@ -52,19 +51,10 @@ #define YYMAXDEPTH 20 #define YYINITDEPTH YYMAXDEPTH -/* Since the code of parse-datetime.y is not included in the Emacs executable - itself, there is no need to #define static in this file. Even if - the code were included in the Emacs executable, it probably - wouldn't do any harm to #undef it here; this will only cause - problems if we try to write to a static variable, which I don't - think this code needs to do. */ -#ifdef emacs -# undef static -#endif - #include <inttypes.h> #include <c-ctype.h> #include <stdarg.h> +#include <stdckdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -104,9 +94,9 @@ /* Verify that time_t is an integer as POSIX requires, and that every time_t value fits in intmax_t. Please file a bug report if these assumptions are false on your platform. */ -verify (TYPE_IS_INTEGER (time_t)); -verify (!TYPE_SIGNED (time_t) || INTMAX_MIN <= TYPE_MINIMUM (time_t)); -verify (TYPE_MAXIMUM (time_t) <= INTMAX_MAX); +static_assert (TYPE_IS_INTEGER (time_t)); +static_assert (!TYPE_SIGNED (time_t) || INTMAX_MIN <= TYPE_MINIMUM (time_t)); +static_assert (TYPE_MAXIMUM (time_t) <= INTMAX_MAX); /* True if N is out of range for time_t. */ static bool @@ -215,14 +205,17 @@ typedef struct bool rels_seen; idx_t dates_seen; idx_t days_seen; + idx_t J_zones_seen; idx_t local_zones_seen; idx_t dsts_seen; idx_t times_seen; idx_t zones_seen; bool year_seen; +#ifdef GNULIB_PARSE_DATETIME2 /* Print debugging output to stderr. */ bool parse_datetime_debug; +#endif /* Which of the 'seen' parts have been printed when debugging. */ bool debug_dates_seen; @@ -239,9 +232,19 @@ typedef struct table local_time_zone_table[3]; } parser_control; +static bool +debugging (parser_control const *pc) +{ +#ifdef GNULIB_PARSE_DATETIME2 + return pc->parse_datetime_debug; +#else + return false; +#endif +} + union YYSTYPE; static int yylex (union YYSTYPE *, parser_control *); -static int yyerror (parser_control const *, char const *); +static void yyerror (parser_control const *, char const *); static bool time_zone_hhmm (parser_control *, textint, intmax_t); /* Extract into *PC any date and time info from a string of digits @@ -279,8 +282,7 @@ digits_to_date_time (parser_control *pc, textint text_int) pc->hour = text_int.value / 100; pc->minutes = text_int.value % 100; } - pc->seconds.tv_sec = 0; - pc->seconds.tv_nsec = 0; + pc->seconds = (struct timespec) {0}; pc->meridian = MER24; } } @@ -292,20 +294,20 @@ static bool apply_relative_time (parser_control *pc, relative_time rel, int factor) { if (factor < 0 - ? (INT_SUBTRACT_WRAPV (pc->rel.ns, rel.ns, &pc->rel.ns) - | INT_SUBTRACT_WRAPV (pc->rel.seconds, rel.seconds, &pc->rel.seconds) - | INT_SUBTRACT_WRAPV (pc->rel.minutes, rel.minutes, &pc->rel.minutes) - | INT_SUBTRACT_WRAPV (pc->rel.hour, rel.hour, &pc->rel.hour) - | INT_SUBTRACT_WRAPV (pc->rel.day, rel.day, &pc->rel.day) - | INT_SUBTRACT_WRAPV (pc->rel.month, rel.month, &pc->rel.month) - | INT_SUBTRACT_WRAPV (pc->rel.year, rel.year, &pc->rel.year)) - : (INT_ADD_WRAPV (pc->rel.ns, rel.ns, &pc->rel.ns) - | INT_ADD_WRAPV (pc->rel.seconds, rel.seconds, &pc->rel.seconds) - | INT_ADD_WRAPV (pc->rel.minutes, rel.minutes, &pc->rel.minutes) - | INT_ADD_WRAPV (pc->rel.hour, rel.hour, &pc->rel.hour) - | INT_ADD_WRAPV (pc->rel.day, rel.day, &pc->rel.day) - | INT_ADD_WRAPV (pc->rel.month, rel.month, &pc->rel.month) - | INT_ADD_WRAPV (pc->rel.year, rel.year, &pc->rel.year))) + ? (ckd_sub (&pc->rel.ns, pc->rel.ns, rel.ns) + | ckd_sub (&pc->rel.seconds, pc->rel.seconds, rel.seconds) + | ckd_sub (&pc->rel.minutes, pc->rel.minutes, rel.minutes) + | ckd_sub (&pc->rel.hour, pc->rel.hour, rel.hour) + | ckd_sub (&pc->rel.day, pc->rel.day, rel.day) + | ckd_sub (&pc->rel.month, pc->rel.month, rel.month) + | ckd_sub (&pc->rel.year, pc->rel.year, rel.year)) + : (ckd_add (&pc->rel.ns, pc->rel.ns, rel.ns) + | ckd_add (&pc->rel.seconds, pc->rel.seconds, rel.seconds) + | ckd_add (&pc->rel.minutes, pc->rel.minutes, rel.minutes) + | ckd_add (&pc->rel.hour, pc->rel.hour, rel.hour) + | ckd_add (&pc->rel.day, pc->rel.day, rel.day) + | ckd_add (&pc->rel.month, pc->rel.month, rel.month) + | ckd_add (&pc->rel.year, pc->rel.year, rel.year))) return false; pc->rels_seen = true; return true; @@ -318,8 +320,7 @@ set_hhmmss (parser_control *pc, intmax_t hour, intmax_t minutes, { pc->hour = hour; pc->minutes = minutes; - pc->seconds.tv_sec = sec; - pc->seconds.tv_nsec = nsec; + pc->seconds = (struct timespec) { .tv_sec = sec, .tv_nsec = nsec }; } /* Return a textual representation of the day ordinal/number values @@ -421,7 +422,7 @@ debug_print_current_time (char const *item, parser_control *pc) { bool space = false; - if (!pc->parse_datetime_debug) + if (!debugging (pc)) return; /* no newline, more items printed below */ @@ -521,7 +522,7 @@ debug_print_relative_time (char const *item, parser_control const *pc) { bool space = false; - if (!pc->parse_datetime_debug) + if (!debugging (pc)) return; /* no newline, more items printed below */ @@ -622,6 +623,11 @@ item: pc->local_zones_seen++; debug_print_current_time (_("local_zone"), pc); } + | 'J' + { + pc->J_zones_seen++; + debug_print_current_time ("J", pc); + } | zone { pc->zones_seen++; @@ -755,7 +761,7 @@ zone: } | tZONE tSNUMBER o_colon_minutes { if (! time_zone_hhmm (pc, $2, $3)) YYABORT; - if (INT_ADD_WRAPV (pc->time_zone, $1, &pc->time_zone)) YYABORT; } + if (ckd_add (&pc->time_zone, pc->time_zone, $1)) YYABORT; } | tDAYZONE { pc->time_zone = $1 + 60 * 60; } | tZONE tDST @@ -802,7 +808,7 @@ date: you want portability, use the ISO 8601 format. */ if (4 <= $1.digits) { - if (pc->parse_datetime_debug) + if (debugging (pc)) { intmax_t digits = $1.digits; dbg_printf (_("warning: value %"PRIdMAX" has %"PRIdMAX" digits. " @@ -816,7 +822,7 @@ date: } else { - if (pc->parse_datetime_debug) + if (debugging (pc)) dbg_printf (_("warning: value %"PRIdMAX" has less than 4 digits. " "Assuming MM/DD/YY[YY]\n"), $1.value); @@ -831,15 +837,15 @@ date: /* E.g., 17-JUN-1992. */ pc->day = $1.value; pc->month = $2; - if (INT_SUBTRACT_WRAPV (0, $3.value, &pc->year.value)) YYABORT; + if (ckd_sub (&pc->year.value, 0, $3.value)) YYABORT; pc->year.digits = $3.digits; } | tMONTH tSNUMBER tSNUMBER { /* E.g., JUN-17-1992. */ pc->month = $1; - if (INT_SUBTRACT_WRAPV (0, $2.value, &pc->day)) YYABORT; - if (INT_SUBTRACT_WRAPV (0, $3.value, &pc->year.value)) YYABORT; + if (ckd_sub (&pc->day, 0, $2.value)) YYABORT; + if (ckd_sub (&pc->year.value, 0, $3.value)) YYABORT; pc->year.digits = $3.digits; } | tMONTH tUNUMBER @@ -872,8 +878,8 @@ iso_8601_date: { /* ISO 8601 format. YYYY-MM-DD. */ pc->year = $1; - if (INT_SUBTRACT_WRAPV (0, $2.value, &pc->month)) YYABORT; - if (INT_SUBTRACT_WRAPV (0, $3.value, &pc->day)) YYABORT; + if (ckd_sub (&pc->month, 0, $2.value)) YYABORT; + if (ckd_sub (&pc->day, 0, $3.value)) YYABORT; } ; @@ -901,10 +907,10 @@ relunit: { $$ = RELATIVE_TIME_0; $$.month = 1; } | tORDINAL tDAY_UNIT { $$ = RELATIVE_TIME_0; - if (INT_MULTIPLY_WRAPV ($1, $2, &$$.day)) YYABORT; } + if (ckd_mul (&$$.day, $1, $2)) YYABORT; } | tUNUMBER tDAY_UNIT { $$ = RELATIVE_TIME_0; - if (INT_MULTIPLY_WRAPV ($1.value, $2, &$$.day)) YYABORT; } + if (ckd_mul (&$$.day, $1.value, $2)) YYABORT; } | tDAY_UNIT { $$ = RELATIVE_TIME_0; $$.day = $1; } | tORDINAL tHOUR_UNIT @@ -939,7 +945,7 @@ relunit_snumber: { $$ = RELATIVE_TIME_0; $$.month = $1.value; } | tSNUMBER tDAY_UNIT { $$ = RELATIVE_TIME_0; - if (INT_MULTIPLY_WRAPV ($1.value, $2, &$$.day)) YYABORT; } + if (ckd_mul (&$$.day, $1.value, $2)) YYABORT; } | tSNUMBER tHOUR_UNIT { $$ = RELATIVE_TIME_0; $$.hour = $1.value; } | tSNUMBER tMINUTE_UNIT @@ -959,14 +965,14 @@ signed_seconds: tSDECIMAL_NUMBER | tSNUMBER { if (time_overflow ($1.value)) YYABORT; - $$.tv_sec = $1.value; $$.tv_nsec = 0; } + $$ = (struct timespec) { .tv_sec = $1.value }; } ; unsigned_seconds: tUDECIMAL_NUMBER | tUNUMBER { if (time_overflow ($1.value)) YYABORT; - $$.tv_sec = $1.value; $$.tv_nsec = 0; } + $$ = (struct timespec) { .tv_sec = $1.value }; } ; number: @@ -1151,7 +1157,8 @@ static table const time_zone_table[] = RFC 822 got these backwards, but RFC 5322 makes the incorrect treatment optional, so do them the right way here. - Note 'T' is a special case, as it is used as the separator in ISO + 'J' is special, as it is local time. + 'T' is also special, as it is the separator in ISO 8601 date and time of day representation. */ static table const military_table[] = { @@ -1164,6 +1171,7 @@ static table const military_table[] = { "G", tZONE, HOUR ( 7) }, { "H", tZONE, HOUR ( 8) }, { "I", tZONE, HOUR ( 9) }, + { "J", 'J', 0 }, { "K", tZONE, HOUR (10) }, { "L", tZONE, HOUR (11) }, { "M", tZONE, HOUR (12) }, @@ -1207,10 +1215,10 @@ time_zone_hhmm (parser_control *pc, textint s, intmax_t mm) n_minutes = (s.value / 100) * 60 + s.value % 100; else { - overflow |= INT_MULTIPLY_WRAPV (s.value, 60, &n_minutes); + overflow |= ckd_mul (&n_minutes, s.value, 60); overflow |= (s.negative - ? INT_SUBTRACT_WRAPV (n_minutes, mm, &n_minutes) - : INT_ADD_WRAPV (n_minutes, mm, &n_minutes)); + ? ckd_sub (&n_minutes, n_minutes, mm) + : ckd_add (&n_minutes, n_minutes, mm)); } if (overflow || ! (-24 * 60 <= n_minutes && n_minutes <= 24 * 60)) @@ -1243,7 +1251,7 @@ enum { TM_YEAR_BUFSIZE = INT_BUFSIZE_BOUND (int) + 1 }; static char const * tm_year_str (int tm_year, char buf[TM_YEAR_BUFSIZE]) { - verify (TM_YEAR_BASE % 100 == 0); + static_assert (TM_YEAR_BASE % 100 == 0); sprintf (buf, &"-%02d%02d"[-TM_YEAR_BASE <= tm_year], abs (tm_year / 100 + TM_YEAR_BASE / 100), abs (tm_year % 100)); @@ -1270,8 +1278,8 @@ to_tm_year (textint textyear, bool debug, int *tm_year) } if (year < 0 - ? INT_SUBTRACT_WRAPV (-TM_YEAR_BASE, year, tm_year) - : INT_SUBTRACT_WRAPV (year, TM_YEAR_BASE, tm_year)) + ? ckd_sub (tm_year, -TM_YEAR_BASE, year) + : ckd_sub (tm_year, year, TM_YEAR_BASE)) { if (debug) dbg_printf (_("error: out-of-range year %"PRIdMAX"\n"), year); @@ -1427,9 +1435,9 @@ yylex (union YYSTYPE *lvalp, parser_control *pc) time_t value = 0; do { - if (INT_MULTIPLY_WRAPV (value, 10, &value)) + if (ckd_mul (&value, value, 10)) return '?'; - if (INT_ADD_WRAPV (value, sign < 0 ? '0' - c : c - '0', &value)) + if (ckd_add (&value, value, sign < 0 ? '0' - c : c - '0')) return '?'; c = *++p; } @@ -1466,13 +1474,13 @@ yylex (union YYSTYPE *lvalp, parser_control *pc) negative. */ if (sign < 0 && ns) { - if (INT_SUBTRACT_WRAPV (s, 1, &s)) + if (ckd_sub (&s, s, 1)) return '?'; ns = BILLION - ns; } - lvalp->timespec.tv_sec = s; - lvalp->timespec.tv_nsec = ns; + lvalp->timespec = (struct timespec) { .tv_sec = s, + .tv_nsec = ns }; pc->input = p; return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER; } @@ -1504,7 +1512,7 @@ yylex (union YYSTYPE *lvalp, parser_control *pc) tp = lookup_word (pc, buff); if (! tp) { - if (pc->parse_datetime_debug) + if (debugging (pc)) dbg_printf (_("error: unknown word '%s'\n"), buff); return '?'; } @@ -1531,11 +1539,10 @@ yylex (union YYSTYPE *lvalp, parser_control *pc) } /* Do nothing if the parser reports an error. */ -static int -yyerror (parser_control const *pc _GL_UNUSED, - char const *s _GL_UNUSED) +static void +yyerror (_GL_UNUSED parser_control const *pc, + _GL_UNUSED char const *s) { - return 0; } /* If *TM0 is the old and *TM1 is the new value of a struct tm after @@ -1630,7 +1637,7 @@ debug_strftime (struct tm const *tm, char *buf, int n) date: normalized time: '(Y-M-D) 2006-04-02 03:45:00' date: __ date: possible reasons: - date: non-existing due to daylight-saving time; + date: nonexistent due to daylight-saving time; date: numeric values overflow; date: missing timezone; */ @@ -1651,7 +1658,7 @@ debug_mktime_not_ok (struct tm const *tm0, struct tm const *tm1, const bool dst_shift = eq_sec && eq_min && !eq_hour && eq_mday && eq_month && eq_year; - if (!pc->parse_datetime_debug) + if (!debugging (pc)) return; dbg_printf (_("error: invalid date/time value:\n")); @@ -1682,7 +1689,7 @@ debug_mktime_not_ok (struct tm const *tm0, struct tm const *tm1, dbg_printf (_(" possible reasons:\n")); if (dst_shift) - dbg_printf (_(" non-existing due to daylight-saving time;\n")); + dbg_printf (_(" nonexistent due to daylight-saving time;\n")); if (!eq_mday && !eq_month) dbg_printf (_(" invalid day/month combination;\n")); dbg_printf (_(" numeric values overflow;\n")); @@ -1690,29 +1697,15 @@ debug_mktime_not_ok (struct tm const *tm0, struct tm const *tm1, : _("missing timezone"))); } -/* The original interface: run with debug=false and the default timezone. */ -bool -parse_datetime (struct timespec *result, char const *p, - struct timespec const *now) -{ - char const *tzstring = getenv ("TZ"); - timezone_t tz = tzalloc (tzstring); - if (!tz) - return false; - bool ok = parse_datetime2 (result, p, now, 0, tz, tzstring); - tzfree (tz); - return ok; -} - /* Parse a date/time string, storing the resulting time value into *RESULT. The string itself is pointed to by P. Return true if successful. P can be an incomplete or relative time specification; if so, use *NOW as the basis for the returned time. Default to timezone TZDEFAULT, which corresponds to tzalloc (TZSTRING). */ -bool -parse_datetime2 (struct timespec *result, char const *p, - struct timespec const *now, unsigned int flags, - timezone_t tzdefault, char const *tzstring) +static bool +parse_datetime_body (struct timespec *result, char const *p, + struct timespec const *now, unsigned int flags, + timezone_t tzdefault, char const *tzstring) { struct tm tm; struct tm tm0; @@ -1803,10 +1796,12 @@ parse_datetime2 (struct timespec *result, char const *p, parser_control pc; pc.input = p; +#ifdef GNULIB_PARSE_DATETIME2 pc.parse_datetime_debug = (flags & PARSE_DATETIME_DEBUG) != 0; - if (INT_ADD_WRAPV (tmp.tm_year, TM_YEAR_BASE, &pc.year.value)) +#endif + if (ckd_add (&pc.year.value, tmp.tm_year, TM_YEAR_BASE)) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf (_("error: initial year out of range\n")); goto fail; } @@ -1815,8 +1810,7 @@ parse_datetime2 (struct timespec *result, char const *p, pc.day = tmp.tm_mday; pc.hour = tmp.tm_hour; pc.minutes = tmp.tm_min; - pc.seconds.tv_sec = tmp.tm_sec; - pc.seconds.tv_nsec = Start_ns; + pc.seconds = (struct timespec) { .tv_sec = tmp.tm_sec, .tv_nsec = Start_ns }; tm.tm_isdst = tmp.tm_isdst; pc.meridian = MER24; @@ -1826,6 +1820,7 @@ parse_datetime2 (struct timespec *result, char const *p, pc.dates_seen = 0; pc.days_seen = 0; pc.times_seen = 0; + pc.J_zones_seen = 0; pc.local_zones_seen = 0; pc.dsts_seen = 0; pc.zones_seen = 0; @@ -1851,7 +1846,7 @@ parse_datetime2 (struct timespec *result, char const *p, for (quarter = 1; quarter <= 3; quarter++) { time_t probe; - if (INT_ADD_WRAPV (Start, quarter * (90 * 24 * 60 * 60), &probe)) + if (ckd_add (&probe, Start, quarter * (90 * 24 * 60 * 60))) break; struct tm probe_tm; if (localtime_rz (tz, &probe, &probe_tm) && probe_tm.tm_zone @@ -1900,7 +1895,7 @@ parse_datetime2 (struct timespec *result, char const *p, if (yyparse (&pc) != 0) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf ((input_sentinel <= pc.input ? _("error: parsing failed\n") : _("error: parsing failed, stopped at '%s'\n")), @@ -1911,7 +1906,7 @@ parse_datetime2 (struct timespec *result, char const *p, /* Determine effective timezone source. */ - if (pc.parse_datetime_debug) + if (debugging (&pc)) { dbg_printf (_("input timezone: ")); @@ -1951,9 +1946,9 @@ parse_datetime2 (struct timespec *result, char const *p, else { if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen - | (pc.local_zones_seen + pc.zones_seen))) + | (pc.J_zones_seen + pc.local_zones_seen + pc.zones_seen))) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) { if (pc.times_seen > 1) dbg_printf ("error: seen multiple time parts\n"); @@ -1963,17 +1958,17 @@ parse_datetime2 (struct timespec *result, char const *p, dbg_printf ("error: seen multiple days parts\n"); if (pc.dsts_seen > 1) dbg_printf ("error: seen multiple daylight-saving parts\n"); - if ((pc.local_zones_seen + pc.zones_seen) > 1) + if ((pc.J_zones_seen + pc.local_zones_seen + pc.zones_seen) > 1) dbg_printf ("error: seen multiple time-zone parts\n"); } goto fail; } - if (! to_tm_year (pc.year, pc.parse_datetime_debug, &tm.tm_year) - || INT_ADD_WRAPV (pc.month, -1, &tm.tm_mon) - || INT_ADD_WRAPV (pc.day, 0, &tm.tm_mday)) + if (! to_tm_year (pc.year, debugging (&pc), &tm.tm_year) + || ckd_add (&tm.tm_mon, pc.month, -1) + || ckd_add (&tm.tm_mday, pc.day, 0)) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf (_("error: year, month, or day overflow\n")); goto fail; } @@ -1984,14 +1979,14 @@ parse_datetime2 (struct timespec *result, char const *p, { char const *mrd = (pc.meridian == MERam ? "am" : pc.meridian == MERpm ?"pm" : ""); - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf (_("error: invalid hour %"PRIdMAX"%s\n"), pc.hour, mrd); goto fail; } tm.tm_min = pc.minutes; tm.tm_sec = pc.seconds.tv_sec; - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf ((pc.times_seen ? _("using specified time as starting value: '%s'\n") : _("using current time as starting value: '%s'\n")), @@ -2001,7 +1996,7 @@ parse_datetime2 (struct timespec *result, char const *p, { tm.tm_hour = tm.tm_min = tm.tm_sec = 0; pc.seconds.tv_nsec = 0; - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf ("warning: using midnight as starting time: 00:00:00\n"); } @@ -2047,7 +2042,7 @@ parse_datetime2 (struct timespec *result, char const *p, timezone_t tz2 = tzalloc (tz2buf); if (!tz2) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf (_("error: tzalloc (\"%s\") failed\n"), tz2buf); goto fail; } @@ -2076,23 +2071,22 @@ parse_datetime2 (struct timespec *result, char const *p, if (pc.days_seen && ! pc.dates_seen) { intmax_t dayincr; - if (INT_MULTIPLY_WRAPV ((pc.day_ordinal - - (0 < pc.day_ordinal - && tm.tm_wday != pc.day_number)), - 7, &dayincr) - || INT_ADD_WRAPV ((pc.day_number - tm.tm_wday + 7) % 7, - dayincr, &dayincr) - || INT_ADD_WRAPV (dayincr, tm.tm_mday, &tm.tm_mday)) - Start = -1; - else + tm.tm_yday = -1; + intmax_t day_ordinal = (pc.day_ordinal + - (0 < pc.day_ordinal + && tm.tm_wday != pc.day_number)); + if (! (ckd_mul (&dayincr, day_ordinal, 7) + || ckd_add (&dayincr, (pc.day_number - tm.tm_wday + 7) % 7, + dayincr) + || ckd_add (&tm.tm_mday, dayincr, tm.tm_mday))) { tm.tm_isdst = -1; Start = mktime_z (tz, &tm); } - if (Start == (time_t) -1) + if (tm.tm_yday < 0) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf (_("error: day '%s' " "(day ordinal=%"PRIdMAX" number=%d) " "resulted in an invalid date: '%s'\n"), @@ -2103,14 +2097,14 @@ parse_datetime2 (struct timespec *result, char const *p, goto fail; } - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf (_("new start date: '%s' is '%s'\n"), str_days (&pc, dbg_ord, sizeof dbg_ord), debug_strfdatetime (&tm, &pc, dbg_tm, sizeof dbg_tm)); } - if (pc.parse_datetime_debug) + if (debugging (&pc)) { if (!pc.dates_seen && !pc.days_seen) dbg_printf (_("using current date as starting value: '%s'\n"), @@ -2128,7 +2122,7 @@ parse_datetime2 (struct timespec *result, char const *p, /* Add relative date. */ if (pc.rel.year | pc.rel.month | pc.rel.day) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) { if ((pc.rel.year != 0 || pc.rel.month != 0) && tm.tm_mday != 15) dbg_printf (_("warning: when adding relative months/years, " @@ -2141,11 +2135,11 @@ parse_datetime2 (struct timespec *result, char const *p, } int year, month, day; - if (INT_ADD_WRAPV (tm.tm_year, pc.rel.year, &year) - || INT_ADD_WRAPV (tm.tm_mon, pc.rel.month, &month) - || INT_ADD_WRAPV (tm.tm_mday, pc.rel.day, &day)) + if (ckd_add (&year, tm.tm_year, pc.rel.year) + || ckd_add (&month, tm.tm_mon, pc.rel.month) + || ckd_add (&day, tm.tm_mday, pc.rel.day)) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf (_("error: %s:%d\n"), __FILE__, __LINE__); goto fail; } @@ -2156,10 +2150,11 @@ parse_datetime2 (struct timespec *result, char const *p, tm.tm_min = tm0.tm_min; tm.tm_sec = tm0.tm_sec; tm.tm_isdst = tm0.tm_isdst; + tm.tm_wday = -1; Start = mktime_z (tz, &tm); - if (Start == (time_t) -1) + if (tm.tm_wday < 0) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf (_("error: adding relative date resulted " "in an invalid date: '%s'\n"), debug_strfdatetime (&tm, &pc, dbg_tm, @@ -2167,7 +2162,7 @@ parse_datetime2 (struct timespec *result, char const *p, goto fail; } - if (pc.parse_datetime_debug) + if (debugging (&pc)) { dbg_printf (_("after date adjustment " "(%+"PRIdMAX" years, %+"PRIdMAX" months, " @@ -2239,12 +2234,12 @@ parse_datetime2 (struct timespec *result, char const *p, : (overflow = true, 0)); #endif intmax_t delta; - overflow |= INT_SUBTRACT_WRAPV (pc.time_zone, utcoff, &delta); + overflow |= ckd_sub (&delta, pc.time_zone, utcoff); time_t t1; - overflow |= INT_SUBTRACT_WRAPV (Start, delta, &t1); + overflow |= ckd_sub (&t1, Start, delta); if (overflow) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf (_("error: timezone %d caused time_t overflow\n"), pc.time_zone); goto fail; @@ -2252,7 +2247,7 @@ parse_datetime2 (struct timespec *result, char const *p, Start = t1; } - if (pc.parse_datetime_debug) + if (debugging (&pc)) { intmax_t Starti = Start; dbg_printf (_("'%s' = %"PRIdMAX" epoch-seconds\n"), @@ -2275,14 +2270,14 @@ parse_datetime2 (struct timespec *result, char const *p, int d4 = (sum_ns - normalized_ns) / BILLION; intmax_t d1, t1, d2, t2, t3; time_t t4; - if (INT_MULTIPLY_WRAPV (pc.rel.hour, 60 * 60, &d1) - || INT_ADD_WRAPV (Start, d1, &t1) - || INT_MULTIPLY_WRAPV (pc.rel.minutes, 60, &d2) - || INT_ADD_WRAPV (t1, d2, &t2) - || INT_ADD_WRAPV (t2, pc.rel.seconds, &t3) - || INT_ADD_WRAPV (t3, d4, &t4)) + if (ckd_mul (&d1, pc.rel.hour, 60 * 60) + || ckd_add (&t1, Start, d1) + || ckd_mul (&d2, pc.rel.minutes, 60) + || ckd_add (&t2, t1, d2) + || ckd_add (&t3, t2, pc.rel.seconds) + || ckd_add (&t4, t3, d4)) { - if (pc.parse_datetime_debug) + if (debugging (&pc)) dbg_printf (_("error: adding relative time caused an " "overflow\n")); goto fail; @@ -2291,7 +2286,7 @@ parse_datetime2 (struct timespec *result, char const *p, result->tv_sec = t4; result->tv_nsec = normalized_ns; - if (pc.parse_datetime_debug + if (debugging (&pc) && (pc.rel.hour | pc.rel.minutes | pc.rel.seconds | pc.rel.ns)) { dbg_printf (_("after time adjustment (%+"PRIdMAX" hours, " @@ -2322,7 +2317,7 @@ parse_datetime2 (struct timespec *result, char const *p, } } - if (pc.parse_datetime_debug) + if (debugging (&pc)) { /* Special case: using 'date -u' simply set TZ=UTC0 */ if (! tzstring) @@ -2373,6 +2368,36 @@ parse_datetime2 (struct timespec *result, char const *p, return ok; } +#ifdef GNULIB_PARSE_DATETIME2 +/* Parse a date/time string, storing the resulting time value into *RESULT. + The string itself is pointed to by P. Return true if successful. + P can be an incomplete or relative time specification; if so, use + *NOW as the basis for the returned time. Default to timezone + TZDEFAULT, which corresponds to tzalloc (TZSTRING). */ +bool +parse_datetime2 (struct timespec *result, char const *p, + struct timespec const *now, unsigned int flags, + timezone_t tzdefault, char const *tzstring) +{ + return parse_datetime_body (result, p, now, flags, tzdefault, tzstring); +} +#endif + + +/* The plain interface: run with debug=false and the default timezone. */ +bool +parse_datetime (struct timespec *result, char const *p, + struct timespec const *now) +{ + char const *tzstring = getenv ("TZ"); + timezone_t tz = tzalloc (tzstring); + if (!tz) + return false; + bool ok = parse_datetime_body (result, p, now, 0, tz, tzstring); + tzfree (tz); + return ok; +} + #if TEST int |