From bd5770da76ee157d3b31323ed2d22f5d9082bb36 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 12 Mar 2023 20:57:16 +0900 Subject: [PATCH 1/4] time-util: make USEC_TIMESTAMP_FORMATTABLE_MAX for 32bit system off by one day As the same reason why we take one day off for 64bit case. This also makes both upper bounds always defined for testing. --- src/basic/time-util.h | 14 +++++++++----- src/test/test-date.c | 4 ++-- src/test/test-time-util.c | 4 ++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/basic/time-util.h b/src/basic/time-util.h index 087f5324ef..79a6715656 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -205,13 +205,17 @@ static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) { return usec_sub_unsigned(timestamp, (usec_t) delta); } +/* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit + * year territory. However, since we want to stay away from this in all timezones we take one day off. */ +#define USEC_TIMESTAMP_FORMATTABLE_MAX_64BIT ((usec_t) 253402214399000000) /* Thu 9999-12-30 23:59:59 UTC */ +/* With a 32bit time_t we can't go beyond 2038... + * We parse timestamp with RFC-822/ISO 8601 (e.g. +06, or -03:00) as UTC, hence the upper bound must be off + * by USEC_PER_DAY. See parse_timestamp() for more details. */ +#define USEC_TIMESTAMP_FORMATTABLE_MAX_32BIT (((usec_t) INT32_MAX) * USEC_PER_SEC - USEC_PER_DAY) #if SIZEOF_TIME_T == 8 - /* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit - * year territory. However, since we want to stay away from this in all timezones we take one day off. */ -# define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 253402214399000000) +# define USEC_TIMESTAMP_FORMATTABLE_MAX USEC_TIMESTAMP_FORMATTABLE_MAX_64BIT #elif SIZEOF_TIME_T == 4 -/* With a 32bit time_t we can't go beyond 2038... */ -# define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 2147483647000000) +# define USEC_TIMESTAMP_FORMATTABLE_MAX USEC_TIMESTAMP_FORMATTABLE_MAX_32BIT #else # error "Yuck, time_t is neither 4 nor 8 bytes wide?" #endif diff --git a/src/test/test-date.c b/src/test/test-date.c index 097066b61a..16c6d4e5be 100644 --- a/src/test/test-date.c +++ b/src/test/test-date.c @@ -101,8 +101,8 @@ int main(int argc, char *argv[]) { test_should_fail("9999-12-31 00:00:00 UTC"); test_should_fail("10000-01-01 00:00:00 UTC"); #elif SIZEOF_TIME_T == 4 - test_should_pass("2038-01-19 03:14:07 UTC"); - test_should_fail("2038-01-19 03:14:08 UTC"); + test_should_pass("2038-01-18 03:14:07 UTC"); + test_should_fail("2038-01-18 03:14:08 UTC"); #endif return 0; diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c index e974520bbe..44eb0d25f6 100644 --- a/src/test/test-time-util.c +++ b/src/test/test-time-util.c @@ -617,8 +617,8 @@ TEST(format_timestamp_range) { test_format_timestamp_one(USEC_TIMESTAMP_FORMATTABLE_MAX + 1, TIMESTAMP_US_UTC, "--- XXXX-XX-XX XX:XX:XX.XXXXXX UTC"); test_format_timestamp_one(USEC_TIMESTAMP_FORMATTABLE_MAX + 1, TIMESTAMP_DATE, "--- XXXX-XX-XX"); #elif SIZEOF_TIME_T == 4 - test_format_timestamp_one(USEC_TIMESTAMP_FORMATTABLE_MAX, TIMESTAMP_UTC, "Tue 2038-01-19 03:14:07 UTC"); - test_format_timestamp_one(USEC_TIMESTAMP_FORMATTABLE_MAX, TIMESTAMP_DATE, "Tue 2038-01-19"); + test_format_timestamp_one(USEC_TIMESTAMP_FORMATTABLE_MAX, TIMESTAMP_UTC, "Mon 2038-01-18 03:14:07 UTC"); + test_format_timestamp_one(USEC_TIMESTAMP_FORMATTABLE_MAX, TIMESTAMP_DATE, "Mon 2038-01-18"); test_format_timestamp_one(USEC_TIMESTAMP_FORMATTABLE_MAX + 1, TIMESTAMP_UTC, "--- XXXX-XX-XX XX:XX:XX UTC"); test_format_timestamp_one(USEC_TIMESTAMP_FORMATTABLE_MAX + 1, TIMESTAMP_US_UTC, "--- XXXX-XX-XX XX:XX:XX.XXXXXX UTC"); test_format_timestamp_one(USEC_TIMESTAMP_FORMATTABLE_MAX + 1, TIMESTAMP_DATE, "--- XXXX-XX-XX"); From 6bed374280c0c24f3893ced34c66ac0f8d7ca7c0 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 12 Mar 2023 20:59:38 +0900 Subject: [PATCH 2/4] test: add more testcases for formatting/parsing timestamp --- src/test/test-time-util.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c index 44eb0d25f6..e5a4cb6515 100644 --- a/src/test/test-time-util.c +++ b/src/test/test-time-util.c @@ -407,6 +407,10 @@ static void test_format_timestamp_impl(usec_t x) { static void test_format_timestamp_loop(void) { test_format_timestamp_impl(USEC_PER_SEC); + test_format_timestamp_impl(USEC_TIMESTAMP_FORMATTABLE_MAX_32BIT-1); + test_format_timestamp_impl(USEC_TIMESTAMP_FORMATTABLE_MAX_32BIT); + test_format_timestamp_impl(USEC_TIMESTAMP_FORMATTABLE_MAX-1); + test_format_timestamp_impl(USEC_TIMESTAMP_FORMATTABLE_MAX); for (unsigned i = 0; i < TRIAL; i++) { usec_t x; From 37c6a3dc1a91e413ab6f949dc1d87f7749e63fb5 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 13 Mar 2023 03:47:15 +0900 Subject: [PATCH 3/4] time-util: add note about on DST change --- src/basic/time-util.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 7d44358cd8..657e231b0d 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -662,6 +662,9 @@ static int parse_timestamp_impl( * +5min * -5days * @2147483647 (seconds since epoch) + * + * Note, on DST change, 00:00:00 may not exist and in that case the time part may be shifted. + * E.g. "Sun 2023-03-13 America/Havana" is parsed as "Sun 2023-03-13 01:00:00 CDT". */ assert(t); From cfacd245e798282fcb9b3231bd6e857abfe124fc Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 13 Mar 2023 03:47:45 +0900 Subject: [PATCH 4/4] test-time-util: do not fail on DST change --- src/test/test-time-util.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c index e5a4cb6515..e0386d4ec5 100644 --- a/src/test/test-time-util.c +++ b/src/test/test-time-util.c @@ -636,12 +636,30 @@ static void test_parse_timestamp_one(const char *str, usec_t max_diff, usec_t ex int r; r = parse_timestamp(str, &usec); - log_debug("/* %s(%s): max_diff="USEC_FMT", expected="USEC_FMT", result="USEC_FMT"*/", __func__, str, max_diff, expected, usec); + log_debug("/* %s(%s): max_diff="USEC_FMT", expected="USEC_FMT", result="USEC_FMT" */", __func__, str, max_diff, expected, usec); assert_se(r >= 0); assert_se(usec >= expected); assert_se(usec_sub_unsigned(usec, expected) <= max_diff); } +static bool time_is_zero(usec_t usec) { + const char *s; + + s = FORMAT_TIMESTAMP(usec); + return strstr(s, " 00:00:00 "); +} + +static bool timezone_equal(usec_t today, usec_t target) { + const char *s, *t, *sz, *tz; + + s = FORMAT_TIMESTAMP(today); + t = FORMAT_TIMESTAMP(target); + assert_se(sz = strrchr(s, ' ')); + assert_se(tz = strrchr(t, ' ')); + log_debug("%s("USEC_FMT", "USEC_FMT") -> %s, %s", __func__, today, target, s, t); + return streq(sz, tz); +} + static void test_parse_timestamp_impl(const char *tz) { usec_t today, now_usec; @@ -823,12 +841,17 @@ static void test_parse_timestamp_impl(const char *tz) { /* without date */ assert_se(parse_timestamp("today", &today) == 0); - test_parse_timestamp_one("00:01", 0, today + USEC_PER_MINUTE); - test_parse_timestamp_one("00:00:01", 0, today + USEC_PER_SEC); - test_parse_timestamp_one("00:00:01.001", 0, today + USEC_PER_SEC + 1000); - test_parse_timestamp_one("00:00:01.0010", 0, today + USEC_PER_SEC + 1000); - test_parse_timestamp_one("tomorrow", 0, today + USEC_PER_DAY); - test_parse_timestamp_one("yesterday", 0, today - USEC_PER_DAY); + if (time_is_zero(today)) { + test_parse_timestamp_one("00:01", 0, today + USEC_PER_MINUTE); + test_parse_timestamp_one("00:00:01", 0, today + USEC_PER_SEC); + test_parse_timestamp_one("00:00:01.001", 0, today + USEC_PER_SEC + 1000); + test_parse_timestamp_one("00:00:01.0010", 0, today + USEC_PER_SEC + 1000); + + if (timezone_equal(today, today + USEC_PER_DAY) && time_is_zero(today + USEC_PER_DAY)) + test_parse_timestamp_one("tomorrow", 0, today + USEC_PER_DAY); + if (timezone_equal(today, today - USEC_PER_DAY) && time_is_zero(today - USEC_PER_DAY)) + test_parse_timestamp_one("yesterday", 0, today - USEC_PER_DAY); + } /* relative */ assert_se(parse_timestamp("now", &now_usec) == 0);