systemd/1225-test-add-tests-for-format_timestamp-and-parse_timest.patch
Jan Macku 2169d2c18c systemd-252-57
Resolves: RHEL-108555,RHEL-108568,RHEL-108576,RHEL-108584,RHEL-108596,RHEL-108598,RHEL-109096,RHEL-109488,RHEL-111065,RHEL-31756,RHEL-50103
2025-09-16 08:59:46 +02:00

445 lines
23 KiB
Diff

From 05b4623cb23c6f083a5bee9769e5cd22d8ff4e16 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Sun, 12 Feb 2023 05:30:49 +0900
Subject: [PATCH] test: add tests for format_timestamp() and parse_timestamp()
with various timezone
(cherry picked from commit 8b51c41fd0796b1299f3b7f2f11eaf4efae8c2db)
Related: RHEL-109488
---
src/test/test-time-util.c | 378 ++++++++++++++++++++++++++++++++++++--
1 file changed, 366 insertions(+), 12 deletions(-)
diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c
index 6b546fb9f5..3fbf7bf3d0 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);
@@ -377,20 +382,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;
- assert_se(streq(FORMAT_TIMESTAMP(x), buf));
+ 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;
+
+ _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);
+ }
}
}
@@ -490,6 +566,219 @@ TEST(format_timestamp_utc) {
test_format_timestamp_utc_one(USEC_INFINITY, 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;
@@ -613,6 +902,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"