From eb87d3e1e9210d9387536cc3ece4e32aacdc5009 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 8 Oct 2024 18:59:37 +0900 Subject: [PATCH 1/4] time-util: fix parsing timestamp with NZ timezone Fixes a bug caused by ef658a63f8163607d9e04f710cd26c0d36ff68ce. --- src/basic/time-util.c | 8 ++++++-- src/test/test-time-util.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/basic/time-util.c b/src/basic/time-util.c index ac2b078225..06c465e0e4 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -994,8 +994,12 @@ int parse_timestamp(const char *t, usec_t *ret) { assert(t); t_len = strlen(t); - if (t_len > 2 && t[t_len - 1] == 'Z' && t[t_len - 2] != ' ') /* RFC3339-style welded UTC: "1985-04-12T23:20:50.52Z" */ - return parse_timestamp_impl(t, t_len - 1, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret); + if (t_len > 2 && t[t_len - 1] == 'Z') { + /* Try to parse as RFC3339-style welded UTC: "1985-04-12T23:20:50.52Z" */ + r = parse_timestamp_impl(t, t_len - 1, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret); + if (r >= 0) + return r; + } if (t_len > 7 && IN_SET(t[t_len - 6], '+', '-') && t[t_len - 7] != ' ') { /* RFC3339-style welded offset: "1990-12-31T15:59:60-08:00" */ k = strptime(&t[t_len - 6], "%z", &tm); diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c index e37a7ff610..4fbb1c4c7a 100644 --- a/src/test/test-time-util.c +++ b/src/test/test-time-util.c @@ -857,6 +857,29 @@ static void test_parse_timestamp_impl(const char *tz) { test_parse_timestamp_one("69-12-31 19:00:01.0010 EST", 0, USEC_PER_SEC + 1000); } + if (timezone_is_valid("NZ", LOG_DEBUG)) { + /* NZ (+1200) */ + test_parse_timestamp_one("Thu 1970-01-01 12:01 NZ", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("Thu 1970-01-01 12:00:01 NZ", 0, USEC_PER_SEC); + test_parse_timestamp_one("Thu 1970-01-01 12:00:01.001 NZ", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("Thu 1970-01-01 12:00:01.0010 NZ", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("Thu 70-01-01 12:01 NZ", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("Thu 70-01-01 12:00:01 NZ", 0, USEC_PER_SEC); + test_parse_timestamp_one("Thu 70-01-01 12:00:01.001 NZ", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("Thu 70-01-01 12:00:01.0010 NZ", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("1970-01-01 12:01 NZ", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("1970-01-01 12:00:01 NZ", 0, USEC_PER_SEC); + test_parse_timestamp_one("1970-01-01 12:00:01.001 NZ", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("1970-01-01 12:00:01.0010 NZ", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("70-01-01 12:01 NZ", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("70-01-01 12:00:01 NZ", 0, USEC_PER_SEC); + test_parse_timestamp_one("70-01-01 12:00:01.001 NZ", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("70-01-01 12:00:01.0010 NZ", 0, USEC_PER_SEC + 1000); + } + /* -06 */ test_parse_timestamp_one("Wed 1969-12-31 18:01 -06", 0, USEC_PER_MINUTE); test_parse_timestamp_one("Wed 1969-12-31 18:00:01 -06", 0, USEC_PER_SEC); @@ -934,6 +957,14 @@ static void test_parse_timestamp_impl(const char *tz) { test_parse_timestamp_one("yesterday", 0, today - USEC_PER_DAY); } + /* with timezone */ + if (tz) { + _cleanup_free_ char *s = NULL; + + ASSERT_NOT_NULL(s = strjoin("Fri 2012-11-23 23:02:15 ", tz)); + ASSERT_OK(parse_timestamp(s, NULL)); + } + /* relative */ assert_se(parse_timestamp("now", &now_usec) == 0); test_parse_timestamp_one("+5hours", USEC_PER_MINUTE, now_usec + 5 * USEC_PER_HOUR); From 6d3012bab4ce4c1ed260598d05b4e9f2ea471658 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 8 Oct 2024 13:50:02 +0900 Subject: [PATCH 2/4] time-util: copy input string before fork() Fixes #34670. --- src/basic/time-util.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 06c465e0e4..1e042b8b60 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -1043,6 +1043,14 @@ int parse_timestamp(const char *t, usec_t *ret) { if (shared == MAP_FAILED) return negative_errno(); + /* The input string may be in argv. Let's copy it. */ + _cleanup_free_ char *t_copy = strdup(t); + if (!t_copy) + return -ENOMEM; + + t = t_copy; + assert_se(tz = endswith(t_copy, tz)); + r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_WAIT, NULL); if (r < 0) { (void) munmap(shared, sizeof *shared); From 25999f868fe0e9684af7a364224ac42071b70f74 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 8 Oct 2024 13:52:40 +0900 Subject: [PATCH 3/4] test: add test cases for timestamp with time zone --- test/units/TEST-65-ANALYZE.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/units/TEST-65-ANALYZE.sh b/test/units/TEST-65-ANALYZE.sh index 76db01d68a..c00559f6fb 100755 --- a/test/units/TEST-65-ANALYZE.sh +++ b/test/units/TEST-65-ANALYZE.sh @@ -172,6 +172,13 @@ systemd-analyze calendar --base-time=yesterday --iterations=5 '*-* *:*:*' systemd-analyze timestamp now systemd-analyze timestamp -- -1 systemd-analyze timestamp yesterday now tomorrow +systemd-analyze timestamp 'Fri 2012-11-23 23:02:15' +systemd-analyze timestamp 'Fri 2012-11-23 23:02:15 UTC' +systemd-analyze timestamp 'Fri 2012-11-23 23:02:15 CET' +for i in $(timedatectl list-timezones); do + [[ -e "/usr/share/zoneinfo/$i" ]] || continue + systemd-analyze timestamp "Fri 2012-11-23 23:02:15 $i" +done (! systemd-analyze timestamp yesterday never tomorrow) (! systemd-analyze timestamp 1) (! systemd-analyze timestamp '*-2-29 0:0:0') From 483db6867ac62db982c0035517c989b32e1da448 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 8 Oct 2024 13:55:56 +0900 Subject: [PATCH 4/4] analyze: use RET_GATHER() --- src/analyze/analyze-calendar.c | 6 +----- src/analyze/analyze-timestamp.c | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/analyze/analyze-calendar.c b/src/analyze/analyze-calendar.c index 6daab08443..b3afa1d4b0 100644 --- a/src/analyze/analyze-calendar.c +++ b/src/analyze/analyze-calendar.c @@ -126,11 +126,7 @@ int verb_calendar(int argc, char *argv[], void *userdata) { n = now(CLOCK_REALTIME); /* We want to use the same "base" for all expressions */ STRV_FOREACH(p, strv_skip(argv, 1)) { - int k; - - k = test_calendar_one(n, *p); - if (r == 0 && k < 0) - r = k; + RET_GATHER(r, test_calendar_one(n, *p)); if (p[1]) putchar('\n'); diff --git a/src/analyze/analyze-timestamp.c b/src/analyze/analyze-timestamp.c index 97de4387ab..8c96a8ec55 100644 --- a/src/analyze/analyze-timestamp.c +++ b/src/analyze/analyze-timestamp.c @@ -76,11 +76,7 @@ int verb_timestamp(int argc, char *argv[], void *userdata) { int r = 0; STRV_FOREACH(p, strv_skip(argv, 1)) { - int k; - - k = test_timestamp_one(*p); - if (r == 0 && k < 0) - r = k; + RET_GATHER(r, test_timestamp_one(*p)); if (p[1]) putchar('\n');