From 0b20d70d1c7c190fb943dd4d1f28e6f456d2193e Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 3 Mar 2023 19:40:40 +0900 Subject: [PATCH 1/8] test: use get_timezones() to iterate all known timezones --- src/test/test-time-util.c | 48 ++++++++------------------------------- 1 file changed, 10 insertions(+), 38 deletions(-) diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c index d6fbb8582f5..cd4d635dfab 100644 --- a/src/test/test-time-util.c +++ b/src/test/test-time-util.c @@ -1,6 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include "dirent-util.h" #include "env-util.h" #include "fd-util.h" #include "fileio.h" @@ -421,23 +420,18 @@ TEST(FORMAT_TIMESTAMP) { test_format_timestamp_loop(); } -static void test_format_timestamp_with_tz_one(const char *name1, const char *name2) { - _cleanup_free_ char *buf = NULL, *tz = NULL; - const char *name, *saved_tz; +static void test_format_timestamp_with_tz_one(const char *tz) { + const char *saved_tz, *colon_tz; - if (name2) - assert_se(buf = path_join(name1, name2)); - name = buf ?: name1; - - if (!timezone_is_valid(name, LOG_DEBUG)) + if (!timezone_is_valid(tz, LOG_DEBUG)) return; - log_info("/* %s(%s) */", __func__, name); + log_info("/* %s(%s) */", __func__, tz); saved_tz = getenv("TZ"); - assert_se(tz = strjoin(":", name)); - assert_se(setenv("TZ", tz, 1) >= 0); + assert_se(colon_tz = strjoina(":", tz)); + assert_se(setenv("TZ", colon_tz, 1) >= 0); tzset(); log_debug("%s: tzname[0]=%s, tzname[1]=%s", tz, strempty(tzname[0]), strempty(tzname[1])); @@ -448,33 +442,11 @@ static void test_format_timestamp_with_tz_one(const char *name1, const char *nam } TEST(FORMAT_TIMESTAMP_with_tz) { - if (!slow_tests_enabled()) - return (void) log_tests_skipped("slow tests are disabled"); + _cleanup_strv_free_ char **timezones = NULL; - _cleanup_closedir_ DIR *dir = opendir("/usr/share/zoneinfo"); - if (!dir) - return (void) log_tests_skipped_errno(errno, "Failed to open /usr/share/zoneinfo"); - - FOREACH_DIRENT(de, dir, break) { - if (de->d_type == DT_REG) - test_format_timestamp_with_tz_one(de->d_name, NULL); - - else if (de->d_type == DT_DIR) { - if (streq(de->d_name, "right")) - /* The test does not support timezone with leap second info. */ - continue; - - _cleanup_closedir_ DIR *subdir = xopendirat(dirfd(dir), de->d_name, 0); - if (!subdir) { - log_notice_errno(errno, "Failed to open /usr/share/zoneinfo/%s, ignoring: %m", de->d_name); - continue; - } - - FOREACH_DIRENT(subde, subdir, break) - if (subde->d_type == DT_REG) - test_format_timestamp_with_tz_one(de->d_name, subde->d_name); - } - } + assert_se(get_timezones(&timezones) >= 0); + STRV_FOREACH(tz, timezones) + test_format_timestamp_with_tz_one(*tz); } TEST(format_timestamp_relative_full) { From 8677fdc78bf252bf8f35cf20791ad11f1c5fcfff Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 3 Mar 2023 12:07:25 +0900 Subject: [PATCH 2/8] test: clear tzname[] after timezone is changed Fixes the issue reported in fe56f21ae3943e79ce9faeb40b79c7093c50e644. --- src/test/test-time-util.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c index cd4d635dfab..cec5bffd79a 100644 --- a/src/test/test-time-util.c +++ b/src/test/test-time-util.c @@ -707,6 +707,7 @@ TEST(parse_timestamp) { test_parse_timestamp_one("70-01-01 09:00:01.0010 JST", 0, USEC_PER_SEC + 1000); assert_se(set_unset_env("TZ", saved_tz, true) == 0); + tzset(); } if (timezone_is_valid("America/New_York", LOG_DEBUG)) { @@ -756,6 +757,7 @@ TEST(parse_timestamp) { test_parse_timestamp_one("69-12-31 19:00:01.0010 EST", 0, USEC_PER_SEC + 1000); assert_se(set_unset_env("TZ", saved_tz, true) == 0); + tzset(); } /* -06 */ @@ -823,13 +825,12 @@ TEST(parse_timestamp) { /* without date */ assert_se(parse_timestamp("today", &today) == 0); - // FIXME: currently failing, needs to investigate the changes from https://github.com/systemd/systemd/pull/26409 - /*test_parse_timestamp_one("00:01", 0, today + USEC_PER_MINUTE); + 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);*/ + test_parse_timestamp_one("yesterday", 0, today - USEC_PER_DAY); /* relative */ assert_se(parse_timestamp("now", &now_usec) == 0); From d8f3ad627c9a857d46d442f8ab722c1efab30d5c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 3 Mar 2023 12:09:59 +0900 Subject: [PATCH 3/8] test: test parse_timestamp() in various timezone --- src/test/test-time-util.c | 59 ++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c index cec5bffd79a..e974520bbe2 100644 --- a/src/test/test-time-util.c +++ b/src/test/test-time-util.c @@ -628,15 +628,17 @@ TEST(format_timestamp_range) { } static void test_parse_timestamp_one(const char *str, usec_t max_diff, usec_t expected) { - usec_t usec; + usec_t usec = USEC_INFINITY; + int r; - log_debug("/* %s(%s) */", __func__, str); - assert_se(parse_timestamp(str, &usec) >= 0); + 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); + assert_se(r >= 0); assert_se(usec >= expected); assert_se(usec_sub_unsigned(usec, expected) <= max_diff); } -TEST(parse_timestamp) { +static void test_parse_timestamp_impl(const char *tz) { usec_t today, now_usec; /* UTC */ @@ -681,10 +683,9 @@ TEST(parse_timestamp) { test_parse_timestamp_one("70-01-01 09:00:01 Asia/Tokyo", 0, USEC_PER_SEC); test_parse_timestamp_one("70-01-01 09:00:01.001 Asia/Tokyo", 0, USEC_PER_SEC + 1000); test_parse_timestamp_one("70-01-01 09:00:01.0010 Asia/Tokyo", 0, USEC_PER_SEC + 1000); + } - const char *saved_tz = getenv("TZ"); - assert_se(setenv("TZ", ":Asia/Tokyo", 1) >= 0); - + if (streq_ptr(tz, "Asia/Tokyo")) { /* JST (+0900) */ test_parse_timestamp_one("Thu 1970-01-01 09:01 JST", 0, USEC_PER_MINUTE); test_parse_timestamp_one("Thu 1970-01-01 09:00:01 JST", 0, USEC_PER_SEC); @@ -705,9 +706,6 @@ TEST(parse_timestamp) { test_parse_timestamp_one("70-01-01 09:00:01 JST", 0, USEC_PER_SEC); test_parse_timestamp_one("70-01-01 09:00:01.001 JST", 0, USEC_PER_SEC + 1000); test_parse_timestamp_one("70-01-01 09:00:01.0010 JST", 0, USEC_PER_SEC + 1000); - - assert_se(set_unset_env("TZ", saved_tz, true) == 0); - tzset(); } if (timezone_is_valid("America/New_York", LOG_DEBUG)) { @@ -731,10 +729,9 @@ TEST(parse_timestamp) { test_parse_timestamp_one("69-12-31 19:00:01 America/New_York", 0, USEC_PER_SEC); test_parse_timestamp_one("69-12-31 19:00:01.001 America/New_York", 0, USEC_PER_SEC + 1000); test_parse_timestamp_one("69-12-31 19:00:01.0010 America/New_York", 0, USEC_PER_SEC + 1000); + } - const char *saved_tz = getenv("TZ"); - assert_se(setenv("TZ", ":America/New_York", 1) >= 0); - + if (streq_ptr(tz, "America/New_York")) { /* EST (-0500) */ test_parse_timestamp_one("Wed 1969-12-31 19:01 EST", 0, USEC_PER_MINUTE); test_parse_timestamp_one("Wed 1969-12-31 19:00:01 EST", 0, USEC_PER_SEC); @@ -755,9 +752,6 @@ TEST(parse_timestamp) { test_parse_timestamp_one("69-12-31 19:00:01 EST", 0, USEC_PER_SEC); test_parse_timestamp_one("69-12-31 19:00:01.001 EST", 0, USEC_PER_SEC + 1000); test_parse_timestamp_one("69-12-31 19:00:01.0010 EST", 0, USEC_PER_SEC + 1000); - - assert_se(set_unset_env("TZ", saved_tz, true) == 0); - tzset(); } /* -06 */ @@ -842,6 +836,39 @@ TEST(parse_timestamp) { test_parse_timestamp_one("30minutes ago", USEC_PER_MINUTE, now_usec - 30 * USEC_PER_MINUTE); } +TEST(parse_timestamp) { + test_parse_timestamp_impl(NULL); +} + +static void test_parse_timestamp_with_tz_one(const char *tz) { + const char *saved_tz, *colon_tz; + + if (!timezone_is_valid(tz, LOG_DEBUG)) + return; + + log_info("/* %s(%s) */", __func__, tz); + + saved_tz = getenv("TZ"); + + assert_se(colon_tz = strjoina(":", tz)); + assert_se(setenv("TZ", colon_tz, 1) >= 0); + tzset(); + log_debug("%s: tzname[0]=%s, tzname[1]=%s", tz, strempty(tzname[0]), strempty(tzname[1])); + + test_parse_timestamp_impl(tz); + + assert_se(set_unset_env("TZ", saved_tz, true) == 0); + tzset(); +} + +TEST(parse_timestamp_with_tz) { + _cleanup_strv_free_ char **timezones = NULL; + + assert_se(get_timezones(&timezones) >= 0); + STRV_FOREACH(tz, timezones) + test_parse_timestamp_with_tz_one(*tz); +} + TEST(deserialize_dual_timestamp) { int r; dual_timestamp t; From b66b3c409900a77b3da7b366ae5a0179abacea99 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 3 Mar 2023 13:22:27 +0900 Subject: [PATCH 4/8] meson: extend timeout for test-time-util The test forks so many child processes, and may hit the default time limit on slow environment or running with sanitizers. --- src/test/meson.build | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/test/meson.build b/src/test/meson.build index dc9e95a7bdd..ae4c3f13aa8 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -156,7 +156,6 @@ simple_tests += files( 'test-strxcpyx.c', 'test-sysctl-util.c', 'test-terminal-util.c', - 'test-time-util.c', 'test-tmpfile-util.c', 'test-tmpfiles.c', 'test-tpm2.c', @@ -501,6 +500,10 @@ tests += [ udev_includes, ], }, + { + 'sources' : files('test-time-util.c'), + 'timeout' : 120, + }, { 'sources' : files('test-udev.c'), 'link_with' : [ From ca9c9d8d8e999fd80fc43d002c8d5b20c4c1a0a4 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 3 Mar 2023 15:24:23 +0900 Subject: [PATCH 5/8] time-util: fix typo Follow-up for 7a9afae6040af0417d893328cb44b622dcdcb94f. --- src/basic/time-util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 2d2a507b203..3fa003eab28 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -841,7 +841,7 @@ from_tm: if (gmtoff < 0) { plus = -gmtoff * USEC_PER_SEC; - /* If gmtoff is negative, the string maye be too old to be parsed as UTC. + /* If gmtoff is negative, the string may be too old to be parsed as UTC. * E.g. 1969-12-31 23:00:00 -06 == 1970-01-01 05:00:00 UTC * We assumed that gmtoff is in the range of -24:00…+24:00, hence the only date we need to * handle here is 1969-12-31. So, let's shift the date with one day, then subtract the shift From 8beb47c824c87267a3852264fbfd0ab3152a309b Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 3 Mar 2023 15:51:56 +0900 Subject: [PATCH 6/8] time-util: rename len -> tz_offset And merge parse_timestamp_with_tz() with parse_timestamp_impl(). Addresses the post-merge comment: https://github.com/systemd/systemd/pull/26409#discussion_r1118647440 --- src/basic/time-util.c | 78 ++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 3fa003eab28..ec16eb0bdf0 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -612,7 +612,7 @@ char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { static int parse_timestamp_impl( const char *t, - bool with_tz, + size_t tz_offset, bool utc, int isdst, long gmtoff, @@ -638,7 +638,9 @@ static int parse_timestamp_impl( { "Sat", 6 }, }; + _cleanup_free_ char *t_alloc = NULL; usec_t usec, plus = 0, minus = 0; + bool with_tz = false; int r, weekday = -1; unsigned fractional = 0; const char *k; @@ -663,6 +665,20 @@ static int parse_timestamp_impl( assert(t); + if (tz_offset != SIZE_MAX) { + /* If the input string contains timezone, then cut it here. */ + + if (tz_offset <= 1) /* timezone must be after a space. */ + return -EINVAL; + + t_alloc = strndup(t, tz_offset - 1); + if (!t_alloc) + return -ENOMEM; + + t = t_alloc; + with_tz = true; + } + if (t[0] == '@' && !with_tz) return parse_sec(t + 1, ret); @@ -880,22 +896,8 @@ finish: return 0; } -static int parse_timestamp_with_tz(const char *t, size_t len, bool utc, int isdst, long gmtoff, usec_t *ret) { - _cleanup_free_ char *buf = NULL; - +static int parse_timestamp_maybe_with_tz(const char *t, size_t tz_offset, bool valid_tz, usec_t *ret) { assert(t); - assert(len > 0); - - buf = strndup(t, len); - if (!buf) - return -ENOMEM; - - return parse_timestamp_impl(buf, /* with_tz = */ true, utc, isdst, gmtoff, ret); -} - -static int parse_timestamp_maybe_with_tz(const char *t, size_t len, bool valid_tz, usec_t *ret) { - assert(t); - assert(len > 0); tzset(); @@ -903,19 +905,19 @@ static int parse_timestamp_maybe_with_tz(const char *t, size_t len, bool valid_t if (isempty(tzname[j])) continue; - if (!streq(t + len + 1, tzname[j])) + if (!streq(t + tz_offset, tzname[j])) continue; /* The specified timezone matches tzname[] of the local timezone. */ - return parse_timestamp_with_tz(t, len, /* utc = */ false, /* isdst = */ j, /* gmtoff = */ 0, ret); + return parse_timestamp_impl(t, tz_offset, /* utc = */ false, /* isdst = */ j, /* gmtoff = */ 0, ret); } if (valid_tz) /* We know that the specified timezone is a valid zoneinfo (e.g. Asia/Tokyo). So, simply drop * the timezone and parse the remaining string as a local time. */ - return parse_timestamp_with_tz(t, len, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret); + return parse_timestamp_impl(t, tz_offset, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret); - return parse_timestamp_impl(t, /* with_tz = */ false, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret); + return parse_timestamp_impl(t, /* tz_offset = */ SIZE_MAX, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret); } typedef struct ParseTimestampResult { @@ -925,45 +927,45 @@ typedef struct ParseTimestampResult { int parse_timestamp(const char *t, usec_t *ret) { ParseTimestampResult *shared, tmp; - const char *k, *tz, *space; + const char *k, *tz, *current_tz; + size_t tz_offset; struct tm tm; int r; assert(t); - space = strrchr(t, ' '); - if (!space) - return parse_timestamp_impl(t, /* with_tz = */ false, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret); + tz = strrchr(t, ' '); + if (!tz) + return parse_timestamp_impl(t, /* tz_offset = */ SIZE_MAX, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret); - /* The string starts with space. */ - if (space == t) - return -EINVAL; + tz++; + tz_offset = tz - t; /* Shortcut, parse the string as UTC. */ - if (streq(space + 1, "UTC")) - return parse_timestamp_with_tz(t, space - t, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret); + if (streq(tz, "UTC")) + return parse_timestamp_impl(t, tz_offset, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret); /* If the timezone is compatible with RFC-822/ISO 8601 (e.g. +06, or -03:00) then parse the string as * UTC and shift the result. */ - k = strptime(space + 1, "%z", &tm); + k = strptime(tz, "%z", &tm); if (k && *k == '\0') { /* glibc accepts gmtoff more than 24 hours, but we refuse it. */ if ((usec_t) labs(tm.tm_gmtoff) > USEC_PER_DAY / USEC_PER_SEC) return -EINVAL; - return parse_timestamp_with_tz(t, space - t, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm.tm_gmtoff, ret); + return parse_timestamp_impl(t, tz_offset, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm.tm_gmtoff, ret); } /* If the last word is not a timezone file (e.g. Asia/Tokyo), then let's check if it matches * tzname[] of the local timezone, e.g. JST or CEST. */ - if (!timezone_is_valid(space + 1, LOG_DEBUG)) - return parse_timestamp_maybe_with_tz(t, space - t, /* valid_tz = */ false, ret); + if (!timezone_is_valid(tz, LOG_DEBUG)) + return parse_timestamp_maybe_with_tz(t, tz_offset, /* valid_tz = */ false, ret); /* Shortcut. If the current $TZ is equivalent to the specified timezone, it is not necessary to fork * the process. */ - tz = getenv("TZ"); - if (tz && *tz == ':' && streq(tz + 1, space + 1)) - return parse_timestamp_maybe_with_tz(t, space - t, /* valid_tz = */ true, ret); + current_tz = getenv("TZ"); + if (current_tz && *current_tz == ':' && streq(current_tz + 1, tz)) + return parse_timestamp_maybe_with_tz(t, tz_offset, /* valid_tz = */ true, ret); /* Otherwise, to avoid polluting the current environment variables, let's fork the process and set * the specified timezone in the child process. */ @@ -981,7 +983,7 @@ int parse_timestamp(const char *t, usec_t *ret) { const char *colon_tz; /* tzset(3) says $TZ should be prefixed with ":" if we reference timezone files */ - colon_tz = strjoina(":", space + 1); + colon_tz = strjoina(":", tz); if (setenv("TZ", colon_tz, 1) != 0) { shared->return_value = negative_errno(); @@ -990,7 +992,7 @@ int parse_timestamp(const char *t, usec_t *ret) { tzset(); - shared->return_value = parse_timestamp_maybe_with_tz(t, space - t, /* valid_tz = */ true, &shared->usec); + shared->return_value = parse_timestamp_maybe_with_tz(t, tz_offset, /* valid_tz = */ true, &shared->usec); _exit(EXIT_SUCCESS); } From 87e0fd575e8b5a747d25b0bd4cf5a7061c6f6ece Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 3 Mar 2023 15:55:42 +0900 Subject: [PATCH 7/8] time-util: extend comment a bit --- src/basic/time-util.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/basic/time-util.c b/src/basic/time-util.c index ec16eb0bdf0..a76bb6bc579 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -912,12 +912,11 @@ static int parse_timestamp_maybe_with_tz(const char *t, size_t tz_offset, bool v return parse_timestamp_impl(t, tz_offset, /* utc = */ false, /* isdst = */ j, /* gmtoff = */ 0, ret); } - if (valid_tz) - /* We know that the specified timezone is a valid zoneinfo (e.g. Asia/Tokyo). So, simply drop - * the timezone and parse the remaining string as a local time. */ - return parse_timestamp_impl(t, tz_offset, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret); - - return parse_timestamp_impl(t, /* tz_offset = */ SIZE_MAX, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret); + /* If we know that the last word is a valid timezone (e.g. Asia/Tokyo), then simply drop the timezone + * and parse the remaining string as a local time. If we know that the last word is not a timezone, + * then assume that it is a part of the time and try to parse the whole string as a local time. */ + return parse_timestamp_impl(t, valid_tz ? tz_offset : SIZE_MAX, + /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret); } typedef struct ParseTimestampResult { @@ -946,7 +945,8 @@ int parse_timestamp(const char *t, usec_t *ret) { return parse_timestamp_impl(t, tz_offset, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret); /* If the timezone is compatible with RFC-822/ISO 8601 (e.g. +06, or -03:00) then parse the string as - * UTC and shift the result. */ + * UTC and shift the result. Note, this must be earlier than the timezone check with tzname[], as + * tzname[] may be in the same format. */ k = strptime(tz, "%z", &tm); if (k && *k == '\0') { /* glibc accepts gmtoff more than 24 hours, but we refuse it. */ From 9f819781caee1b9a0bfe3a3211455a940a06f1ae Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 3 Mar 2023 16:00:59 +0900 Subject: [PATCH 8/8] time-util: refuse non-zero gmtoff with non-UTC timezone Also this moves the range check for gmtoff to parse_timestamp_impl(), to address the post-merge comment: https://github.com/systemd/systemd/pull/26409#discussion_r1118650190 --- src/basic/time-util.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/basic/time-util.c b/src/basic/time-util.c index a76bb6bc579..26fd1bae653 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -679,6 +679,15 @@ static int parse_timestamp_impl( with_tz = true; } + if (utc) { + /* glibc accepts gmtoff more than 24 hours, but we refuse it. */ + if ((usec_t) labs(gmtoff) * USEC_PER_SEC > USEC_PER_DAY) + return -EINVAL; + } else { + if (gmtoff != 0) + return -EINVAL; + } + if (t[0] == '@' && !with_tz) return parse_sec(t + 1, ret); @@ -948,13 +957,8 @@ int parse_timestamp(const char *t, usec_t *ret) { * UTC and shift the result. Note, this must be earlier than the timezone check with tzname[], as * tzname[] may be in the same format. */ k = strptime(tz, "%z", &tm); - if (k && *k == '\0') { - /* glibc accepts gmtoff more than 24 hours, but we refuse it. */ - if ((usec_t) labs(tm.tm_gmtoff) > USEC_PER_DAY / USEC_PER_SEC) - return -EINVAL; - + if (k && *k == '\0') return parse_timestamp_impl(t, tz_offset, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm.tm_gmtoff, ret); - } /* If the last word is not a timezone file (e.g. Asia/Tokyo), then let's check if it matches * tzname[] of the local timezone, e.g. JST or CEST. */