mirror of
https://github.com/intel/llvm.git
synced 2026-01-20 10:58:11 +08:00
[libc++][TZDB] Implements time_zone::to_sys. (#90901)
This implements the overload with the choose argument and adds this enum. Implements parts of: - P0355 Extending chrono to Calendars and Time Zones
This commit is contained in:
@@ -42,6 +42,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
namespace chrono {
|
||||
|
||||
enum class choose { earliest, latest };
|
||||
|
||||
class _LIBCPP_AVAILABILITY_TZDB time_zone {
|
||||
_LIBCPP_HIDE_FROM_ABI time_zone() = default;
|
||||
|
||||
@@ -97,6 +99,36 @@ public:
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class _Duration>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI sys_time<common_type_t<_Duration, seconds>>
|
||||
to_sys(const local_time<_Duration>& __time, choose __z) const {
|
||||
local_info __info = get_info(__time);
|
||||
switch (__info.result) {
|
||||
case local_info::unique:
|
||||
case local_info::nonexistent: // first and second are the same
|
||||
return sys_time<common_type_t<_Duration, seconds>>{__time.time_since_epoch() - __info.first.offset};
|
||||
|
||||
case local_info::ambiguous:
|
||||
switch (__z) {
|
||||
case choose::earliest:
|
||||
return sys_time<common_type_t<_Duration, seconds>>{__time.time_since_epoch() - __info.first.offset};
|
||||
|
||||
case choose::latest:
|
||||
return sys_time<common_type_t<_Duration, seconds>>{__time.time_since_epoch() - __info.second.offset};
|
||||
|
||||
// Note a value out of bounds is not specified.
|
||||
}
|
||||
}
|
||||
|
||||
// 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:
|
||||
|
||||
@@ -774,6 +774,10 @@ class time_zone {
|
||||
template<class Duration>
|
||||
sys_time<common_type_t<Duration, seconds>>
|
||||
to_sys(const local_time<Duration>& tp) const;
|
||||
|
||||
template<class Duration>
|
||||
sys_time<common_type_t<Duration, seconds>>
|
||||
to_sys(const local_time<Duration>& tp, choose z) 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
|
||||
|
||||
@@ -223,11 +223,8 @@ export namespace std {
|
||||
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
|
||||
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
|
||||
|
||||
# if 0
|
||||
// [time.zone.timezone], class time_zone
|
||||
using std::chrono::choose;
|
||||
# endif // if 0
|
||||
|
||||
using std::chrono::time_zone;
|
||||
|
||||
# if 0
|
||||
|
||||
@@ -49,9 +49,12 @@ void test() {
|
||||
{
|
||||
std::chrono::sys_seconds s{};
|
||||
std::chrono::local_seconds l{};
|
||||
std::chrono::choose z = std::chrono::choose::earliest;
|
||||
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}}
|
||||
tz.to_sys(l); // not nodiscard
|
||||
tz.to_sys(l, z); // 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}}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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>
|
||||
|
||||
// enum class choose;
|
||||
|
||||
#include <chrono>
|
||||
#include <type_traits>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
int main(int, char**) {
|
||||
using E = std::chrono::choose;
|
||||
static_assert(std::is_enum_v<E>);
|
||||
|
||||
// Check that E is a scoped enum by checking for conversions.
|
||||
using UT = std::underlying_type_t<E>;
|
||||
static_assert(!std::is_convertible_v<E, UT>);
|
||||
|
||||
[[maybe_unused]] const E& early = E::earliest;
|
||||
[[maybe_unused]] const E& late = E::latest;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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, choose z) 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(), std::chrono::choose::earliest),
|
||||
"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), std::chrono::choose::latest),
|
||||
"cannot convert the local time; it would be after the maximum system clock value");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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>
|
||||
// sys_time<common_type_t<Duration, seconds>>
|
||||
// to_sys(const local_time<Duration>& tp, choose z) const;
|
||||
|
||||
#include <chrono>
|
||||
#include <format>
|
||||
#include <cassert>
|
||||
#include <string_view>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
// 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() {
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
const std::chrono::time_zone* tz = std::chrono::locate_zone("Etc/GMT+1");
|
||||
|
||||
assert(tz->to_sys(std::chrono::local_time<std::chrono::nanoseconds>{-1ns}, std::chrono::choose::earliest) ==
|
||||
std::chrono::sys_time<std::chrono::nanoseconds>{-1ns + 1h});
|
||||
|
||||
assert(tz->to_sys(std::chrono::local_time<std::chrono::microseconds>{0us}, std::chrono::choose::latest) ==
|
||||
std::chrono::sys_time<std::chrono::microseconds>{1h});
|
||||
|
||||
assert(tz->to_sys(
|
||||
std::chrono::local_time<std::chrono::seconds>{
|
||||
(std::chrono::sys_days{std::chrono::January / 1 / -21970}).time_since_epoch()},
|
||||
std::chrono::choose::earliest) ==
|
||||
std::chrono::sys_time<std::chrono::seconds>{
|
||||
(std::chrono::sys_days{std::chrono::January / 1 / -21970}).time_since_epoch() + 1h});
|
||||
|
||||
// sys_time<common_type_t<Duration, seconds>> is seconds for the larger types
|
||||
assert(tz->to_sys(
|
||||
std::chrono::local_time<std::chrono::days>{
|
||||
(std::chrono::sys_days{std::chrono::January / 1 / 21970}).time_since_epoch()},
|
||||
std::chrono::choose::latest) ==
|
||||
std::chrono::sys_time<std::chrono::seconds>{
|
||||
(std::chrono::sys_days{std::chrono::January / 1 / 21970}).time_since_epoch() + 1h});
|
||||
|
||||
assert(tz->to_sys(std::chrono::local_time<std::chrono::weeks>{}, std::chrono::choose::earliest) ==
|
||||
std::chrono::sys_time<std::chrono::seconds>{
|
||||
(std::chrono::sys_days{std::chrono::January / 1 / 1970}).time_since_epoch() + 1h});
|
||||
|
||||
// Note months and years cannot be streamed; however these functions don't
|
||||
// throw an exception and thus can be used.
|
||||
assert(tz->to_sys(std::chrono::local_time<std::chrono::months>{}, std::chrono::choose::latest) ==
|
||||
std::chrono::sys_time<std::chrono::seconds>{
|
||||
(std::chrono::sys_days{std::chrono::January / 1 / 1970}).time_since_epoch() + 1h});
|
||||
|
||||
assert(tz->to_sys(std::chrono::local_time<std::chrono::years>{}, std::chrono::choose::earliest) ==
|
||||
std::chrono::sys_time<std::chrono::seconds>{
|
||||
(std::chrono::sys_days{std::chrono::January / 1 / 1970}).time_since_epoch() + 1h});
|
||||
}
|
||||
|
||||
// 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()};
|
||||
|
||||
std::chrono::sys_seconds expected{time.time_since_epoch() - 1h};
|
||||
|
||||
// Validates whether the database did not change.
|
||||
std::chrono::local_info info = tz->get_info(time);
|
||||
assert(info.result == std::chrono::local_info::nonexistent);
|
||||
|
||||
assert(tz->to_sys(time + 0ns, std::chrono::choose::earliest) == expected);
|
||||
assert(tz->to_sys(time + 0us, std::chrono::choose::latest) == expected);
|
||||
assert(tz->to_sys(time + 0ms, std::chrono::choose::earliest) == expected);
|
||||
assert(tz->to_sys(time + 0s, std::chrono::choose::latest) == expected);
|
||||
}
|
||||
|
||||
// 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()};
|
||||
|
||||
std::chrono::sys_seconds earlier{time.time_since_epoch() - 2h};
|
||||
std::chrono::sys_seconds later{time.time_since_epoch() - 1h};
|
||||
|
||||
// Validates whether the database did not change.
|
||||
std::chrono::local_info info = tz->get_info(time);
|
||||
assert(info.result == std::chrono::local_info::ambiguous);
|
||||
|
||||
assert(tz->to_sys(time + 0ns, std::chrono::choose::earliest) == earlier);
|
||||
assert(tz->to_sys(time + 0us, std::chrono::choose::latest) == later);
|
||||
assert(tz->to_sys(time + 0ms, std::chrono::choose::earliest) == earlier);
|
||||
assert(tz->to_sys(time + 0s, std::chrono::choose::latest) == later);
|
||||
}
|
||||
|
||||
// This test does the basic validations of this function. The library function
|
||||
// uses `local_info get_info(const local_time<Duration>& tp)` as implementation
|
||||
// detail. The get_info function does extensive testing of the data.
|
||||
int main(int, char**) {
|
||||
test_unique();
|
||||
test_nonexistent();
|
||||
test_ambiguous();
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user