From: Yu Watanabe Date: Sat, 11 Feb 2023 20:30:49 +0000 (+0900) Subject: test: add tests for format_timestamp() and parse_timestamp() with various timezone X-Git-Tag: v254-rc1~1163^2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F26409%2Fhead;p=thirdparty%2Fsystemd.git test: add tests for format_timestamp() and parse_timestamp() with various timezone --- diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c index dee012fa2ef..d4ba1249772 100644 --- a/src/test/test-time-util.c +++ b/src/test/test-time-util.c @@ -1,6 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "dirent-util.h" #include "env-util.h" +#include "fd-util.h" +#include "fileio.h" #include "random-util.h" #include "serialize.h" #include "string-util.h" @@ -8,6 +11,8 @@ #include "tests.h" #include "time-util.h" +#define TRIAL 100u + TEST(parse_sec) { usec_t u; @@ -334,11 +339,11 @@ TEST(usec_sub_signed) { } TEST(format_timestamp) { - for (unsigned i = 0; i < 100; i++) { + for (unsigned i = 0; i < TRIAL; i++) { char buf[CONST_MAX(FORMAT_TIMESTAMP_MAX, FORMAT_TIMESPAN_MAX)]; usec_t x, y; - x = random_u64_range(2147483600 * USEC_PER_SEC) + 1; + x = random_u64_range(USEC_TIMESTAMP_FORMATTABLE_MAX - USEC_PER_SEC) + USEC_PER_SEC; assert_se(format_timestamp(buf, sizeof(buf), x)); log_debug("%s", buf); @@ -384,20 +389,91 @@ TEST(format_timestamp) { } } +static void test_format_timestamp_impl(usec_t x) { + bool success; + const char *xx, *yy; + usec_t y; + + xx = FORMAT_TIMESTAMP(x); + assert_se(xx); + assert_se(parse_timestamp(xx, &y) >= 0); + yy = FORMAT_TIMESTAMP(y); + assert_se(yy); + + success = (x / USEC_PER_SEC == y / USEC_PER_SEC) && streq(xx, yy); + log_full(success ? LOG_DEBUG : LOG_ERR, "@" USEC_FMT " → %s → @" USEC_FMT " → %s", x, xx, y, yy); + assert_se(x / USEC_PER_SEC == y / USEC_PER_SEC); + assert_se(streq(xx, yy)); +} + +static void test_format_timestamp_loop(void) { + test_format_timestamp_impl(USEC_PER_SEC); + + for (unsigned i = 0; i < TRIAL; i++) { + usec_t x; + + x = random_u64_range(USEC_TIMESTAMP_FORMATTABLE_MAX - USEC_PER_SEC) + USEC_PER_SEC; + test_format_timestamp_impl(x); + } +} + TEST(FORMAT_TIMESTAMP) { - for (unsigned i = 0; i < 100; i++) { - _cleanup_free_ char *buf; - usec_t x, y; + test_format_timestamp_loop(); +} - x = random_u64_range(2147483600 * USEC_PER_SEC) + 1; +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; - /* strbuf() is to test the macro in an argument to a function call. */ - assert_se(buf = strdup(FORMAT_TIMESTAMP(x))); - log_debug("%s", buf); - assert_se(parse_timestamp(buf, &y) >= 0); - assert_se(x / USEC_PER_SEC == y / USEC_PER_SEC); + if (name2) + assert_se(buf = path_join(name1, name2)); + name = buf ?: name1; + + if (!timezone_is_valid(name, LOG_DEBUG)) + return; + + log_info("/* %s(%s) */", __func__, name); + + saved_tz = getenv("TZ"); + + assert_se(tz = strjoin(":", name)); + assert_se(setenv("TZ", tz, 1) >= 0); + tzset(); + log_debug("%s: tzname[0]=%s, tzname[1]=%s", tz, strempty(tzname[0]), strempty(tzname[1])); + + test_format_timestamp_loop(); + + assert_se(set_unset_env("TZ", saved_tz, true) == 0); + tzset(); +} + +TEST(FORMAT_TIMESTAMP_with_tz) { + if (!slow_tests_enabled()) + return (void) log_tests_skipped("slow tests are disabled"); + + _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; - assert_se(streq(FORMAT_TIMESTAMP(x), buf)); + _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); + } } } @@ -579,6 +655,219 @@ TEST(format_timestamp_range) { test_format_timestamp_one(USEC_INFINITY, TIMESTAMP_UTC, NULL); } +static void test_parse_timestamp_one(const char *str, usec_t max_diff, usec_t expected) { + usec_t usec; + + log_debug("/* %s(%s) */", __func__, str); + assert_se(parse_timestamp(str, &usec) >= 0); + assert_se(usec >= expected); + assert_se(usec_sub_unsigned(usec, expected) <= max_diff); +} + +TEST(parse_timestamp) { + usec_t today, now_usec; + + /* UTC */ + test_parse_timestamp_one("Thu 1970-01-01 00:01 UTC", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("Thu 1970-01-01 00:00:01 UTC", 0, USEC_PER_SEC); + test_parse_timestamp_one("Thu 1970-01-01 00:00:01.001 UTC", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("Thu 1970-01-01 00:00:01.0010 UTC", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("Thu 70-01-01 00:01 UTC", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("Thu 70-01-01 00:00:01 UTC", 0, USEC_PER_SEC); + test_parse_timestamp_one("Thu 70-01-01 00:00:01.001 UTC", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("Thu 70-01-01 00:00:01.0010 UTC", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("1970-01-01 00:01 UTC", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("1970-01-01 00:00:01 UTC", 0, USEC_PER_SEC); + test_parse_timestamp_one("1970-01-01 00:00:01.001 UTC", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("1970-01-01 00:00:01.0010 UTC", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("70-01-01 00:01 UTC", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("70-01-01 00:00:01 UTC", 0, USEC_PER_SEC); + test_parse_timestamp_one("70-01-01 00:00:01.001 UTC", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("70-01-01 00:00:01.0010 UTC", 0, USEC_PER_SEC + 1000); + + if (timezone_is_valid("Asia/Tokyo", LOG_DEBUG)) { + /* Asia/Tokyo (+0900) */ + test_parse_timestamp_one("Thu 1970-01-01 09:01 Asia/Tokyo", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("Thu 1970-01-01 09:00:01 Asia/Tokyo", 0, USEC_PER_SEC); + test_parse_timestamp_one("Thu 1970-01-01 09:00:01.001 Asia/Tokyo", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("Thu 1970-01-01 09:00:01.0010 Asia/Tokyo", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("Thu 70-01-01 09:01 Asia/Tokyo", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("Thu 70-01-01 09:00:01 Asia/Tokyo", 0, USEC_PER_SEC); + test_parse_timestamp_one("Thu 70-01-01 09:00:01.001 Asia/Tokyo", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("Thu 70-01-01 09:00:01.0010 Asia/Tokyo", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("1970-01-01 09:01 Asia/Tokyo", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("1970-01-01 09:00:01 Asia/Tokyo", 0, USEC_PER_SEC); + test_parse_timestamp_one("1970-01-01 09:00:01.001 Asia/Tokyo", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("1970-01-01 09:00:01.0010 Asia/Tokyo", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("70-01-01 09:01 Asia/Tokyo", 0, USEC_PER_MINUTE); + 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); + + /* 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); + test_parse_timestamp_one("Thu 1970-01-01 09:00:01.001 JST", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("Thu 1970-01-01 09:00:01.0010 JST", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("Thu 70-01-01 09:01 JST", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("Thu 70-01-01 09:00:01 JST", 0, USEC_PER_SEC); + test_parse_timestamp_one("Thu 70-01-01 09:00:01.001 JST", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("Thu 70-01-01 09:00:01.0010 JST", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("1970-01-01 09:01 JST", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("1970-01-01 09:00:01 JST", 0, USEC_PER_SEC); + test_parse_timestamp_one("1970-01-01 09:00:01.001 JST", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("1970-01-01 09:00:01.0010 JST", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("70-01-01 09:01 JST", 0, USEC_PER_MINUTE); + 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); + } + + if (timezone_is_valid("America/New_York", LOG_DEBUG)) { + /* America/New_York (-0500) */ + test_parse_timestamp_one("Wed 1969-12-31 19:01 America/New_York", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("Wed 1969-12-31 19:00:01 America/New_York", 0, USEC_PER_SEC); + test_parse_timestamp_one("Wed 1969-12-31 19:00:01.001 America/New_York", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("Wed 1969-12-31 19:00:01.0010 America/New_York", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("Wed 69-12-31 19:01 America/New_York", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("Wed 69-12-31 19:00:01 America/New_York", 0, USEC_PER_SEC); + test_parse_timestamp_one("Wed 69-12-31 19:00:01.001 America/New_York", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("Wed 69-12-31 19:00:01.0010 America/New_York", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("1969-12-31 19:01 America/New_York", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("1969-12-31 19:00:01 America/New_York", 0, USEC_PER_SEC); + test_parse_timestamp_one("1969-12-31 19:00:01.001 America/New_York", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("1969-12-31 19:00:01.0010 America/New_York", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("69-12-31 19:01 America/New_York", 0, USEC_PER_MINUTE); + 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); + + /* 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); + test_parse_timestamp_one("Wed 1969-12-31 19:00:01.001 EST", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("Wed 1969-12-31 19:00:01.0010 EST", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("Wed 69-12-31 19:01 EST", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("Wed 69-12-31 19:00:01 EST", 0, USEC_PER_SEC); + test_parse_timestamp_one("Wed 69-12-31 19:00:01.001 EST", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("Wed 69-12-31 19:00:01.0010 EST", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("1969-12-31 19:01 EST", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("1969-12-31 19:00:01 EST", 0, USEC_PER_SEC); + test_parse_timestamp_one("1969-12-31 19:00:01.001 EST", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("1969-12-31 19:00:01.0010 EST", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("69-12-31 19:01 EST", 0, USEC_PER_MINUTE); + 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); + } + + /* -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); + test_parse_timestamp_one("Wed 1969-12-31 18:00:01.001 -06", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("Wed 1969-12-31 18:00:01.0010 -06", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("Wed 69-12-31 18:01 -06", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("Wed 69-12-31 18:00:01 -06", 0, USEC_PER_SEC); + test_parse_timestamp_one("Wed 69-12-31 18:00:01.001 -06", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("Wed 69-12-31 18:00:01.0010 -06", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("1969-12-31 18:01 -06", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("1969-12-31 18:00:01 -06", 0, USEC_PER_SEC); + test_parse_timestamp_one("1969-12-31 18:00:01.001 -06", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("1969-12-31 18:00:01.0010 -06", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("69-12-31 18:01 -06", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("69-12-31 18:00:01 -06", 0, USEC_PER_SEC); + test_parse_timestamp_one("69-12-31 18:00:01.001 -06", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("69-12-31 18:00:01.0010 -06", 0, USEC_PER_SEC + 1000); + + /* -0600 */ + test_parse_timestamp_one("Wed 1969-12-31 18:01 -0600", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("Wed 1969-12-31 18:00:01 -0600", 0, USEC_PER_SEC); + test_parse_timestamp_one("Wed 1969-12-31 18:00:01.001 -0600", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("Wed 1969-12-31 18:00:01.0010 -0600", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("Wed 69-12-31 18:01 -0600", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("Wed 69-12-31 18:00:01 -0600", 0, USEC_PER_SEC); + test_parse_timestamp_one("Wed 69-12-31 18:00:01.001 -0600", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("Wed 69-12-31 18:00:01.0010 -0600", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("1969-12-31 18:01 -0600", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("1969-12-31 18:00:01 -0600", 0, USEC_PER_SEC); + test_parse_timestamp_one("1969-12-31 18:00:01.001 -0600", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("1969-12-31 18:00:01.0010 -0600", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("69-12-31 18:01 -0600", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("69-12-31 18:00:01 -0600", 0, USEC_PER_SEC); + test_parse_timestamp_one("69-12-31 18:00:01.001 -0600", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("69-12-31 18:00:01.0010 -0600", 0, USEC_PER_SEC + 1000); + + /* -06:00 */ + test_parse_timestamp_one("Wed 1969-12-31 18:01 -06:00", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("Wed 1969-12-31 18:00:01 -06:00", 0, USEC_PER_SEC); + test_parse_timestamp_one("Wed 1969-12-31 18:00:01.001 -06:00", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("Wed 1969-12-31 18:00:01.0010 -06:00", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("Wed 69-12-31 18:01 -06:00", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("Wed 69-12-31 18:00:01 -06:00", 0, USEC_PER_SEC); + test_parse_timestamp_one("Wed 69-12-31 18:00:01.001 -06:00", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("Wed 69-12-31 18:00:01.0010 -06:00", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("1969-12-31 18:01 -06:00", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("1969-12-31 18:00:01 -06:00", 0, USEC_PER_SEC); + test_parse_timestamp_one("1969-12-31 18:00:01.001 -06:00", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("1969-12-31 18:00:01.0010 -06:00", 0, USEC_PER_SEC + 1000); + + test_parse_timestamp_one("69-12-31 18:01 -06:00", 0, USEC_PER_MINUTE); + test_parse_timestamp_one("69-12-31 18:00:01 -06:00", 0, USEC_PER_SEC); + test_parse_timestamp_one("69-12-31 18:00:01.001 -06:00", 0, USEC_PER_SEC + 1000); + test_parse_timestamp_one("69-12-31 18:00:01.0010 -06:00", 0, USEC_PER_SEC + 1000); + + /* 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); + + /* relative */ + assert_se(parse_timestamp("now", &now_usec) == 0); + test_parse_timestamp_one("+5hours", USEC_PER_MINUTE, now_usec + 5 * USEC_PER_HOUR); + if (now_usec >= 10 * USEC_PER_DAY) + test_parse_timestamp_one("-10days", USEC_PER_MINUTE, now_usec - 10 * USEC_PER_DAY); + test_parse_timestamp_one("2weeks left", USEC_PER_MINUTE, now_usec + 2 * USEC_PER_WEEK); + if (now_usec >= 30 * USEC_PER_MINUTE) + test_parse_timestamp_one("30minutes ago", USEC_PER_MINUTE, now_usec - 30 * USEC_PER_MINUTE); +} + TEST(deserialize_dual_timestamp) { int r; dual_timestamp t; @@ -702,6 +991,71 @@ TEST(map_clock_usec) { } } +static void test_timezone_offset_change_one(const char *utc, const char *pretty) { + usec_t x, y, z; + char *s; + + assert_se(parse_timestamp(utc, &x) >= 0); + + s = FORMAT_TIMESTAMP_STYLE(x, TIMESTAMP_UTC); + assert_se(parse_timestamp(s, &y) >= 0); + log_debug("%s -> " USEC_FMT " -> %s -> " USEC_FMT, utc, x, s, y); + assert_se(streq(s, utc)); + assert_se(x == y); + + assert_se(parse_timestamp(pretty, &y) >= 0); + s = FORMAT_TIMESTAMP_STYLE(y, TIMESTAMP_PRETTY); + assert_se(parse_timestamp(s, &z) >= 0); + log_debug("%s -> " USEC_FMT " -> %s -> " USEC_FMT, pretty, y, s, z); + assert_se(streq(s, pretty)); + assert_se(x == y); + assert_se(x == z); +} + +TEST(timezone_offset_change) { + const char *tz = getenv("TZ"); + + /* See issue #26370. */ + + if (timezone_is_valid("Africa/Casablanca", LOG_DEBUG)) { + assert_se(setenv("TZ", ":Africa/Casablanca", 1) >= 0); + tzset(); + log_debug("Africa/Casablanca: tzname[0]=%s, tzname[1]=%s", strempty(tzname[0]), strempty(tzname[1])); + + test_timezone_offset_change_one("Sun 2015-10-25 01:59:59 UTC", "Sun 2015-10-25 02:59:59 +01"); + test_timezone_offset_change_one("Sun 2015-10-25 02:00:00 UTC", "Sun 2015-10-25 02:00:00 +00"); + test_timezone_offset_change_one("Sun 2018-06-17 01:59:59 UTC", "Sun 2018-06-17 01:59:59 +00"); + test_timezone_offset_change_one("Sun 2018-06-17 02:00:00 UTC", "Sun 2018-06-17 03:00:00 +01"); + test_timezone_offset_change_one("Sun 2018-10-28 01:59:59 UTC", "Sun 2018-10-28 02:59:59 +01"); + test_timezone_offset_change_one("Sun 2018-10-28 02:00:00 UTC", "Sun 2018-10-28 03:00:00 +01"); + } + + if (timezone_is_valid("Asia/Atyrau", LOG_DEBUG)) { + assert_se(setenv("TZ", ":Asia/Atyrau", 1) >= 0); + tzset(); + log_debug("Asia/Atyrau: tzname[0]=%s, tzname[1]=%s", strempty(tzname[0]), strempty(tzname[1])); + + test_timezone_offset_change_one("Sat 2004-03-27 21:59:59 UTC", "Sun 2004-03-28 01:59:59 +04"); + test_timezone_offset_change_one("Sat 2004-03-27 22:00:00 UTC", "Sun 2004-03-28 03:00:00 +05"); + test_timezone_offset_change_one("Sat 2004-10-30 21:59:59 UTC", "Sun 2004-10-31 02:59:59 +05"); + test_timezone_offset_change_one("Sat 2004-10-30 22:00:00 UTC", "Sun 2004-10-31 03:00:00 +05"); + } + + if (timezone_is_valid("Chile/EasterIsland", LOG_DEBUG)) { + assert_se(setenv("TZ", ":Chile/EasterIsland", 1) >= 0); + tzset(); + log_debug("Chile/EasterIsland: tzname[0]=%s, tzname[1]=%s", strempty(tzname[0]), strempty(tzname[1])); + + test_timezone_offset_change_one("Sun 1981-10-11 03:59:59 UTC", "Sat 1981-10-10 20:59:59 -07"); + test_timezone_offset_change_one("Sun 1981-10-11 04:00:00 UTC", "Sat 1981-10-10 22:00:00 -06"); + test_timezone_offset_change_one("Sun 1982-03-14 02:59:59 UTC", "Sat 1982-03-13 20:59:59 -06"); + test_timezone_offset_change_one("Sun 1982-03-14 03:00:00 UTC", "Sat 1982-03-13 21:00:00 -06"); + } + + assert_se(set_unset_env("TZ", tz, true) == 0); + tzset(); +} + static int intro(void) { log_info("realtime=" USEC_FMT "\n" "monotonic=" USEC_FMT "\n"