]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
test: ignore fails when the formatted timezone differs from the current one
authorFrantisek Sumsal <frantisek@sumsal.cz>
Thu, 2 Jul 2026 12:32:36 +0000 (14:32 +0200)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 2 Jul 2026 22:23:30 +0000 (07:23 +0900)
When formatting a timestamp the C API takes into account historical data
from tzdata, so it returns a date strings with a historically-correct
timezone abbreviation. However, tzname[] doesn't do this and it returns
the most recent abbreviation for the given zone.

For example, according to tzdata America/Cancun switched from EST/EDT to
CST/CDT on 1998-08-02:

Zone America/Cancun     -5:47:04 -      LMT     1922 Jan  1  6:00u
                        -6:00   -       CST     1981 Dec 26  2:00
                        -5:00   -       EST     1983 Jan  4  0:00
                        -6:00   Mexico  C%sT    1997 Oct 26  2:00
                        -5:00   Mexico  E%sT    1998 Aug  2  2:00
                        -6:00   Mexico  C%sT    2015 Feb  1  2:00
                        -5:00   -       EST

So, formatting a timestamp from this time will yield a string with the
EDT timezone:

$ TZ=America/Cancun date -d "@902035565"
Sun Aug  2 01:26:05 EDT 1998

But using tzname[] (or strptime %z) shows the most recent data, where
America/Cancun uses EST (and doesn't use DST anymore, hence
tzname[1]=CDT that glibc remembers from the previous zone epoch):

$ TZ=America/Cancun ./tz
{EST, CDT}

This means that when we parse the formatted timestamp back we don't use
the historical timezone data, so we might end up with a different
offset:

TZ=America/Cancun, tzname[0]=EST, tzname[1]=CDT
@902035565603993 → Sun 1998-08-02 01:26:05 EDT → @902039165000000 → Sun 1998-08-02 01:26:05 CDT
src/test/test-time-util.c:452: Assertion failed: Expected "ignore" to be true
Aborted                    (core dumped) build-local/test-time-util

Instead of adding exceptions for every single timezone that switched
between different offsets in the past, let's address this a bit more
generally and skip the check if the parsed timezone doesn't match any of
the current timezones - this still keeps the check that the time
difference in such case is exactly one hour, so its effect should be
limited mostly to DST-related changes.

Resolves: #37684

src/test/test-time-util.c

index 392b06214b0ff435e4013bf13bb1aff29319a8da..1a1370eda6da79ba53fe4b3fb55c18c401ca3b8d 100644 (file)
@@ -433,15 +433,27 @@ static void test_format_timestamp_impl(usec_t x) {
         if (x_sec == y_sec && streq(xx, yy))
                 return; /* Yay! */
 
-        /* When the timezone is built with rearguard being enabled (e.g. old Ubuntu and RHEL), the timezone
-         * Africa/Windhoek may provide time shifted 1 hour from the original. See
-         * https://github.com/systemd/systemd/issues/28472 and https://github.com/systemd/systemd/pull/35471.
-         * Also, the same may happen on MSK timezone (e.g. Europe/Volgograd or Europe/Kirov), or on
-         * Africa/Tripoli (Libya) which switched between CET and EET multiple times historically, causing
-         * certain timestamps to round-trip with a 1h offset. */
+        /* There are two classes of known round-trip failures that produce exactly a 1h offset:
+         *
+         * 1) The formatted abbreviation doesn't match the current timezone's abbreviations - the timestamp
+         *    is from a historical era (e.g. Africa/Tripoli switched between CET and EET multiple times
+         *    historically, America/Cancun switched between EST/EDT and CST/CDT several times in the past,
+         *    etc.), and the round-trip is inherently unreliable on platforms where parse_gmtoff() resolves
+         *    such abbreviations with incorrect offsets.
+         *
+         * 2) Rearguard/vanguard database format differences where the abbreviation matches but the
+         *    offset is still wrong (e.g. Africa/Windhoek, Europe/Kirov, Europe/Volgograd).
+         *
+         * See:
+         *   - https://github.com/systemd/systemd/issues/28472
+         *   - https://github.com/systemd/systemd/pull/35471
+         *   - https://github.com/systemd/systemd/issues/37684
+         */
         bool ignore =
-                (STRPTR_IN_SET(getenv("TZ"), "Africa/Windhoek", "Africa/Tripoli", "Libya") ||
-                 STRPTR_IN_SET(get_tzname(/* dst= */ false), "CAT", "EAT", "MSK", "WET")) &&
+                ((!streq_ptr(tz, get_tzname(/* dst= */ false)) &&
+                  !streq_ptr(tz, get_tzname(/* dst= */ true))) ||
+                 streq_ptr(getenv("TZ"), "Africa/Windhoek") ||
+                 STRPTR_IN_SET(get_tzname(/* dst= */ false), "MSK", "WET")) &&
                 (x_sec > y_sec ? x_sec - y_sec : y_sec - x_sec) == 3600;
 
         log_full(ignore ? LOG_WARNING : LOG_ERR,
@@ -459,16 +471,17 @@ static void test_format_timestamp_loop(void) {
         test_format_timestamp_impl(USEC_TIMESTAMP_FORMATTABLE_MAX-1);
         test_format_timestamp_impl(USEC_TIMESTAMP_FORMATTABLE_MAX);
 
-        /* Two cases which trigger https://github.com/systemd/systemd/issues/28472 */
+        /* Specific timestamps known to cause a 1h round-trip discrepancy with certain timezones:
+         *
+         * Two cases which trigger https://github.com/systemd/systemd/issues/28472. */
         test_format_timestamp_impl(1504938962980066);
         test_format_timestamp_impl(1509482094632752);
-
         /* With tzdata-2025c, the timestamp (randomly?) fails on MSK time zone (e.g. Europe/Volgograd). */
         test_format_timestamp_impl(1414277092997572);
-
-        /* Africa/Tripoli (Libya) switched from CET to EET multiple times in the past, causing a 1h
-         * round-trip discrepancy for historical timestamps. */
+        /* Africa/Tripoli (Libya) switched from CET to EET multiple times in the past. */
         test_format_timestamp_impl(378687574661411);
+        /* America/Cancun switched from EST/EDT to CST/CDT multiple times in the past. */
+        test_format_timestamp_impl(902035565603993);
 
         for (unsigned i = 0; i < TRIAL; i++) {
                 usec_t x;