Skip to content

[libc++][TZDB] Finishes zoned_time constructors. #95010

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

Merged
merged 1 commit into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions libcxx/docs/Status/Cxx20Issues.csv
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@
"`3209 <https://wg21.link/LWG3209>`__","Expression in ``year::ok()``\ returns clause is ill-formed","Cologne","|Complete|",""
"","","","","",""
"`3231 <https://wg21.link/LWG3231>`__","``year_month_day_last::day``\ specification does not cover ``!ok()``\ values","Belfast","|Nothing To Do|",""
"`3225 <https://wg21.link/LWG3225>`__","``zoned_time``\ converting constructor shall not be ``noexcept``\ ","Belfast","","","|chrono|"
"`3225 <https://wg21.link/LWG3225>`__","``zoned_time``\ converting constructor shall not be ``noexcept``\ ","Belfast","|Complete|","19.0","|chrono|"
"`3190 <https://wg21.link/LWG3190>`__","``std::allocator::allocate``\ sometimes returns too little storage","Belfast","|Complete|","14.0"
"`3218 <https://wg21.link/LWG3218>`__","Modifier for ``%d``\ parse flag does not match POSIX and ``format``\ specification","Belfast","","","|chrono| |format|"
"`3224 <https://wg21.link/LWG3224>`__","``zoned_time``\ constructor from ``TimeZonePtr``\ does not specify initialization of ``tp_``\ ","Belfast","|Complete|","19.0","|chrono|"
Expand Down Expand Up @@ -199,7 +199,7 @@
"`3194 <https://wg21.link/LWG3194>`__","``ConvertibleTo``\ prose does not match code","Prague","|Complete|","13.0"
"`3200 <https://wg21.link/LWG3200>`__","``midpoint``\ should not constrain ``T``\ is complete","Prague","|Nothing To Do|",""
"`3201 <https://wg21.link/LWG3201>`__","``lerp``\ should be marked as ``noexcept``\ ","Prague","|Complete|",""
"`3226 <https://wg21.link/LWG3226>`__","``zoned_time``\ constructor from ``string_view``\ should accept ``zoned_time<Duration2, TimeZonePtr2>``\ ","Prague","","","|chrono|"
"`3226 <https://wg21.link/LWG3226>`__","``zoned_time``\ constructor from ``string_view``\ should accept ``zoned_time<Duration2, TimeZonePtr2>``\ ","Prague","|Complete|","19.0","|chrono|"
"`3233 <https://wg21.link/LWG3233>`__","Broken requirements for ``shared_ptr``\ converting constructors","Prague","|Complete|","19.0"
"`3237 <https://wg21.link/LWG3237>`__","LWG 3038 and 3190 have inconsistent PRs","Prague","|Complete|","16.0"
"`3238 <https://wg21.link/LWG3238>`__","Insufficiently-defined behavior of ``std::function``\ deduction guides","Prague","|Nothing To Do|",""
Expand Down
73 changes: 68 additions & 5 deletions libcxx/include/__chrono/zoned_time.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
// Enable the contents of the header only when libc++ was built with experimental features enabled.
#if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)

# include <__chrono/calendar.h>
# include <__chrono/duration.h>
# include <__chrono/system_clock.h>
# include <__chrono/time_zone.h>
Expand Down Expand Up @@ -56,6 +57,11 @@ class zoned_time {
static_assert(__is_duration<_Duration>::value,
"the program is ill-formed since _Duration is not a specialization of std::chrono::duration");

// The wording uses the constraints like
// constructible_from<zoned_time, decltype(__traits::locate_zone(string_view{}))>
// Using these constraints in the code causes the compiler to give an
// error that the constraint depends on itself. To avoid that issue use
// the fact it is possible to create this object from a _TimeZonePtr.
using __traits = zoned_traits<_TimeZonePtr>;

public:
Expand All @@ -76,14 +82,71 @@ class zoned_time {

_LIBCPP_HIDE_FROM_ABI explicit zoned_time(string_view __name)
requires(requires { __traits::locate_zone(string_view{}); } &&
// The wording uses the constraint
// constructible_from<zoned_time, decltype(__traits::locate_zone(string_view{}))>
// Using this constraint in the code causes the compiler to give an
// error the constraint depends on itself. To avoid that issue use
// the fact it is possible to create this object from a _TimeZonePtr.
constructible_from<_TimeZonePtr, decltype(__traits::locate_zone(string_view{}))>)
: __zone_{__traits::locate_zone(__name)}, __tp_{} {}

template <class _Duration2>
_LIBCPP_HIDE_FROM_ABI zoned_time(const zoned_time<_Duration2, _TimeZonePtr>& __zt)
requires is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>>
: __zone_{__zt.get_time_zone()}, __tp_{__zt.get_sys_time()} {}

_LIBCPP_HIDE_FROM_ABI zoned_time(_TimeZonePtr __zone, const sys_time<_Duration>& __tp)
: __zone_{std::move(__zone)}, __tp_{__tp} {}

_LIBCPP_HIDE_FROM_ABI zoned_time(string_view __name, const sys_time<_Duration>& __tp)
requires requires { _TimeZonePtr{__traits::locate_zone(string_view{})}; }
: zoned_time{__traits::locate_zone(__name), __tp} {}

_LIBCPP_HIDE_FROM_ABI zoned_time(_TimeZonePtr __zone, const local_time<_Duration>& __tp)
requires(is_convertible_v<decltype(std::declval<_TimeZonePtr&>() -> to_sys(local_time<_Duration>{})),
sys_time<duration>>)
: __zone_{std::move(__zone)}, __tp_{__zone_->to_sys(__tp)} {}

_LIBCPP_HIDE_FROM_ABI zoned_time(string_view __name, const local_time<_Duration>& __tp)
requires(requires {
_TimeZonePtr{__traits::locate_zone(string_view{})};
} && is_convertible_v<decltype(std::declval<_TimeZonePtr&>() -> to_sys(local_time<_Duration>{})),
sys_time<duration>>)
: zoned_time{__traits::locate_zone(__name), __tp} {}

_LIBCPP_HIDE_FROM_ABI zoned_time(_TimeZonePtr __zone, const local_time<_Duration>& __tp, choose __c)
requires(is_convertible_v<
decltype(std::declval<_TimeZonePtr&>() -> to_sys(local_time<_Duration>{}, choose::earliest)),
sys_time<duration>>)
: __zone_{std::move(__zone)}, __tp_{__zone_->to_sys(__tp, __c)} {}

_LIBCPP_HIDE_FROM_ABI zoned_time(string_view __name, const local_time<_Duration>& __tp, choose __c)
requires(requires {
_TimeZonePtr{__traits::locate_zone(string_view{})};
} && is_convertible_v<decltype(std::declval<_TimeZonePtr&>() -> to_sys(local_time<_Duration>{}, choose::earliest)),
sys_time<duration>>)
: zoned_time{__traits::locate_zone(__name), __tp, __c} {}

template <class _Duration2, class _TimeZonePtr2>
_LIBCPP_HIDE_FROM_ABI zoned_time(_TimeZonePtr __zone, const zoned_time<_Duration2, _TimeZonePtr2>& __zt)
requires is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>>
: __zone_{std::move(__zone)}, __tp_{__zt.get_sys_time()} {}

// per wording choose has no effect
template <class _Duration2, class _TimeZonePtr2>
_LIBCPP_HIDE_FROM_ABI zoned_time(_TimeZonePtr __zone, const zoned_time<_Duration2, _TimeZonePtr2>& __zt, choose)
requires is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>>
: __zone_{std::move(__zone)}, __tp_{__zt.get_sys_time()} {}

template <class _Duration2, class _TimeZonePtr2>
_LIBCPP_HIDE_FROM_ABI zoned_time(string_view __name, const zoned_time<_Duration2, _TimeZonePtr2>& __zt)
requires(requires {
_TimeZonePtr{__traits::locate_zone(string_view{})};
} && is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>>)
: zoned_time{__traits::locate_zone(__name), __zt} {}

template <class _Duration2, class _TimeZonePtr2>
_LIBCPP_HIDE_FROM_ABI zoned_time(string_view __name, const zoned_time<_Duration2, _TimeZonePtr2>& __zt, choose __c)
requires(requires {
_TimeZonePtr{__traits::locate_zone(string_view{})};
} && is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>>)
: zoned_time{__traits::locate_zone(__name), __zt, __c} {}

[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _TimeZonePtr get_time_zone() const { return __zone_; }
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI sys_time<duration> get_sys_time() const { return __tp_; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <charconv>
#include <chrono>
#include <string_view>
#include <type_traits>

enum class offset_time_zone_flags {
none = 0,
Expand All @@ -39,6 +40,15 @@ class offset_time_zone {

std::chrono::seconds offset() const { return offset_; }

offset_time_zone* operator->() { return this; }

template <class Duration>
std::chrono::sys_time<std::common_type_t<Duration, std::chrono::seconds>>
to_sys(const std::chrono::local_time<Duration>& local) const {
return std::chrono::sys_time<std::common_type_t<Duration, std::chrono::seconds>>{
local.time_since_epoch() + offset_};
}

private:
std::chrono::seconds offset_;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//===----------------------------------------------------------------------===//
//
// 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(string_view name, const local_time<Duration>& st);

#include <chrono>
#include <concepts>

#include "../test_offset_time_zone.h"

namespace cr = std::chrono;

// Verify the results of the constructed object.
int main(int, char**) {
{
using ptr = const cr::time_zone*;
static_assert(std::constructible_from<cr::zoned_time<cr::seconds, ptr>, std::string_view, cr::sys_seconds>);

cr::zoned_time<cr::seconds> zt{"Etc/GMT+1", cr::local_seconds{cr::seconds{0}}};

assert(zt.get_time_zone() == cr::locate_zone("Etc/GMT+1"));
assert(zt.get_sys_time() == cr::sys_seconds{cr::hours{1}});
}

{
using ptr = offset_time_zone<offset_time_zone_flags::none>;
static_assert(!std::constructible_from<cr::zoned_time<cr::seconds, ptr>, std::string_view, cr::local_seconds>);
}

{
using ptr = offset_time_zone<offset_time_zone_flags::has_default_zone>;
static_assert(!std::constructible_from<cr::zoned_time<cr::seconds, ptr>, std::string_view, cr::local_seconds>);
}

{
using ptr = offset_time_zone<offset_time_zone_flags::has_locate_zone>;
static_assert(std::constructible_from<cr::zoned_time<cr::seconds, ptr>, std::string_view, cr::local_seconds>);

ptr tz;
cr::zoned_time<cr::seconds, ptr> zt{"42", cr::local_seconds{cr::seconds{99}}};

assert(zt.get_time_zone().offset() == cr::seconds{42});
assert(zt.get_sys_time() == cr::sys_seconds{cr::seconds{141}});
}

{
using ptr = offset_time_zone<offset_time_zone_flags::both>;
static_assert(std::constructible_from<cr::zoned_time<cr::seconds, ptr>, std::string_view, cr::local_seconds>);

ptr tz;
cr::zoned_time<cr::seconds, ptr> zt{"42", cr::local_seconds{cr::seconds{99}}};

assert(zt.get_time_zone().offset() == cr::seconds{42});
assert(zt.get_sys_time() == cr::sys_seconds{cr::seconds{141}});
}

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//===----------------------------------------------------------------------===/
//
// 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(string_view name, const local_time<Duration>& st, choose c);

#include <chrono>
#include <concepts>
#include <cassert>

#include "../test_offset_time_zone.h"

namespace cr = std::chrono;

int main(int, char**) {
// Tests unique conversions. To make sure the test does not depend on changes
// in the database it uses a time zone with a fixed offset.
{
cr::zoned_time<cr::seconds> zt{"Etc/GMT+1", cr::local_seconds{cr::seconds{0}}, cr::choose::earliest};

assert(zt.get_time_zone() == cr::locate_zone("Etc/GMT+1"));
assert(zt.get_sys_time() == cr::sys_seconds{cr::hours{1}});
}

// Tests ambiguous conversions.
{
// 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 -

using namespace std::literals::chrono_literals;
{
cr::zoned_time<cr::seconds> zt{
"Europe/Berlin",
cr::local_seconds{(cr::sys_days{cr::September / 28 / 1986} + 2h + 30min).time_since_epoch()},
cr::choose::earliest};

assert(zt.get_time_zone() == cr::locate_zone("Europe/Berlin"));
assert(zt.get_sys_time() == cr::sys_days{cr::September / 28 / 1986} + 0h + 30min);
}
{
cr::zoned_time<cr::seconds> zt{
"Europe/Berlin",
cr::local_seconds{(cr::sys_days{cr::September / 28 / 1986} + 2h + 30min).time_since_epoch()},
cr::choose::latest};

assert(zt.get_time_zone() == cr::locate_zone("Europe/Berlin"));
assert(zt.get_sys_time() == cr::sys_days{cr::September / 28 / 1986} + 1h + 30min);
}
}

static_assert(std::constructible_from<cr::zoned_time<cr::seconds, const cr::time_zone*>,
std::string_view,
cr::local_seconds,
cr::choose>);

static_assert(!std::constructible_from< cr::zoned_time<cr::seconds, offset_time_zone<offset_time_zone_flags::none>>,
std::string_view,
cr::local_seconds,
cr::choose>);

static_assert(
!std::constructible_from< cr::zoned_time<cr::seconds, offset_time_zone<offset_time_zone_flags::has_default_zone>>,
std::string_view,
cr::local_seconds,
cr::choose>);

static_assert(
!std::constructible_from< cr::zoned_time<cr::seconds, offset_time_zone<offset_time_zone_flags::has_locate_zone>>,
std::string_view,
cr::local_seconds,
cr::choose>);

static_assert(!std::constructible_from< cr::zoned_time<cr::seconds, offset_time_zone<offset_time_zone_flags::both>>,
std::string_view,
cr::local_seconds,
cr::choose>);

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//===----------------------------------------------------------------------===//
//
// 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(string_view name, const sys_time<Duration>& st);

#include <chrono>
#include <concepts>

#include "../test_offset_time_zone.h"

namespace cr = std::chrono;

// Verify the results of the constructed object.
int main(int, char**) {
{
using ptr = const cr::time_zone*;
static_assert(std::constructible_from<cr::zoned_time<cr::seconds, ptr>, std::string_view, cr::sys_seconds>);

cr::zoned_time<cr::seconds> zt{"UTC", cr::sys_seconds{cr::seconds{99}}};

assert(zt.get_time_zone() == cr::locate_zone("UTC"));
assert(zt.get_sys_time() == cr::sys_seconds{cr::seconds{99}});
}

{
using ptr = offset_time_zone<offset_time_zone_flags::none>;
static_assert(!std::constructible_from<cr::zoned_time<cr::seconds, ptr>, std::string_view, cr::sys_seconds>);
}

{
using ptr = offset_time_zone<offset_time_zone_flags::has_default_zone>;
static_assert(!std::constructible_from<cr::zoned_time<cr::seconds, ptr>, std::string_view, cr::sys_seconds>);
}

{
using ptr = offset_time_zone<offset_time_zone_flags::has_locate_zone>;
static_assert(std::constructible_from<cr::zoned_time<cr::seconds, ptr>, std::string_view, cr::sys_seconds>);

ptr tz;
cr::zoned_time<cr::seconds, ptr> zt{"42", cr::sys_seconds{cr::seconds{99}}};

assert(zt.get_time_zone().offset() == cr::seconds{42});
assert(zt.get_sys_time() == cr::sys_seconds{cr::seconds{99}});
}

{
using ptr = offset_time_zone<offset_time_zone_flags::both>;
static_assert(std::constructible_from<cr::zoned_time<cr::seconds, ptr>, std::string_view, cr::sys_seconds>);

ptr tz;
cr::zoned_time<cr::seconds, ptr> zt{"42", cr::sys_seconds{cr::seconds{99}}};

assert(zt.get_time_zone().offset() == cr::seconds{42});
assert(zt.get_sys_time() == cr::sys_seconds{cr::seconds{99}});
}

return 0;
}
Loading
Loading