-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[libc++][TZDB] Implements time_zone get_info(local_time). #89537
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-libcxx Author: Mark de Wever (mordante) ChangesImplements parts of:
Patch is 63.94 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/89537.diff 5 Files Affected:
diff --git a/libcxx/include/__chrono/time_zone.h b/libcxx/include/__chrono/time_zone.h
index 799602c1cdbaf0..3ea03683ccc187 100644
--- a/libcxx/include/__chrono/time_zone.h
+++ b/libcxx/include/__chrono/time_zone.h
@@ -17,6 +17,7 @@
#if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
# include <__chrono/duration.h>
+# include <__chrono/local_info.h>
# include <__chrono/sys_info.h>
# include <__chrono/system_clock.h>
# include <__compare/strong_order.h>
@@ -63,12 +64,18 @@ class _LIBCPP_AVAILABILITY_TZDB time_zone {
return __get_info(chrono::time_point_cast<seconds>(__time));
}
+ template <class _Duration>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI local_info get_info(const local_time<_Duration>& __time) const {
+ return __get_info(chrono::time_point_cast<seconds>(__time));
+ }
+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI const __impl& __implementation() const noexcept { return *__impl_; }
private:
[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string_view __name() const noexcept;
[[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI sys_info __get_info(sys_seconds __time) const;
+ [[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI local_info __get_info(local_seconds __time) const;
unique_ptr<__impl> __impl_;
};
diff --git a/libcxx/include/chrono b/libcxx/include/chrono
index 96a3e92faa81f2..4d8398af1a108f 100644
--- a/libcxx/include/chrono
+++ b/libcxx/include/chrono
@@ -763,6 +763,9 @@ class time_zone {
template<class Duration>
sys_info get_info(const sys_time<Duration>& st) const;
+
+ template<class Duration>
+ local_info get_info(const local_time<Duration>& tp) const;
};
bool operator==(const time_zone& x, const time_zone& y) noexcept; // C++20
strong_ordering operator<=>(const time_zone& x, const time_zone& y) noexcept; // C++20
diff --git a/libcxx/src/time_zone.cpp b/libcxx/src/time_zone.cpp
index 928f3d2855e456..24c22859080e10 100644
--- a/libcxx/src/time_zone.cpp
+++ b/libcxx/src/time_zone.cpp
@@ -34,6 +34,7 @@
#include <chrono>
#include <expected>
#include <map>
+#include <numeric>
#include <ranges>
#include "include/tzdb/time_zone_private.h"
@@ -903,6 +904,180 @@ time_zone::__get_info(sys_seconds __time) const {
std::__throw_runtime_error("tzdb: corrupt db");
}
+enum class __position {
+ __beginning,
+ __middle,
+ __end,
+};
+
+// Determines the position of "__time" inside "__info".
+//
+// The code picks an arbitrary value to determine the "middle"
+// - Every time that is more than the threshold from a boundary, or
+// - Every value that is at the boundary sys_seconds::min() or
+// sys_seconds::max().
+//
+// If not in the middle, it returns __beginning or __end.
+[[nodiscard]] static __position __get_position(sys_seconds __time, const sys_info __info) {
+ _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
+ __time >= __info.begin && __time < __info.end, "A value outside the range's position can't be determined.");
+
+ using _Tp = sys_seconds::rep;
+ // Africa/Freetown has a 4 day "zone"
+ // Africa/Freetown Fri Sep 1 00:59:59 1939 UT = Thu Aug 31 23:59:59 1939 -01 isdst=0 gmtoff=-3600
+ // Africa/Freetown Fri Sep 1 01:00:00 1939 UT = Fri Sep 1 00:20:00 1939 -0040 isdst=1 gmtoff=-2400
+ // Africa/Freetown Tue Sep 5 00:39:59 1939 UT = Mon Sep 4 23:59:59 1939 -0040 isdst=1 gmtoff=-2400
+ // Africa/Freetown Tue Sep 5 00:40:00 1939 UT = Mon Sep 4 23:40:00 1939 -01 isdst=0 gmtoff=-3600
+ //
+ // Originally used a one week threshold, but due to this switched to 1 day.
+ // This seems to work in practice.
+ //
+ // TODO TZDB Evaluate the proper threshold.
+ constexpr _Tp __threshold = 24 * 3600;
+
+ _Tp __upper = std::__add_sat(__info.begin.time_since_epoch().count(), __threshold);
+ if (__time >= __info.begin && __time.time_since_epoch().count() < __upper)
+ return __info.begin != sys_seconds::min() ? __position::__beginning : __position::__middle;
+
+ _Tp __lower = std::__sub_sat(__info.end.time_since_epoch().count(), __threshold);
+ if (__time < __info.end && __time.time_since_epoch().count() >= __lower)
+ return __info.end != sys_seconds::max() ? __position::__end : __position::__middle;
+
+ return __position::__middle;
+}
+
+[[nodiscard]] static local_info
+__get_info(local_seconds __local_time, const sys_info& __first, const sys_info& __second) {
+ std::chrono::local_seconds __end_first{__first.end.time_since_epoch() + __first.offset};
+ std::chrono::local_seconds __begin_second{__second.begin.time_since_epoch() + __second.offset};
+
+ if (__local_time < __end_first) {
+ if (__local_time >= __begin_second)
+ // |--------|
+ // |------|
+ // ^
+ return {local_info::ambiguous, __first, __second};
+
+ // |--------|
+ // |------|
+ // ^
+ return {local_info::unique, __first, sys_info{}};
+ }
+
+ if (__local_time < __begin_second)
+ // |--------|
+ // |------|
+ // ^
+ return {local_info::nonexistent, __first, __second};
+
+ // |--------|
+ // |------|
+ // ^
+ return {local_info::unique, __second, sys_info{}};
+}
+
+[[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI local_info
+time_zone::__get_info(local_seconds __local_time) const {
+ seconds __local_seconds = __local_time.time_since_epoch();
+
+ /* An example of a typical year with a DST switch displayed in local time.
+ *
+ * At the first of April the time goes forward one hour. This means the
+ * time marked with ~~ is not a valid local time. This is represented by the
+ * nonexistent value in local_info.result.
+ *
+ * At the first of November the time goes backward one hour. This means the
+ * time marked with ^^ happens twice. This is represented by the ambiguous
+ * value in local_info.result.
+ *
+ * 2020.11.01 2021.04.01 2021.11.01
+ * offset +05 offset +05 offset +05
+ * save 0s save 1h save 0s
+ * |-------------W----------|
+ * |----------W--------------|
+ * |-------------
+ * ~~ ^^
+ *
+ * These shifts can happen due to changes in the current time zone for a
+ * location. For example, Indian/Kerguelen switched only once. In 1950 from an
+ * offset of 0 hours to an offset of +05 hours.
+ *
+ * During all these shifts the UTC time will have not gaps.
+ */
+
+ // The code needs to determine the system time for the local time. There is no
+ // information available. Assume the offset between system time and local time
+ // is 0s. This gives an initial estimate.
+ sys_seconds __guess{__local_seconds};
+ sys_info __info = __get_info(__guess);
+
+ // At this point the offset can be used to determine an estimate for the local
+ // time. Before doing the determine the offset validate whether the local time
+ // is the range [chrono::local_seconds::min(), chrono::local_seconds::max()).
+ if (__local_seconds < 0s && __info.offset > 0s)
+ if (__local_seconds - chrono::local_seconds::min().time_since_epoch() < __info.offset)
+ return {-1, __info, {}};
+
+ if (__local_seconds > 0s && __info.offset < 0s)
+ if (chrono::local_seconds::max().time_since_epoch() - __local_seconds < -__info.offset)
+ return {-2, __info, {}};
+
+ // Based on the information in the sys_info found the local time can be
+ // converted to a system time. This resulting time can be in the following
+ // locations of the sys_info found:
+ //
+ // |----------W--------------|
+ // 1 2 3 4 5
+ //
+ // 1. The estimate is before the returned sys_info object.
+ // The result is either non-existent or unique in the previous sys_info.
+ // 2. The estimate is in the beginning of the returned sys_info object.
+ // The result is either unique or ambiguous with the previous sys_info.
+ // 3. The estimate is in the "middle" of the returned sys_info.
+ // The result is unique.
+ // 4. The result is at the end of the returned sys_info object.
+ // The result is either unique or ambiguous with the next sys_info.
+ // 5. The estimate is after the returned sys_info object.
+ // The result is either non-existent or unique in the next sys_info.
+ //
+ // There is no specification where the "middle" starts. Similar issues can
+ // happen when sys_info objects are "short", then "unique in the next" could
+ // become "ambiguous in the next and the one following". Theoretically there
+ // is the option of the following time-line
+ //
+ // |------------|
+ // |----|
+ // |-----------------|
+ //
+ // However the local_info object only has 2 sys_info objects, so this option
+ // is not tested.
+ //
+ // The positions 2, 3, or 4 are determined by __get_position. This function
+ // also contains the definition of "middle".
+
+ sys_seconds __sys_time{__local_seconds - __info.offset};
+ if (__sys_time < __info.begin)
+ // Case 1 before __info
+ return chrono::__get_info(__local_time, __get_info(__info.begin - 1s), __info);
+
+ if (__sys_time >= __info.end)
+ // Case 5 after __info
+ return chrono::__get_info(__local_time, __info, __get_info(__info.end));
+
+ switch (__get_position(__sys_time, __info)) {
+ case __position::__beginning: // Case 2
+ return chrono::__get_info(__local_time, __get_info(__info.begin - 1s), __info);
+
+ case __position::__middle: // Case 3
+ return {local_info::unique, __info, {}};
+
+ case __position::__end: // Case 4
+ return chrono::__get_info(__local_time, __info, __get_info(__info.end));
+ }
+
+ std::__libcpp_unreachable();
+}
+
} // namespace chrono
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.verify.cpp b/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.verify.cpp
index a5ce5d16581306..fea1e4417cc12c 100644
--- a/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.verify.cpp
+++ b/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.verify.cpp
@@ -48,8 +48,10 @@ void test() {
{
std::chrono::sys_seconds s{};
+ std::chrono::local_seconds l{};
tz.name(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
tz.get_info(s); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ tz.get_info(l); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
operator==(tz, tz); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
operator<=>(tz, tz); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
}
diff --git a/libcxx/test/std/time/time.zone/time.zone.timezone/time.zone.members/get_info.local_time.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.timezone/time.zone.members/get_info.local_time.pass.cpp
new file mode 100644
index 00000000000000..6dc15974c44843
--- /dev/null
+++ b/libcxx/test/std/time/time.zone/time.zone.timezone/time.zone.members/get_info.local_time.pass.cpp
@@ -0,0 +1,1302 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
+
+// XFAIL: libcpp-has-no-experimental-tzdb
+// XFAIL: availability-tzdb-missing
+
+// <chrono>
+
+// class time_zone;
+
+// template <class _Duration>
+// local_info get_info(const local_time<_Duration>& time) const;
+
+// This test uses the system provided database. This makes the test portable,
+// but may cause failures when the database information changes. Historic data
+// may change if new facts are uncovered, future data may change when regions
+// change their time zone or daylight saving time. Most tests will not look in
+// the future to attempt to avoid issues. All tests list the data on which they
+// are based, this makes debugging easier upon failure; including to see whether
+// the provided data has not been changed.
+//
+// The first part of the test is manually crafted, the second part compares the
+// transitions for all time zones in the database.
+
+#include <algorithm>
+#include <cassert>
+#include <chrono>
+#include <format>
+
+#include "test_macros.h"
+#include "assert_macros.h"
+#include "concat_macros.h"
+
+// The year range to validate. The dates used in practice are expected to be
+// inside the tested range.
+constexpr std::chrono::year first{1800};
+constexpr std::chrono::year last{2100};
+
+/***** ***** HELPERS ***** *****/
+
+[[nodiscard]] static std::chrono::sys_seconds to_sys_seconds(
+ std::chrono::year year,
+ std::chrono::month month,
+ std::chrono::day day,
+ std::chrono::hours h = std::chrono::hours(0),
+ std::chrono::minutes m = std::chrono::minutes{0},
+ std::chrono::seconds s = std::chrono::seconds{0}) {
+ std::chrono::year_month_day result{year, month, day};
+
+ return std::chrono::time_point_cast<std::chrono::seconds>(static_cast<std::chrono::sys_days>(result)) + h + m + s;
+}
+
+[[nodiscard]] static std::chrono::local_seconds to_local_seconds(
+ std::chrono::year year,
+ std::chrono::month month,
+ std::chrono::day day,
+ std::chrono::hours h = std::chrono::hours(0),
+ std::chrono::minutes m = std::chrono::minutes{0},
+ std::chrono::seconds s = std::chrono::seconds{0}) {
+ std::chrono::year_month_day result{year, month, day};
+
+ return std::chrono::time_point_cast<std::chrono::seconds>(static_cast<std::chrono::local_days>(result)) + h + m + s;
+}
+
+static void assert_equal(const std::chrono::sys_info& lhs, const std::chrono::sys_info& rhs) {
+ TEST_REQUIRE(lhs.begin == rhs.begin,
+ TEST_WRITE_CONCATENATED("\nBegin:\nExpected output ", lhs.begin, "\nActual output ", rhs.begin, '\n'));
+ TEST_REQUIRE(lhs.end == rhs.end,
+ TEST_WRITE_CONCATENATED("\nEnd:\nExpected output ", lhs.end, "\nActual output ", rhs.end, '\n'));
+ TEST_REQUIRE(
+ lhs.offset == rhs.offset,
+ TEST_WRITE_CONCATENATED("\nOffset:\nExpected output ", lhs.offset, "\nActual output ", rhs.offset, '\n'));
+ TEST_REQUIRE(lhs.save == rhs.save,
+ TEST_WRITE_CONCATENATED("\nSave:\nExpected output ", lhs.save, "\nActual output ", rhs.save, '\n'));
+ TEST_REQUIRE(
+ lhs.abbrev == rhs.abbrev,
+ TEST_WRITE_CONCATENATED("\nAbbrev:\nExpected output ", lhs.abbrev, "\nActual output ", rhs.abbrev, '\n'));
+}
+
+static void assert_equal(const std::chrono::local_info& lhs, const std::chrono::local_info& rhs) {
+ TEST_REQUIRE(
+ lhs.result == rhs.result,
+ TEST_WRITE_CONCATENATED("\nResult:\nExpected output ", lhs.result, "\nActual output ", rhs.result, '\n'));
+
+ assert_equal(lhs.first, rhs.first);
+ assert_equal(lhs.second, rhs.second);
+}
+
+/***** ***** TESTS ***** *****/
+
+static void test_gmt() {
+ // Simple zone always valid, no rule entries, lookup using a link.
+ // L Etc/GMT GMT
+ // Z Etc/GMT 0 - GMT
+
+ using namespace std::literals::chrono_literals;
+ const std::chrono::time_zone* tz = std::chrono::locate_zone("GMT");
+
+ assert_equal(
+ std::chrono::local_info(
+ std::chrono::local_info::unique,
+ std::chrono::sys_info(std::chrono::sys_seconds::min(), std::chrono::sys_seconds::max(), 0s, 0min, "GMT"),
+ std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
+ tz->get_info(std::chrono::local_seconds::min()));
+}
+
+static void test_local_time_out_of_range() {
+ // Fixed positive offset
+ // Etc/GMT-1 1 - +01
+
+ using namespace std::literals::chrono_literals;
+ { // lower bound
+ const std::chrono::time_zone* tz = std::chrono::locate_zone("Etc/GMT-1");
+
+ assert_equal(
+ std::chrono::local_info(
+ -1,
+ std::chrono::sys_info(std::chrono::sys_seconds::min(), std::chrono::sys_seconds::max(), 1h, 0min, "+01"),
+ std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
+ tz->get_info(std::chrono::local_seconds::min()));
+
+ assert_equal(
+ std::chrono::local_info(
+ -1,
+ std::chrono::sys_info(std::chrono::sys_seconds::min(), std::chrono::sys_seconds::max(), 1h, 0min, "+01"),
+ std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
+ tz->get_info(std::chrono::local_seconds::min() + 59min + 59s));
+
+ assert_equal(
+ std::chrono::local_info(
+ std::chrono::local_info::unique,
+ std::chrono::sys_info(std::chrono::sys_seconds::min(), std::chrono::sys_seconds::max(), 1h, 0min, "+01"),
+ std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
+ tz->get_info(std::chrono::local_seconds::min() + 1h));
+ }
+
+ { // upper bound
+ const std::chrono::time_zone* tz = std::chrono::locate_zone("Etc/GMT+1");
+
+ assert_equal(
+ std::chrono::local_info(
+ -2,
+ std::chrono::sys_info(std::chrono::sys_seconds::min(), std::chrono::sys_seconds::max(), -1h, 0min, "-01"),
+ std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
+ tz->get_info(std::chrono::local_seconds::max() - 1s));
+
+ assert_equal(
+ std::chrono::local_info(
+ std::chrono::local_info::unique,
+ std::chrono::sys_info(std::chrono::sys_seconds::min(), std::chrono::sys_seconds::max(), -1h, 0min, "-01"),
+ std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
+ tz->get_info(std::chrono::local_seconds::max() - 1h - 1s));
+ }
+}
+
+static void test_indian_kerguelen() {
+ // One change, no rules, no dst changes.
+
+ // Z Indian/Kerguelen 0 - -00 1950
+ // 5 - +05
+
+ using namespace std::literals::chrono_literals;
+ const std::chrono::time_zone* tz = std::chrono::locate_zone("Indian/Kerguelen");
+
+ assert_equal(
+ std::chrono::local_info(
+ std::chrono::local_info::unique,
+ std::chrono::sys_info(
+ std::chrono::sys_seconds::min(), to_sys_seconds(1950y, std::chrono::January, 1d), 0s, 0min, "-00"),
+ std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
+ tz->get_info(std::chrono::local_seconds::min()));
+
+ assert_equal(
+ std::chrono::local_info(
+ std::chrono::local_info::nonexistent,
+ std::chrono::sys_info(
+ std::chrono::sys_seconds::min(), to_sys_seconds(1950y, std::chrono::January, 1d), 0s, 0min, "-00"),
+ std::chrono::sys_info(
+ to_sys_seconds(1950y, std::chrono::January, 1d), std::chrono::sys_seconds::max(), 5h, 0min, "+05")),
+ tz->get_info(to_local_seconds(1950y, std::chrono::January, 1d)));
+
+ assert_equal(
+ std::chrono::local_info(
+ std::chrono::local_info::unique,
+ std::chrono::sys_info(
+ to_sys_seconds(1950y, std::chrono::January, 1d), std::chrono::sys_seconds::max(), 5h, 0min, "+05"),
+ std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
+ tz->get_info(to_local_seconds(1950y, std::chrono::January, 1d, 5h)));
+
+ assert_equal(
+ std::chrono::local_info(
+ std::chrono::local_info::unique,
+ std::chrono::sys_info(
+ to_sys_seconds(1950y, std::chrono::January, 1d), std::chrono::sys_seconds::max(), 5h, 0min, "+05"),
+ std::chrono::sys_info(std::chrono::sys_seconds(0s), std::chrono::sys_seconds(0s), 0s, 0min, "")),
+ tz->get_info(std::chrono::local_seconds::max() - 1s));
+}
+
+static void test_antarctica_rothera() {
+ // One change, no rules, no dst changes
+
+ // Z Antarctica/Rothera ...
[truncated]
|
4838909
to
17f0550
Compare
827d11e
to
6939905
Compare
Implements parts of: - P0355 Extending to Calendars and Time Zones
6939905
to
a6b280e
Compare
We started to see flakiness in test
Failed build task: https://ci.chromium.org/ui/p/fuchsia/builders/toolchain.ci/clang-linux-x64/b8745592240286102897/overview This test fails about 1 in 3 runs, randomly after this PR was landed. |
We got the same test failure on Amrv7 Win-to-Linux cross builder
The target system is Ubuntu 18.04.6 LTS (armv7l). |
@fmayer do you know the reason why it hangs? |
I don't, revert SGTM |
@fmayer Would you like to revert to fix the bot? Seems unsupported didn't work anyway. |
Which version of the timezone database is installed on this system? I also didn't get a blame e-mail for this build failure? Are the e-mails enabled? |
The test is not the fastest, so enabling sanitizer might trigger a time-out. This feature is still experimental in libc++ and I've not looked at performance improvements. (I know there are several improvements that can be done, but I first want working code and benchmarks.) |
That's why revert should be the default action here. |
It can be the case, it's flaky with asan as well https://lab.llvm.org/buildbot/#/builders/239/builds/7415
If we confident that this is just timeout, we need to split or speedup this test. Long tests also a problem, less serious, but still. |
yes, but I was never able to reproduce it. Our bots do it reliably, but for whatever reason, I wasn't able to, even using docker and following our bot recipe for building clang. |
agreed |
yes, should be enabled. |
Thanks for the information. I also haven't been able to reproduce it.
Thanks that's quite recent, I will test with this version locally.
That's odd I can't find a message, maybe it got lost. For a similar test I have disable testing on |
Do you mean but anyway, this test gets unsupported for any cross compiled tests there because of |
I looked into the test flakiness introduced by this PR and filed the bug: #95612 . TL;DR is the test results depends on the host's timezone database (which is located at |
I'm also seeing it fail, on Gentoo with timezone-data 2024a-r1.
FWICS it's failing on |
I really don't see how this test can be flaky. However it seems I underestimated how volatile the time zone database is and maybe we indeed should test with a fixed version. I'll discuss that with @ldionne. For now I created #95659 which marks the test as unsupported. If this passes our CI I'll land it. |
If you tell me how I could try giving you more info. However, all the previous experiences prove, relying on timezone-data is extremely flaky, especially that these things change all the time. |
Thanks for the offer! |
Implements parts of: