From: Dylan M. Taylor Date: Fri, 6 Mar 2026 12:27:10 +0000 (-0500) Subject: time-util: extract parse_calendar_date() from sysupdate X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=770958e24a2ee59593aa6833a4a825db0a6abbbc;p=thirdparty%2Fsystemd.git time-util: extract parse_calendar_date() from sysupdate Move the YYYY-MM-DD date parsing and validation logic from sysupdate-resource.c into a shared parse_calendar_date() function in time-util, so it can be reused by other subsystems. --- diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 5dd00af952d..4ba5bfafd71 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -1892,3 +1892,32 @@ TimestampStyle timestamp_style_from_string(const char *s) { return TIMESTAMP_US_UTC; return t; } + +int parse_calendar_date(const char *s, usec_t *ret) { + struct tm parsed_tm = {}, copy_tm; + usec_t usec; + const char *k; + int r; + + assert(s); + + k = strptime(s, "%Y-%m-%d", &parsed_tm); + if (!k || *k) + return -EINVAL; + + copy_tm = parsed_tm; + r = mktime_or_timegm_usec(©_tm, /* utc= */ true, &usec); + if (r < 0) + return r; + + /* Refuse non-normalized dates, e.g. Feb 30 */ + if (copy_tm.tm_mday != parsed_tm.tm_mday || + copy_tm.tm_mon != parsed_tm.tm_mon || + copy_tm.tm_year != parsed_tm.tm_year) + return -EINVAL; + + if (ret) + *ret = usec; + + return 0; +} diff --git a/src/basic/time-util.h b/src/basic/time-util.h index bde0b02d037..c3971889013 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -180,6 +180,7 @@ const char* etc_localtime(void); int mktime_or_timegm_usec(struct tm *tm, bool utc, usec_t *ret); int localtime_or_gmtime_usec(usec_t t, bool utc, struct tm *ret); +int parse_calendar_date(const char *s, usec_t *ret); uint32_t usec_to_jiffies(usec_t usec); usec_t jiffies_to_usec(uint32_t jiffies); diff --git a/src/sysupdate/sysupdate-resource.c b/src/sysupdate/sysupdate-resource.c index 1cc48201efa..ba1842b2d6c 100644 --- a/src/sysupdate/sysupdate-resource.c +++ b/src/sysupdate/sysupdate-resource.c @@ -433,24 +433,10 @@ static int process_magic_file( if (iovec_memcmp(&IOVEC_MAKE(expected_hash, sizeof(expected_hash)), hash) != 0) log_warning("Hash of best before marker file '%s' has unexpected value, proceeding anyway.", fn); - struct tm parsed_tm = {}; - const char *n = strptime(e, "%Y-%m-%d", &parsed_tm); - if (!n || *n != 0) { - /* Doesn't parse? Then it's not a best-before date */ - log_warning("Found best before marker with an invalid date, ignoring: %s", fn); - return 0; - } - - struct tm copy_tm = parsed_tm; usec_t best_before; - r = mktime_or_timegm_usec(©_tm, /* utc= */ true, &best_before); - if (r < 0) - return log_error_errno(r, "Failed to convert best before time: %m"); - if (copy_tm.tm_mday != parsed_tm.tm_mday || - copy_tm.tm_mon != parsed_tm.tm_mon || - copy_tm.tm_year != parsed_tm.tm_year) { - /* date was not normalized? (e.g. "30th of feb") */ - log_warning("Found best before marker with a non-normalized data, ignoring: %s", fn); + r = parse_calendar_date(e, &best_before); + if (r < 0) { + log_warning_errno(r, "Found best before marker with an invalid date, ignoring: %s", fn); return 0; } diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c index d5d4992f827..172056a9bf6 100644 --- a/src/test/test-time-util.c +++ b/src/test/test-time-util.c @@ -1281,4 +1281,30 @@ static int intro(void) { return EXIT_SUCCESS; } +TEST(parse_calendar_date) { + usec_t usec; + + /* Valid dates */ + ASSERT_OK(parse_calendar_date("2000-01-01", &usec)); + ASSERT_OK(parse_calendar_date("1970-01-01", &usec)); + ASSERT_EQ(usec, 0u); /* epoch */ + ASSERT_OK(parse_calendar_date("2000-02-29", &usec)); /* leap year */ + + /* NULL ret is allowed (validation only) */ + ASSERT_OK(parse_calendar_date("2000-06-15", NULL)); + + /* Non-normalized dates */ + ASSERT_ERROR(parse_calendar_date("2023-02-29", &usec), EINVAL); /* not a leap year */ + ASSERT_ERROR(parse_calendar_date("2023-04-31", &usec), EINVAL); /* April has 30 days */ + ASSERT_ERROR(parse_calendar_date("2023-13-01", &usec), EINVAL); /* month 13 */ + ASSERT_ERROR(parse_calendar_date("2023-00-01", &usec), EINVAL); /* month 0 */ + + /* Malformed input */ + ASSERT_ERROR(parse_calendar_date("", &usec), EINVAL); + ASSERT_ERROR(parse_calendar_date("not-a-date", &usec), EINVAL); + ASSERT_ERROR(parse_calendar_date("2023-06-15T00:00:00", &usec), EINVAL); /* trailing time */ + ASSERT_ERROR(parse_calendar_date("2023/06/15", &usec), EINVAL); /* wrong separator */ + ASSERT_ERROR(parse_calendar_date("06-15-2023", &usec), EINVAL); /* wrong order */ +} + DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro);