llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-libcxx Author: Mark de Wever (mordante) <details> <summary>Changes</summary> Note the implementation of zoned_time& operator=(const local_time<Duration>& lt); is not correct; however the wording cannot be easily implemented. It could be if the object caches the local_time assigned. However this does not seem to intended. The current implementation matches MSVC STL and libstdc++. Implements parts of: - P0355 Extending to chrono Calendars and Time Zones --- Patch is 38.33 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/95026.diff 8 Files Affected: - (modified) libcxx/include/__chrono/zoned_time.h (+21) - (modified) libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp (+10-2) - (added) libcxx/test/std/time/time.zone/time.zone.zonedtime/time.zone.zonedtime.members/assign.local_time.pass.cpp (+245) - (added) libcxx/test/std/time/time.zone/time.zone.zonedtime/time.zone.zonedtime.members/assign.sys_time.pass.cpp (+134) - (added) libcxx/test/std/time/time.zone/time.zone.zonedtime/time.zone.zonedtime.members/get_info.pass.cpp (+49) - (added) libcxx/test/std/time/time.zone/time.zone.zonedtime/time.zone.zonedtime.members/get_local_time.pass.cpp (+131) - (added) libcxx/test/std/time/time.zone/time.zone.zonedtime/time.zone.zonedtime.members/operator_local_time.pass.cpp (+133) - (added) libcxx/test/std/time/time.zone/time.zone.zonedtime/time.zone.zonedtime.members/operator_sys_time.pass.cpp (+123) ``````````diff diff --git a/libcxx/include/__chrono/zoned_time.h b/libcxx/include/__chrono/zoned_time.h index bf8e04b6407ef..e59f527f4d825 100644 --- a/libcxx/include/__chrono/zoned_time.h +++ b/libcxx/include/__chrono/zoned_time.h @@ -146,8 +146,29 @@ class zoned_time { } && is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>>) : zoned_time{__traits::locate_zone(__name), __zt, __c} {} + _LIBCPP_HIDE_FROM_ABI zoned_time& operator=(const sys_time<_Duration>& __tp) { + __tp_ = __tp; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI zoned_time& operator=(const local_time<_Duration>& __tp) { + // TODO TZDB This seems wrong. + // Assigning a non-existant or abiguous time will throw and not satisfy + // the post condition. This seems quite odd; I constructed an object with + // choose::earliest and that choice is not respected. + // what did LEWG do with this. + // MSVC STL and libstdc++ behave the same + __tp_ = __zone_->to_sys(__tp); + return *this; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI operator sys_time<duration>() const { return get_sys_time(); } + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit operator local_time<duration>() const { return get_local_time(); } + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _TimeZonePtr get_time_zone() const { return __zone_; } + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI local_time<duration> get_local_time() const { return __zone_->to_local(__tp_); } [[nodiscard]] _LIBCPP_HIDE_FROM_ABI sys_time<duration> get_sys_time() const { return __tp_; } + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI sys_info get_info() const { return __zone_->get_info(__tp_); } private: _TimeZonePtr __zone_; diff --git a/libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp b/libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp index e8337cb33822e..32a67dc4dc9c4 100644 --- a/libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp +++ b/libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp @@ -81,7 +81,15 @@ void test() { { std::chrono::zoned_time<std::chrono::seconds> zt; - zt.get_time_zone(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} - zt.get_sys_time(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + static_cast<std::chrono::sys_seconds>(zt); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + static_cast<std::chrono::local_seconds>(zt); + + zt.get_time_zone(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + zt.get_local_time(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + zt.get_sys_time(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + zt.get_info(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} } } diff --git a/libcxx/test/std/time/time.zone/time.zone.zonedtime/time.zone.zonedtime.members/assign.local_time.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.zonedtime/time.zone.zonedtime.members/assign.local_time.pass.cpp new file mode 100644 index 0000000000000..e5f2e5d92e71a --- /dev/null +++ b/libcxx/test/std/time/time.zone/time.zone.zonedtime/time.zone.zonedtime.members/assign.local_time.pass.cpp @@ -0,0 +1,245 @@ +//===----------------------------------------------------------------------===// +// +// 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> + +// template<class Duration, class TimeZonePtr = const time_zone*> +// class zoned_time; +// +// zoned_time& operator=(const local_time<Duration>& st); + +// TODO TZDB Investigate the issues in this test, this seems like +// a design issue of the class. +// +// [time.zone.zonedtime.members]/3 +// Effects: After assignment, get_local_time() == lt. +// This assignment has no effect on the return value of get_time_zone(). +// +// The test cases describe the issues. + +#include <cassert> +#include <chrono> +#include <concepts> +#include <type_traits> + +// Tests unique conversions. To make sure the test is does not depend on changes +// in the database it uses a time zone with a fixed offset. +static void test_unique() { + // common_type_t<duration, seconds> -> duration + { + using duration = std::chrono::nanoseconds; + using sys_time_point = std::chrono::sys_time<duration>; + using local_time_point = std::chrono::local_time<duration>; + using zoned_time = std::chrono::zoned_time<duration>; + zoned_time zt{"Etc/GMT+1", sys_time_point{duration{42}}}; + + assert(zt.get_time_zone() == std::chrono::locate_zone("Etc/GMT+1")); + assert(zt.get_sys_time() == sys_time_point{duration{42}}); + assert(zt.get_local_time() == local_time_point{duration{42} - std::chrono::hours{1}}); + + std::same_as<zoned_time&> decltype(auto) _ = zt = local_time_point{duration{99}}; + assert(zt.get_time_zone() == std::chrono::locate_zone("Etc/GMT+1")); + assert(zt.get_sys_time() == sys_time_point{duration{99} + std::chrono::hours{1}}); + assert(zt.get_local_time() == local_time_point{duration{99}}); + } + { + using duration = std::chrono::microseconds; + using sys_time_point = std::chrono::sys_time<duration>; + using local_time_point = std::chrono::local_time<duration>; + using zoned_time = std::chrono::zoned_time<duration>; + zoned_time zt{"Etc/GMT+1", sys_time_point{duration{42}}}; + + assert(zt.get_time_zone() == std::chrono::locate_zone("Etc/GMT+1")); + assert(zt.get_sys_time() == sys_time_point{duration{42}}); + assert(zt.get_local_time() == local_time_point{duration{42} - std::chrono::hours{1}}); + + std::same_as<zoned_time&> decltype(auto) _ = zt = local_time_point{duration{99}}; + assert(zt.get_time_zone() == std::chrono::locate_zone("Etc/GMT+1")); + assert(zt.get_sys_time() == sys_time_point{duration{99} + std::chrono::hours{1}}); + assert(zt.get_local_time() == local_time_point{duration{99}}); + } + { + using duration = std::chrono::milliseconds; + using sys_time_point = std::chrono::sys_time<duration>; + using local_time_point = std::chrono::local_time<duration>; + using zoned_time = std::chrono::zoned_time<duration>; + zoned_time zt{"Etc/GMT+1", sys_time_point{duration{42}}}; + + assert(zt.get_time_zone() == std::chrono::locate_zone("Etc/GMT+1")); + assert(zt.get_sys_time() == sys_time_point{duration{42}}); + assert(zt.get_local_time() == local_time_point{duration{42} - std::chrono::hours{1}}); + + std::same_as<zoned_time&> decltype(auto) _ = zt = local_time_point{duration{99}}; + assert(zt.get_time_zone() == std::chrono::locate_zone("Etc/GMT+1")); + assert(zt.get_sys_time() == sys_time_point{duration{99} + std::chrono::hours{1}}); + assert(zt.get_local_time() == local_time_point{duration{99}}); + } + // common_type_t<seconds, seconds> -> seconds + { + using duration = std::chrono::seconds; + using sys_time_point = std::chrono::sys_time<duration>; + using local_time_point = std::chrono::local_time<duration>; + using zoned_time = std::chrono::zoned_time<duration>; + zoned_time zt{"Etc/GMT+1", sys_time_point{duration{42}}}; + + assert(zt.get_time_zone() == std::chrono::locate_zone("Etc/GMT+1")); + assert(zt.get_sys_time() == sys_time_point{duration{42}}); + assert(zt.get_local_time() == local_time_point{duration{42} - std::chrono::hours{1}}); + + std::same_as<zoned_time&> decltype(auto) _ = zt = local_time_point{duration{99}}; + assert(zt.get_time_zone() == std::chrono::locate_zone("Etc/GMT+1")); + assert(zt.get_sys_time() == sys_time_point{duration{99} + std::chrono::hours{1}}); + assert(zt.get_local_time() == local_time_point{duration{99}}); + } + // common_type_t<duration, seconds> -> seconds + { + using duration = std::chrono::days; + using sys_time_point = std::chrono::sys_time<duration>; + using local_time_point = std::chrono::local_time<duration>; + using zoned_time = std::chrono::zoned_time<duration>; + zoned_time zt{"Etc/GMT+1", sys_time_point{duration{42}}}; + + assert(zt.get_time_zone() == std::chrono::locate_zone("Etc/GMT+1")); + assert(zt.get_sys_time() == std::chrono::sys_seconds{duration{42}}); + assert(zt.get_local_time() == std::chrono::local_seconds{duration{42} - std::chrono::hours{1}}); + + std::same_as<zoned_time&> decltype(auto) _ = zt = local_time_point{duration{99}}; + assert(zt.get_time_zone() == std::chrono::locate_zone("Etc/GMT+1")); + assert(zt.get_sys_time() == std::chrono::sys_seconds{duration{99} + std::chrono::hours{1}}); + assert(zt.get_local_time() == std::chrono::local_seconds{duration{99}}); + } + { + using duration = std::chrono::weeks; + using sys_time_point = std::chrono::sys_time<duration>; + using local_time_point = std::chrono::local_time<duration>; + using zoned_time = std::chrono::zoned_time<duration>; + zoned_time zt{"Etc/GMT+1", sys_time_point{duration{42}}}; + + assert(zt.get_time_zone() == std::chrono::locate_zone("Etc/GMT+1")); + assert(zt.get_sys_time() == std::chrono::sys_seconds{duration{42}}); + assert(zt.get_local_time() == std::chrono::local_seconds{duration{42} - std::chrono::hours{1}}); + + std::same_as<zoned_time&> decltype(auto) _ = zt = local_time_point{duration{99}}; + assert(zt.get_time_zone() == std::chrono::locate_zone("Etc/GMT+1")); + assert(zt.get_sys_time() == std::chrono::sys_seconds{duration{99} + std::chrono::hours{1}}); + assert(zt.get_local_time() == std::chrono::local_seconds{duration{99}}); + } + /* This does not work; due to using __tp_ = __zone_->to_sys(__tp); + * Here the ambiguous/non-existent exception can't stream months and years, + * leading to a compilation error. + { + using duration = std::chrono::months; + using sys_time_point = std::chrono::sys_time<duration>; + using local_time_point = std::chrono::local_time<duration>; + using zoned_time = std::chrono::zoned_time<duration>; + zoned_time zt{"Etc/GMT+1", sys_time_point{duration{42}}}; + + assert(zt.get_time_zone() == std::chrono::locate_zone("Etc/GMT+1")); + assert(zt.get_sys_time() == std::chrono::sys_seconds{duration{42}}); + assert(zt.get_local_time() == std::chrono::local_seconds{duration{42} - std::chrono::hours{1}}); + + std::same_as<zoned_time&> decltype(auto) _ = zt = local_time_point{duration{99}}; + assert(zt.get_time_zone() == std::chrono::locate_zone("Etc/GMT+1")); + assert(zt.get_sys_time() == std::chrono::sys_seconds{duration{99} + std::chrono::hours{1}}); + assert(zt.get_local_time() == std::chrono::local_seconds{duration{99}}); + } */ +} + +// Tests non-existant conversions. +static void test_nonexistent() { + using namespace std::literals::chrono_literals; + + const std::chrono::time_zone* tz = std::chrono::locate_zone("Europe/Berlin"); + + // Z Europe/Berlin 0:53:28 - LMT 1893 Ap + // ... + // 1 DE CE%sT 1980 + // 1 E CE%sT + // + // ... + // R E 1981 ma - Mar lastSu 1u 1 S + // R E 1996 ma - O lastSu 1u 0 - + + // Pick an historic date where it's well known what the time zone rules were. + // This makes it unlikely updates to the database change these rules. + std::chrono::local_time<std::chrono::seconds> time{ + (std::chrono::sys_days{std::chrono::March / 30 / 1986} + 2h + 30min).time_since_epoch()}; + + using duration = std::chrono::seconds; + using sys_time_point = std::chrono::sys_time<duration>; + using local_time_point = std::chrono::local_time<duration>; + using zoned_time = std::chrono::zoned_time<duration>; + zoned_time zt{tz}; + +#ifndef TEST_NOEXCEPT + bool thrown = false; + try { + std::same_as<zoned_time&> decltype(auto) _ = zt = time; + } catch (const std::chrono::nonexistent_local_time&) { + thrown = true; + } + // There is no system type that can represent the current local time. So the + // assertion passes. The current implementation throws an exception too. + assert(zt.get_local_time() != time); + assert(thrown); +#endif +} + +// Tests ambiguous conversions. +static void test_ambiguous() { + using namespace std::literals::chrono_literals; + + const std::chrono::time_zone* tz = std::chrono::locate_zone("Europe/Berlin"); + + // Z Europe/Berlin 0:53:28 - LMT 1893 Ap + // ... + // 1 DE CE%sT 1980 + // 1 E CE%sT + // + // ... + // R E 1981 ma - Mar lastSu 1u 1 S + // R E 1996 ma - O lastSu 1u 0 - + + // Pick an historic date where it's well known what the time zone rules were. + // This makes it unlikely updates to the database change these rules. + std::chrono::local_time<std::chrono::seconds> time{ + (std::chrono::sys_days{std::chrono::September / 28 / 1986} + 2h + 30min).time_since_epoch()}; + + using duration = std::chrono::seconds; + using sys_time_point = std::chrono::sys_time<duration>; + using local_time_point = std::chrono::local_time<duration>; + using zoned_time = std::chrono::zoned_time<duration>; + zoned_time zt{tz}; + +#ifndef TEST_NOEXCEPT + bool thrown = false; + try { + std::same_as<zoned_time&> decltype(auto) _ = zt = time; + } catch (const std::chrono::ambiguous_local_time&) { + thrown = true; + } + // There is no system type that can represent the current local time. So the + // assertion passes. The current implementation throws an exception too. + assert(zt.get_local_time() != time); + assert(thrown); +#endif +} + +int main(int, char**) { + test_unique(); + test_nonexistent(); + test_ambiguous(); + + return 0; +} diff --git a/libcxx/test/std/time/time.zone/time.zone.zonedtime/time.zone.zonedtime.members/assign.sys_time.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.zonedtime/time.zone.zonedtime.members/assign.sys_time.pass.cpp new file mode 100644 index 0000000000000..ab824bce15447 --- /dev/null +++ b/libcxx/test/std/time/time.zone/time.zone.zonedtime/time.zone.zonedtime.members/assign.sys_time.pass.cpp @@ -0,0 +1,134 @@ +//===----------------------------------------------------------------------===// +// +// 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> + +// template<class Duration, class TimeZonePtr = const time_zone*> +// class zoned_time; +// +// zoned_time& operator=(const sys_time<Duration>& st); + +#include <cassert> +#include <chrono> +#include <concepts> +#include <type_traits> + +int main(int, char**) { + { + using duration = std::chrono::nanoseconds; + using time_point = std::chrono::sys_time<duration>; + using zoned_time = std::chrono::zoned_time<duration>; + zoned_time zt{time_point{duration{42}}}; + + assert(zt.get_time_zone() == std::chrono::locate_zone("UTC")); + assert(zt.get_sys_time() == time_point{duration{42}}); + + std::same_as<zoned_time&> decltype(auto) _ = zt = time_point{duration{99}}; + assert(zt.get_time_zone() == std::chrono::locate_zone("UTC")); + assert(zt.get_sys_time() == time_point{duration{99}}); + } + { + using duration = std::chrono::microseconds; + using time_point = std::chrono::sys_time<duration>; + using zoned_time = std::chrono::zoned_time<duration>; + zoned_time zt{time_point{duration{42}}}; + + assert(zt.get_time_zone() == std::chrono::locate_zone("UTC")); + assert(zt.get_sys_time() == time_point{duration{42}}); + + std::same_as<zoned_time&> decltype(auto) _ = zt = time_point{duration{99}}; + assert(zt.get_time_zone() == std::chrono::locate_zone("UTC")); + assert(zt.get_sys_time() == time_point{duration{99}}); + } + { + using duration = std::chrono::milliseconds; + using time_point = std::chrono::sys_time<duration>; + using zoned_time = std::chrono::zoned_time<duration>; + zoned_time zt{time_point{duration{42}}}; + + assert(zt.get_time_zone() == std::chrono::locate_zone("UTC")); + assert(zt.get_sys_time() == time_point{duration{42}}); + + std::same_as<zoned_time&> decltype(auto) _ = zt = time_point{duration{99}}; + assert(zt.get_time_zone() == std::chrono::locate_zone("UTC")); + assert(zt.get_sys_time() == time_point{duration{99}}); + } + { + using duration = std::chrono::seconds; + using time_point = std::chrono::sys_time<duration>; + using zoned_time = std::chrono::zoned_time<duration>; + zoned_time zt{time_point{duration{42}}}; + + assert(zt.get_time_zone() == std::chrono::locate_zone("UTC")); + assert(zt.get_sys_time() == time_point{duration{42}}); + + std::same_as<zoned_time&> decltype(auto) _ = zt = time_point{duration{99}}; + assert(zt.get_time_zone() == std::chrono::locate_zone("UTC")); + assert(zt.get_sys_time() == time_point{duration{99}}); + } + { + using duration = std::chrono::days; + using time_point = std::chrono::sys_time<duration>; + using zoned_time = std::chrono::zoned_time<duration>; + zoned_time zt{time_point{duration{42}}}; + + assert(zt.get_time_zone() == std::chrono::locate_zone("UTC")); + assert(zt.get_sys_time() == time_point{duration{42}}); + + std::same_as<zoned_time&> decltype(auto) _ = zt = time_point{duration{99}}; + assert(zt.get_time_zone() == std::chrono::locate_zone("UTC")); + assert(zt.get_sys_time() == time_point{duration{99}}); + } + { + using duration = std::chrono::weeks; + using time_point = std::chrono::sys_time<duration>; + using zoned_time = std::chrono::zoned_time<duration>; + zoned_time zt{time_point{duration{42}}}; + + assert(zt.get_time_zone() == std::chrono::locate_zone("UTC")); + assert(zt.get_sys_time() == time_point{duration{42}}); + + std::same_as<zoned_time&> decltype(auto) _ = zt = time_point{duration{99}}; + assert(zt.get_time_zone() == std::chrono::locate_zone("UTC")); + assert(zt.get_sys_time() == time_point{duration{99}}); + } + { + using duration = std::chrono::months; + using time_point = std::chrono::sys_time<duration>; + using zoned_time = std::chrono::zoned_time<duration>; + zoned_time zt{time_point{duration{42}}}; + + assert(zt.get_time_zone() == std::chrono::locate_zone("UTC")); + assert(zt.get_sys_time() == time_point{duration{42}}); + + std::same_as<zoned_time&> decltype(auto) _ = zt = time_point{duration{99}}; + assert(zt.get_time_zone() == std::chrono::locate_zone("UTC")); + assert(zt.get_sys_time() == time_point{duration{99}}); + } + { + using duration = std::chrono::years; + using time_point = std::chrono::sys_time<duration>; + using zoned_time = std::chrono::zoned_time<duration>; + zoned_time zt{time_point{duration{42}}}; + + assert(zt.get_time_zone() == std::chrono::locate_zone("UTC")); + assert(zt.get_sys_time() == time_point{duration{42}}); + + std::same_as<zoned_time&> decltype(auto) _ = zt = time_point{duration{99}}; + assert(zt.get_time_zone() == std::chrono::locate_zone("UTC")); + assert(zt.get_sys_time() == time_point{duration{99}}); + } + + return 0; +} diff --git a/libcxx/test/std/time/time.zone/time.zone.zonedtime/time.zone.zonedtime.members/get_info.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.zonedtime/time.zone.zonedtime.members/get_info.pass.cpp new file mode 100644 index 0000000000000..2206692aba616 --- /dev/null +++ b/libcxx/test/std/time/time.zone/time.zone.zonedtime/time.zone.zonedtime.members/get_info.pass.cpp @@ -0,0 +1,49 @@ +//===----------------------------------------------... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/95026 _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits