-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[libc++][TZDB] Implements time_zone::to_sys. #90394
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) ChangesThis implements the throwing overload and the exception classes throw by this overload. Implements parts of:
Patch is 44.82 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/90394.diff 16 Files Affected:
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 1296c536bc882c..386bd967eed7ab 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -264,6 +264,7 @@ set(files
__chrono/convert_to_tm.h
__chrono/day.h
__chrono/duration.h
+ __chrono/exception.h
__chrono/file_clock.h
__chrono/formatter.h
__chrono/hh_mm_ss.h
diff --git a/libcxx/include/__chrono/exception.h b/libcxx/include/__chrono/exception.h
new file mode 100644
index 00000000000000..ca0acecf151ba9
--- /dev/null
+++ b/libcxx/include/__chrono/exception.h
@@ -0,0 +1,128 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html
+
+#ifndef _LIBCPP___CHRONO_EXCEPTION_H
+#define _LIBCPP___CHRONO_EXCEPTION_H
+
+#include <version>
+// Enable the contents of the header only when libc++ was built with experimental features enabled.
+#if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
+
+# include <__availability>
+# include <__chrono/calendar.h>
+# include <__chrono/local_info.h>
+# include <__config>
+# include <__verbose_abort>
+# include <format>
+# include <stdexcept>
+# include <string>
+
+# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+# endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+# if _LIBCPP_STD_VER >= 20
+
+namespace chrono {
+
+class nonexistent_local_time : public runtime_error {
+public:
+ template <class _Duration>
+ _LIBCPP_HIDE_FROM_ABI nonexistent_local_time(const local_time<_Duration>& __time, const local_info& __info)
+ : runtime_error{__create_message(__time, __info)} {
+ // [time.zone.exception.nonexist]/2
+ // Preconditions: i.result == local_info::nonexistent is true.
+ // The value of __info.result is not used.
+ _LIBCPP_ASSERT_PEDANTIC(__info.result == local_info::nonexistent,
+ "creating an nonexistent_local_time from a local_info that is not non-existent");
+ }
+
+ _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI ~nonexistent_local_time() override; // exported as key function
+
+private:
+ template <class _Duration>
+ _LIBCPP_HIDE_FROM_ABI string __create_message(const local_time<_Duration>& __time, const local_info& __info) {
+ return std::format(
+ R"({} is in a gap between
+{} {} and
+{} {} which are both equivalent to
+{} UTC)",
+ __time,
+ local_seconds{__info.first.end.time_since_epoch()} + __info.first.offset,
+ __info.first.abbrev,
+ local_seconds{__info.second.begin.time_since_epoch()} + __info.second.offset,
+ __info.second.abbrev,
+ __info.first.end);
+ }
+};
+
+template <class _Duration>
+_LIBCPP_NORETURN _LIBCPP_HIDE_FROM_ABI void __throw_nonexistent_local_time(
+ [[maybe_unused]] const local_time<_Duration>& __time, [[maybe_unused]] const local_info& __info) {
+# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+ throw nonexistent_local_time(__time, __info);
+# else
+ _LIBCPP_VERBOSE_ABORT("nonexistent_local_time was thrown in -fno-exceptions mode");
+# endif
+}
+
+class ambiguous_local_time : public runtime_error {
+public:
+ template <class _Duration>
+ _LIBCPP_HIDE_FROM_ABI ambiguous_local_time(const local_time<_Duration>& __time, const local_info& __info)
+ : runtime_error{__create_message(__time, __info)} {
+ // [time.zone.exception.ambig]/2
+ // Preconditions: i.result == local_info::ambiguous is true.
+ // The value of __info.result is not used.
+ _LIBCPP_ASSERT_PEDANTIC(__info.result == local_info::ambiguous,
+ "creating an ambiguous_local_time from a local_info that is not ambiguous");
+ }
+
+ _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI ~ambiguous_local_time() override; // exported as key function
+
+private:
+ template <class _Duration>
+ _LIBCPP_HIDE_FROM_ABI string __create_message(const local_time<_Duration>& __time, const local_info& __info) {
+ return std::format(
+ // There are two spaces after the full-stop; this has been verified
+ // in the sources of the Standard.
+ R"({0} is ambiguous. It could be
+{0} {1} == {2} UTC or
+{0} {3} == {4} UTC)",
+ __time,
+ __info.first.abbrev,
+ __time - __info.first.offset,
+ __info.second.abbrev,
+ __time - __info.second.offset);
+ }
+};
+
+template <class _Duration>
+_LIBCPP_NORETURN _LIBCPP_HIDE_FROM_ABI void __throw_ambiguous_local_time(
+ [[maybe_unused]] const local_time<_Duration>& __time, [[maybe_unused]] const local_info& __info) {
+# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+ throw ambiguous_local_time(__time, __info);
+# else
+ _LIBCPP_VERBOSE_ABORT("ambiguous_local_time was thrown in -fno-exceptions mode");
+# endif
+}
+
+} // namespace chrono
+
+# endif // _LIBCPP_STD_VER >= 20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
+
+#endif // _LIBCPP___CHRONO_EXCEPTION_H
diff --git a/libcxx/include/__chrono/time_zone.h b/libcxx/include/__chrono/time_zone.h
index e28c9189c381b4..fbeed6103c49b3 100644
--- a/libcxx/include/__chrono/time_zone.h
+++ b/libcxx/include/__chrono/time_zone.h
@@ -18,6 +18,7 @@
# include <__chrono/calendar.h>
# include <__chrono/duration.h>
+# include <__chrono/exception.h>
# include <__chrono/local_info.h>
# include <__chrono/sys_info.h>
# include <__chrono/system_clock.h>
@@ -70,6 +71,30 @@ class _LIBCPP_AVAILABILITY_TZDB time_zone {
return __get_info(chrono::time_point_cast<seconds>(__time));
}
+ // Since the interface promisses throwing, don't add nodiscard.
+ template <class _Duration>
+ _LIBCPP_HIDE_FROM_ABI sys_time<common_type_t<_Duration, seconds>> to_sys(const local_time<_Duration>& __time) const {
+ local_info __info = get_info(__time);
+ switch (__info.result) {
+ case local_info::unique:
+ return sys_time<common_type_t<_Duration, seconds>>{__time.time_since_epoch() - __info.first.offset};
+
+ case local_info::nonexistent:
+ chrono::__throw_nonexistent_local_time(__time, __info);
+
+ case local_info::ambiguous:
+ chrono::__throw_ambiguous_local_time(__time, __info);
+ }
+
+ // TODO TZDB The Standard does not specify anything in these cases.
+ _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
+ __info.result != -1, "cannot convert the local time; it would be before the minimum system clock value");
+ _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
+ __info.result != -2, "cannot convert the local time; it would be after the maximum system clock value");
+
+ return {};
+ }
+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI const __impl& __implementation() const noexcept { return *__impl_; }
private:
diff --git a/libcxx/include/chrono b/libcxx/include/chrono
index 4d8398af1a108f..4b0ea938710bdd 100644
--- a/libcxx/include/chrono
+++ b/libcxx/include/chrono
@@ -724,6 +724,10 @@ const time_zone* current_zone()
const tzdb& reload_tzdb(); // C++20
string remote_version(); // C++20
+// [time.zone.exception], exception classes
+class nonexistent_local_time; // C++20
+class ambiguous_local_time; // C++20
+
// [time.zone.info], information classes
struct sys_info { // C++20
sys_seconds begin;
@@ -766,6 +770,10 @@ class time_zone {
template<class Duration>
local_info get_info(const local_time<Duration>& tp) const;
+
+ template<class Duration>
+ sys_time<common_type_t<Duration, seconds>>
+ to_sys(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
@@ -916,6 +924,7 @@ constexpr chrono::year operator ""y(unsigned lo
# include <__chrono/calendar.h>
# include <__chrono/day.h>
# include <__chrono/hh_mm_ss.h>
+# include <__chrono/exception.h>
# include <__chrono/literals.h>
# include <__chrono/local_info.h>
# include <__chrono/month.h>
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 8727ab88f16c0a..1f1005ed09b67c 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1112,6 +1112,7 @@ module std_private_chrono_duration [system] {
header "__chrono/duration.h"
export std_private_type_traits_is_convertible
}
+module std_private_chrono_exception [system] { header "__chrono/exception.h" }
module std_private_chrono_file_clock [system] { header "__chrono/file_clock.h" }
module std_private_chrono_formatter [system] {
header "__chrono/formatter.h"
diff --git a/libcxx/modules/std/chrono.inc b/libcxx/modules/std/chrono.inc
index 1265e21dc54ef6..38e3c4184521b7 100644
--- a/libcxx/modules/std/chrono.inc
+++ b/libcxx/modules/std/chrono.inc
@@ -208,11 +208,9 @@ export namespace std {
using std::chrono::reload_tzdb;
using std::chrono::remote_version;
-# if 0
// [time.zone.exception], exception classes
using std::chrono::ambiguous_local_time;
using std::chrono::nonexistent_local_time;
-# endif // if 0
// [time.zone.info], information classes
using std::chrono::local_info;
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 8b28d1b8918955..0a8e11767ec46a 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -339,6 +339,9 @@ if (LIBCXX_ENABLE_LOCALIZATION AND LIBCXX_ENABLE_FILESYSTEM AND LIBCXX_ENABLE_TI
include/tzdb/types_private.h
include/tzdb/tzdb_list_private.h
include/tzdb/tzdb_private.h
+ # TODO TZDB The exception could be moved in chrono once the TZDB library
+ # is no longer experimental.
+ chrono_exception.cpp
time_zone.cpp
tzdb.cpp
tzdb_list.cpp
diff --git a/libcxx/src/chrono_exception.cpp b/libcxx/src/chrono_exception.cpp
new file mode 100644
index 00000000000000..8aeafdeecd69b9
--- /dev/null
+++ b/libcxx/src/chrono_exception.cpp
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <chrono>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace chrono {
+
+_LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI nonexistent_local_time::~nonexistent_local_time() = default;
+_LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI ambiguous_local_time::~ambiguous_local_time() = default;
+
+} // namespace chrono
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.ambig/assert.ctor.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.ambig/assert.ctor.pass.cpp
new file mode 100644
index 00000000000000..73e6bf2846f0e0
--- /dev/null
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.ambig/assert.ctor.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// REQUIRES: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// XFAIL: libcpp-has-no-experimental-tzdb
+
+// <chrono>
+
+// class ambiguous_local_time
+//
+// template<class Duration>
+// ambiguous_local_time(const local_time<Duration>& tp, const local_info& i);
+
+#include <chrono>
+
+#include "check_assertion.h"
+
+// [time.zone.exception.ambig]/2
+// Preconditions: i.result == local_info::ambiguous is true.
+int main(int, char**) {
+ TEST_LIBCPP_ASSERT_FAILURE(
+ (std::chrono::ambiguous_local_time{
+ std::chrono::local_seconds{},
+ std::chrono::local_info{-1, // this is not one of the "named" result values
+ std::chrono::sys_info{},
+ std::chrono::sys_info{}}}),
+ "creating an ambiguous_local_time from a local_info that is not ambiguous");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ (std::chrono::ambiguous_local_time{
+ std::chrono::local_seconds{},
+ std::chrono::local_info{std::chrono::local_info::unique, std::chrono::sys_info{}, std::chrono::sys_info{}}}),
+ "creating an ambiguous_local_time from a local_info that is not ambiguous");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ (std::chrono::ambiguous_local_time{
+ std::chrono::local_seconds{},
+ std::chrono::local_info{
+ std::chrono::local_info::nonexistent, std::chrono::sys_info{}, std::chrono::sys_info{}}}),
+ "creating an ambiguous_local_time from a local_info that is not ambiguous");
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.nonexist/assert.ctor.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.nonexist/assert.ctor.pass.cpp
new file mode 100644
index 00000000000000..fdd9f79958f980
--- /dev/null
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.exception/time.zone.exception.nonexist/assert.ctor.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// REQUIRES: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// XFAIL: libcpp-has-no-experimental-tzdb
+
+// <chrono>
+
+// class nonexistent_local_time
+//
+// template<class Duration>
+// nonexistent_local_time(const local_time<Duration>& tp, const local_info& i);
+
+#include <chrono>
+
+#include "check_assertion.h"
+
+// [time.zone.exception.nonexist]/2
+// Preconditions: i.result == local_info::nonexistent is true.
+int main(int, char**) {
+ TEST_LIBCPP_ASSERT_FAILURE(
+ (std::chrono::nonexistent_local_time{
+ std::chrono::local_seconds{},
+ std::chrono::local_info{-1, // this is not one of the "named" result values
+ std::chrono::sys_info{},
+ std::chrono::sys_info{}}}),
+ "creating an nonexistent_local_time from a local_info that is not non-existent");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ (std::chrono::nonexistent_local_time{
+ std::chrono::local_seconds{},
+ std::chrono::local_info{std::chrono::local_info::unique, std::chrono::sys_info{}, std::chrono::sys_info{}}}),
+ "creating an nonexistent_local_time from a local_info that is not non-existent");
+
+ TEST_LIBCPP_ASSERT_FAILURE(
+ (std::chrono::nonexistent_local_time{
+ std::chrono::local_seconds{},
+ std::chrono::local_info{
+ std::chrono::local_info::ambiguous, std::chrono::sys_info{}, std::chrono::sys_info{}}}),
+ "creating an nonexistent_local_time from a local_info that is not non-existent");
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_sys.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_sys.pass.cpp
new file mode 100644
index 00000000000000..3a2ff00088676b
--- /dev/null
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.timezone/time.zone.members/assert.to_sys.pass.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// REQUIRES: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// XFAIL: libcpp-has-no-experimental-tzdb
+
+// <chrono>
+
+// template <class _Duration>
+// sys_time<common_type_t<Duration, seconds>>
+// to_sys(const local_time<Duration>& tp) const;
+
+#include <chrono>
+
+#include "check_assertion.h"
+
+// Tests values that cannot be converted. To make sure the test is does not depend on changes
+// in the database it uses a time zone with a fixed offset.
+int main(int, char**) {
+ TEST_LIBCPP_ASSERT_FAILURE(std::chrono::locate_zone("Etc/GMT-1")->to_sys(std::chrono::local_seconds::min()),
+ "cannot convert the local time; it would be before the minimum system clock value");
+
+ // TODO TZDB look why std::chrono::local_seconds::max() fails
+ TEST_LIBCPP_ASSERT_FAILURE(
+ std::chrono::locate_zone("Etc/GMT+1")->to_sys(std::chrono::local_seconds::max() - std::chrono::seconds(1)),
+ "cannot convert the local time; it would be after the maximum system clock value");
+
+ return 0;
+}
diff --git a/libcxx/test/std/time/time.zone/time.zone.exception/time.zone.exception.ambig/ctor.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.exception/time.zone.exception.ambig/ctor.pass.cpp
new file mode 100644
index 00000000000000..ae98cb2a17c183
--- /dev/null
+++ b/libcxx/test/std/time/time.zone/time.zone.exception/time.zone.exception.ambig/ctor.pass.cpp
@@ -0,0 +1,169 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// XFAIL: libcpp-has-no-experimental-tzdb
+
+// <chrono>
+
+// class ambiguous_local_time
+//
+// template<class Duration>
+// ambiguous_local_time(const local_time<Duration>& tp, const local_info& i);
+
+#include <chrono>
+#include <string_view>
+
+#include "assert_macros.h"
+#include "concat_macros.h"
+
+template <class Duration>
+static void
+test(const std::chrono::local_time<Duration>& tp, const std::chrono::local_info& i, std::string_view expected) {
+ std::chrono::ambiguous_local_time exception{tp, i};
+ std::string_view result = exception.what();
+ TEST_REQUIRE(result == expected,
+ TEST_WRITE_CONCATENATED("Expected output\n", expected, "\n\nActual output\n", result, '\n'));
+}
+
+// The constructor constructs the runtime_error base class with a specific
+// message. This implicitly tests what() too, since that is inherited from
+// runtime_error there is no separate test for what().
+int main(int, char**) {
+ using namespace std::literals::chrono_literals;
+
+ // There is no requirement on the ordering of PREV and NEXT so an "invalid"
+ // overlap is allowed. All tests with negative dates use the same order as
+ // positive tests.
+
+ test(std::chrono::local_time<std::chrono::nanoseconds>{-1ns},
+ std::chrono::local_info{
+ std::chrono::local_info::ambiguous,
+ std::chrono::sys_info{
+ ...
[truncated]
|
e72a966
to
d7b4271
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM w/ comments applied (in particular the missing tests).
...t/std/time/time.zone/time.zone.exception/time.zone.exception.nonexist/types.compile.pass.cpp
Outdated
Show resolved
Hide resolved
6939905
to
a6b280e
Compare
77dc1f4
to
89152f3
Compare
This implements the throwing overload and the exception classes throw by this overload. Implements parts of: - P0355 Extending chrono to Calendars and Time Zones
89152f3
to
9a052b4
Compare
Hi, we started to see libcxx tests failures on our Windows x64 msvc bots when running tests for
Link to the failed bot: https://ci.chromium.org/ui/p/fuchsia/builders/toolchain.ci/clang-windows-x64/b8745567626897665713/overview Could you take a look please? |
@zeroomega Some tests contain the line I don't have access to a Windows system. Could you test whether adding that line to the tests solves the issue? |
I did some test. I added I am not sure why this issue only happens when running test on I don't think adding |
This moves the files to libcxx/src/experimental/ as discussed in llvm#90394. Fixes: llvm#94902
This moves the files to libcxx/src/experimental/ as discussed in llvm#90394. Fixes: llvm#94902
This moves the files to libcxx/src/experimental/ as discussed in llvm#90394. Fixes: llvm#94902
This reverts commit 77116bd.
This implements the throwing overload and the exception classes throw by this overload.
Implements parts of: