Skip to content

Commit a1d73ac

Browse files
authored
[libc++][TZDB] Finishes zoned_time constructors. (#95010)
Completes - LWG3225 zoned_time converting constructor shall not be noexcept - LWG3226 zoned_time constructor from string_view should accept zoned_time<Duration2, TimeZonePtr2> Implements parts of: - P0355 Extending to chrono Calendars and Time Zones
1 parent dda4184 commit a1d73ac

14 files changed

+1252
-7
lines changed

libcxx/docs/Status/Cxx20Issues.csv

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@
162162
"`3209 <https://wg21.link/LWG3209>`__","Expression in ``year::ok()``\ returns clause is ill-formed","Cologne","|Complete|",""
163163
"","","","","",""
164164
"`3231 <https://wg21.link/LWG3231>`__","``year_month_day_last::day``\ specification does not cover ``!ok()``\ values","Belfast","|Nothing To Do|",""
165-
"`3225 <https://wg21.link/LWG3225>`__","``zoned_time``\ converting constructor shall not be ``noexcept``\ ","Belfast","","","|chrono|"
165+
"`3225 <https://wg21.link/LWG3225>`__","``zoned_time``\ converting constructor shall not be ``noexcept``\ ","Belfast","|Complete|","19.0","|chrono|"
166166
"`3190 <https://wg21.link/LWG3190>`__","``std::allocator::allocate``\ sometimes returns too little storage","Belfast","|Complete|","14.0"
167167
"`3218 <https://wg21.link/LWG3218>`__","Modifier for ``%d``\ parse flag does not match POSIX and ``format``\ specification","Belfast","","","|chrono| |format|"
168168
"`3224 <https://wg21.link/LWG3224>`__","``zoned_time``\ constructor from ``TimeZonePtr``\ does not specify initialization of ``tp_``\ ","Belfast","|Complete|","19.0","|chrono|"
@@ -199,7 +199,7 @@
199199
"`3194 <https://wg21.link/LWG3194>`__","``ConvertibleTo``\ prose does not match code","Prague","|Complete|","13.0"
200200
"`3200 <https://wg21.link/LWG3200>`__","``midpoint``\ should not constrain ``T``\ is complete","Prague","|Nothing To Do|",""
201201
"`3201 <https://wg21.link/LWG3201>`__","``lerp``\ should be marked as ``noexcept``\ ","Prague","|Complete|",""
202-
"`3226 <https://wg21.link/LWG3226>`__","``zoned_time``\ constructor from ``string_view``\ should accept ``zoned_time<Duration2, TimeZonePtr2>``\ ","Prague","","","|chrono|"
202+
"`3226 <https://wg21.link/LWG3226>`__","``zoned_time``\ constructor from ``string_view``\ should accept ``zoned_time<Duration2, TimeZonePtr2>``\ ","Prague","|Complete|","19.0","|chrono|"
203203
"`3233 <https://wg21.link/LWG3233>`__","Broken requirements for ``shared_ptr``\ converting constructors","Prague","|Complete|","19.0"
204204
"`3237 <https://wg21.link/LWG3237>`__","LWG 3038 and 3190 have inconsistent PRs","Prague","|Complete|","16.0"
205205
"`3238 <https://wg21.link/LWG3238>`__","Insufficiently-defined behavior of ``std::function``\ deduction guides","Prague","|Nothing To Do|",""

libcxx/include/__chrono/zoned_time.h

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// Enable the contents of the header only when libc++ was built with experimental features enabled.
1717
#if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
1818

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

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

6167
public:
@@ -76,14 +82,71 @@ class zoned_time {
7682

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

88+
template <class _Duration2>
89+
_LIBCPP_HIDE_FROM_ABI zoned_time(const zoned_time<_Duration2, _TimeZonePtr>& __zt)
90+
requires is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>>
91+
: __zone_{__zt.get_time_zone()}, __tp_{__zt.get_sys_time()} {}
92+
93+
_LIBCPP_HIDE_FROM_ABI zoned_time(_TimeZonePtr __zone, const sys_time<_Duration>& __tp)
94+
: __zone_{std::move(__zone)}, __tp_{__tp} {}
95+
96+
_LIBCPP_HIDE_FROM_ABI zoned_time(string_view __name, const sys_time<_Duration>& __tp)
97+
requires requires { _TimeZonePtr{__traits::locate_zone(string_view{})}; }
98+
: zoned_time{__traits::locate_zone(__name), __tp} {}
99+
100+
_LIBCPP_HIDE_FROM_ABI zoned_time(_TimeZonePtr __zone, const local_time<_Duration>& __tp)
101+
requires(is_convertible_v<decltype(std::declval<_TimeZonePtr&>() -> to_sys(local_time<_Duration>{})),
102+
sys_time<duration>>)
103+
: __zone_{std::move(__zone)}, __tp_{__zone_->to_sys(__tp)} {}
104+
105+
_LIBCPP_HIDE_FROM_ABI zoned_time(string_view __name, const local_time<_Duration>& __tp)
106+
requires(requires {
107+
_TimeZonePtr{__traits::locate_zone(string_view{})};
108+
} && is_convertible_v<decltype(std::declval<_TimeZonePtr&>() -> to_sys(local_time<_Duration>{})),
109+
sys_time<duration>>)
110+
: zoned_time{__traits::locate_zone(__name), __tp} {}
111+
112+
_LIBCPP_HIDE_FROM_ABI zoned_time(_TimeZonePtr __zone, const local_time<_Duration>& __tp, choose __c)
113+
requires(is_convertible_v<
114+
decltype(std::declval<_TimeZonePtr&>() -> to_sys(local_time<_Duration>{}, choose::earliest)),
115+
sys_time<duration>>)
116+
: __zone_{std::move(__zone)}, __tp_{__zone_->to_sys(__tp, __c)} {}
117+
118+
_LIBCPP_HIDE_FROM_ABI zoned_time(string_view __name, const local_time<_Duration>& __tp, choose __c)
119+
requires(requires {
120+
_TimeZonePtr{__traits::locate_zone(string_view{})};
121+
} && is_convertible_v<decltype(std::declval<_TimeZonePtr&>() -> to_sys(local_time<_Duration>{}, choose::earliest)),
122+
sys_time<duration>>)
123+
: zoned_time{__traits::locate_zone(__name), __tp, __c} {}
124+
125+
template <class _Duration2, class _TimeZonePtr2>
126+
_LIBCPP_HIDE_FROM_ABI zoned_time(_TimeZonePtr __zone, const zoned_time<_Duration2, _TimeZonePtr2>& __zt)
127+
requires is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>>
128+
: __zone_{std::move(__zone)}, __tp_{__zt.get_sys_time()} {}
129+
130+
// per wording choose has no effect
131+
template <class _Duration2, class _TimeZonePtr2>
132+
_LIBCPP_HIDE_FROM_ABI zoned_time(_TimeZonePtr __zone, const zoned_time<_Duration2, _TimeZonePtr2>& __zt, choose)
133+
requires is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>>
134+
: __zone_{std::move(__zone)}, __tp_{__zt.get_sys_time()} {}
135+
136+
template <class _Duration2, class _TimeZonePtr2>
137+
_LIBCPP_HIDE_FROM_ABI zoned_time(string_view __name, const zoned_time<_Duration2, _TimeZonePtr2>& __zt)
138+
requires(requires {
139+
_TimeZonePtr{__traits::locate_zone(string_view{})};
140+
} && is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>>)
141+
: zoned_time{__traits::locate_zone(__name), __zt} {}
142+
143+
template <class _Duration2, class _TimeZonePtr2>
144+
_LIBCPP_HIDE_FROM_ABI zoned_time(string_view __name, const zoned_time<_Duration2, _TimeZonePtr2>& __zt, choose __c)
145+
requires(requires {
146+
_TimeZonePtr{__traits::locate_zone(string_view{})};
147+
} && is_convertible_v<sys_time<_Duration2>, sys_time<_Duration>>)
148+
: zoned_time{__traits::locate_zone(__name), __zt, __c} {}
149+
87150
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _TimeZonePtr get_time_zone() const { return __zone_; }
88151
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI sys_time<duration> get_sys_time() const { return __tp_; }
89152

libcxx/test/std/time/time.zone/time.zone.zonedtime/test_offset_time_zone.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <charconv>
1515
#include <chrono>
1616
#include <string_view>
17+
#include <type_traits>
1718

1819
enum class offset_time_zone_flags {
1920
none = 0,
@@ -39,6 +40,15 @@ class offset_time_zone {
3940

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

43+
offset_time_zone* operator->() { return this; }
44+
45+
template <class Duration>
46+
std::chrono::sys_time<std::common_type_t<Duration, std::chrono::seconds>>
47+
to_sys(const std::chrono::local_time<Duration>& local) const {
48+
return std::chrono::sys_time<std::common_type_t<Duration, std::chrono::seconds>>{
49+
local.time_since_epoch() + offset_};
50+
}
51+
4252
private:
4353
std::chrono::seconds offset_;
4454
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++14, c++17
10+
// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
11+
12+
// XFAIL: libcpp-has-no-experimental-tzdb
13+
// XFAIL: availability-tzdb-missing
14+
15+
// <chrono>
16+
17+
// template<class Duration, class TimeZonePtr = const time_zone*>
18+
// class zoned_time;
19+
//
20+
// zoned_time(string_view name, const local_time<Duration>& st);
21+
22+
#include <chrono>
23+
#include <concepts>
24+
25+
#include "../test_offset_time_zone.h"
26+
27+
namespace cr = std::chrono;
28+
29+
// Verify the results of the constructed object.
30+
int main(int, char**) {
31+
{
32+
using ptr = const cr::time_zone*;
33+
static_assert(std::constructible_from<cr::zoned_time<cr::seconds, ptr>, std::string_view, cr::sys_seconds>);
34+
35+
cr::zoned_time<cr::seconds> zt{"Etc/GMT+1", cr::local_seconds{cr::seconds{0}}};
36+
37+
assert(zt.get_time_zone() == cr::locate_zone("Etc/GMT+1"));
38+
assert(zt.get_sys_time() == cr::sys_seconds{cr::hours{1}});
39+
}
40+
41+
{
42+
using ptr = offset_time_zone<offset_time_zone_flags::none>;
43+
static_assert(!std::constructible_from<cr::zoned_time<cr::seconds, ptr>, std::string_view, cr::local_seconds>);
44+
}
45+
46+
{
47+
using ptr = offset_time_zone<offset_time_zone_flags::has_default_zone>;
48+
static_assert(!std::constructible_from<cr::zoned_time<cr::seconds, ptr>, std::string_view, cr::local_seconds>);
49+
}
50+
51+
{
52+
using ptr = offset_time_zone<offset_time_zone_flags::has_locate_zone>;
53+
static_assert(std::constructible_from<cr::zoned_time<cr::seconds, ptr>, std::string_view, cr::local_seconds>);
54+
55+
ptr tz;
56+
cr::zoned_time<cr::seconds, ptr> zt{"42", cr::local_seconds{cr::seconds{99}}};
57+
58+
assert(zt.get_time_zone().offset() == cr::seconds{42});
59+
assert(zt.get_sys_time() == cr::sys_seconds{cr::seconds{141}});
60+
}
61+
62+
{
63+
using ptr = offset_time_zone<offset_time_zone_flags::both>;
64+
static_assert(std::constructible_from<cr::zoned_time<cr::seconds, ptr>, std::string_view, cr::local_seconds>);
65+
66+
ptr tz;
67+
cr::zoned_time<cr::seconds, ptr> zt{"42", cr::local_seconds{cr::seconds{99}}};
68+
69+
assert(zt.get_time_zone().offset() == cr::seconds{42});
70+
assert(zt.get_sys_time() == cr::sys_seconds{cr::seconds{141}});
71+
}
72+
73+
return 0;
74+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
//===----------------------------------------------------------------------===/
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++14, c++17
10+
// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
11+
12+
// XFAIL: libcpp-has-no-experimental-tzdb
13+
// XFAIL: availability-tzdb-missing
14+
15+
// <chrono>
16+
17+
// template<class Duration, class TimeZonePtr = const time_zone*>
18+
// class zoned_time;
19+
//
20+
// zoned_time(string_view name, const local_time<Duration>& st, choose c);
21+
22+
#include <chrono>
23+
#include <concepts>
24+
#include <cassert>
25+
26+
#include "../test_offset_time_zone.h"
27+
28+
namespace cr = std::chrono;
29+
30+
int main(int, char**) {
31+
// Tests unique conversions. To make sure the test does not depend on changes
32+
// in the database it uses a time zone with a fixed offset.
33+
{
34+
cr::zoned_time<cr::seconds> zt{"Etc/GMT+1", cr::local_seconds{cr::seconds{0}}, cr::choose::earliest};
35+
36+
assert(zt.get_time_zone() == cr::locate_zone("Etc/GMT+1"));
37+
assert(zt.get_sys_time() == cr::sys_seconds{cr::hours{1}});
38+
}
39+
40+
// Tests ambiguous conversions.
41+
{
42+
// Z Europe/Berlin 0:53:28 - LMT 1893 Ap
43+
// ...
44+
// 1 DE CE%sT 1980
45+
// 1 E CE%sT
46+
//
47+
// ...
48+
// R E 1981 ma - Mar lastSu 1u 1 S
49+
// R E 1996 ma - O lastSu 1u 0 -
50+
51+
using namespace std::literals::chrono_literals;
52+
{
53+
cr::zoned_time<cr::seconds> zt{
54+
"Europe/Berlin",
55+
cr::local_seconds{(cr::sys_days{cr::September / 28 / 1986} + 2h + 30min).time_since_epoch()},
56+
cr::choose::earliest};
57+
58+
assert(zt.get_time_zone() == cr::locate_zone("Europe/Berlin"));
59+
assert(zt.get_sys_time() == cr::sys_days{cr::September / 28 / 1986} + 0h + 30min);
60+
}
61+
{
62+
cr::zoned_time<cr::seconds> zt{
63+
"Europe/Berlin",
64+
cr::local_seconds{(cr::sys_days{cr::September / 28 / 1986} + 2h + 30min).time_since_epoch()},
65+
cr::choose::latest};
66+
67+
assert(zt.get_time_zone() == cr::locate_zone("Europe/Berlin"));
68+
assert(zt.get_sys_time() == cr::sys_days{cr::September / 28 / 1986} + 1h + 30min);
69+
}
70+
}
71+
72+
static_assert(std::constructible_from<cr::zoned_time<cr::seconds, const cr::time_zone*>,
73+
std::string_view,
74+
cr::local_seconds,
75+
cr::choose>);
76+
77+
static_assert(!std::constructible_from< cr::zoned_time<cr::seconds, offset_time_zone<offset_time_zone_flags::none>>,
78+
std::string_view,
79+
cr::local_seconds,
80+
cr::choose>);
81+
82+
static_assert(
83+
!std::constructible_from< cr::zoned_time<cr::seconds, offset_time_zone<offset_time_zone_flags::has_default_zone>>,
84+
std::string_view,
85+
cr::local_seconds,
86+
cr::choose>);
87+
88+
static_assert(
89+
!std::constructible_from< cr::zoned_time<cr::seconds, offset_time_zone<offset_time_zone_flags::has_locate_zone>>,
90+
std::string_view,
91+
cr::local_seconds,
92+
cr::choose>);
93+
94+
static_assert(!std::constructible_from< cr::zoned_time<cr::seconds, offset_time_zone<offset_time_zone_flags::both>>,
95+
std::string_view,
96+
cr::local_seconds,
97+
cr::choose>);
98+
99+
return 0;
100+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++14, c++17
10+
// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
11+
12+
// XFAIL: libcpp-has-no-experimental-tzdb
13+
// XFAIL: availability-tzdb-missing
14+
15+
// <chrono>
16+
17+
// template<class Duration, class TimeZonePtr = const time_zone*>
18+
// class zoned_time;
19+
//
20+
// zoned_time(string_view name, const sys_time<Duration>& st);
21+
22+
#include <chrono>
23+
#include <concepts>
24+
25+
#include "../test_offset_time_zone.h"
26+
27+
namespace cr = std::chrono;
28+
29+
// Verify the results of the constructed object.
30+
int main(int, char**) {
31+
{
32+
using ptr = const cr::time_zone*;
33+
static_assert(std::constructible_from<cr::zoned_time<cr::seconds, ptr>, std::string_view, cr::sys_seconds>);
34+
35+
cr::zoned_time<cr::seconds> zt{"UTC", cr::sys_seconds{cr::seconds{99}}};
36+
37+
assert(zt.get_time_zone() == cr::locate_zone("UTC"));
38+
assert(zt.get_sys_time() == cr::sys_seconds{cr::seconds{99}});
39+
}
40+
41+
{
42+
using ptr = offset_time_zone<offset_time_zone_flags::none>;
43+
static_assert(!std::constructible_from<cr::zoned_time<cr::seconds, ptr>, std::string_view, cr::sys_seconds>);
44+
}
45+
46+
{
47+
using ptr = offset_time_zone<offset_time_zone_flags::has_default_zone>;
48+
static_assert(!std::constructible_from<cr::zoned_time<cr::seconds, ptr>, std::string_view, cr::sys_seconds>);
49+
}
50+
51+
{
52+
using ptr = offset_time_zone<offset_time_zone_flags::has_locate_zone>;
53+
static_assert(std::constructible_from<cr::zoned_time<cr::seconds, ptr>, std::string_view, cr::sys_seconds>);
54+
55+
ptr tz;
56+
cr::zoned_time<cr::seconds, ptr> zt{"42", cr::sys_seconds{cr::seconds{99}}};
57+
58+
assert(zt.get_time_zone().offset() == cr::seconds{42});
59+
assert(zt.get_sys_time() == cr::sys_seconds{cr::seconds{99}});
60+
}
61+
62+
{
63+
using ptr = offset_time_zone<offset_time_zone_flags::both>;
64+
static_assert(std::constructible_from<cr::zoned_time<cr::seconds, ptr>, std::string_view, cr::sys_seconds>);
65+
66+
ptr tz;
67+
cr::zoned_time<cr::seconds, ptr> zt{"42", cr::sys_seconds{cr::seconds{99}}};
68+
69+
assert(zt.get_time_zone().offset() == cr::seconds{42});
70+
assert(zt.get_sys_time() == cr::sys_seconds{cr::seconds{99}});
71+
}
72+
73+
return 0;
74+
}

0 commit comments

Comments
 (0)