Skip to content

Commit d7b4271

Browse files
committed
[libc++][TZDB] Implements time_zone::to_sys.
This implements the throwing overload and the exception classes throw by this overload. Implements parts of: - P0355 Extending chrono to Calendars and Time Zones
1 parent 6939905 commit d7b4271

File tree

23 files changed

+987
-44
lines changed

23 files changed

+987
-44
lines changed

libcxx/include/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ set(files
264264
__chrono/convert_to_tm.h
265265
__chrono/day.h
266266
__chrono/duration.h
267+
__chrono/exception.h
267268
__chrono/file_clock.h
268269
__chrono/formatter.h
269270
__chrono/hh_mm_ss.h

libcxx/include/__chrono/exception.h

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// -*- C++ -*-
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html
11+
12+
#ifndef _LIBCPP___CHRONO_EXCEPTION_H
13+
#define _LIBCPP___CHRONO_EXCEPTION_H
14+
15+
#include <version>
16+
// Enable the contents of the header only when libc++ was built with experimental features enabled.
17+
#if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
18+
19+
# include <__availability>
20+
# include <__chrono/calendar.h>
21+
# include <__chrono/local_info.h>
22+
# include <__chrono/time_point.h>
23+
# include <__config>
24+
# include <__verbose_abort>
25+
# include <format>
26+
# include <stdexcept>
27+
# include <string>
28+
29+
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
30+
# pragma GCC system_header
31+
# endif
32+
33+
_LIBCPP_BEGIN_NAMESPACE_STD
34+
35+
# if _LIBCPP_STD_VER >= 20
36+
37+
namespace chrono {
38+
39+
class nonexistent_local_time : public runtime_error {
40+
public:
41+
template <class _Duration>
42+
_LIBCPP_HIDE_FROM_ABI nonexistent_local_time(const local_time<_Duration>& __time, const local_info& __info)
43+
: runtime_error{__create_message(__time, __info)} {
44+
// [time.zone.exception.nonexist]/2
45+
// Preconditions: i.result == local_info::nonexistent is true.
46+
// The value of __info.result is not used.
47+
_LIBCPP_ASSERT_PEDANTIC(__info.result == local_info::nonexistent,
48+
"creating an nonexistent_local_time from a local_info that is not non-existent");
49+
}
50+
51+
_LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI ~nonexistent_local_time() override; // exported as key function
52+
53+
private:
54+
template <class _Duration>
55+
_LIBCPP_HIDE_FROM_ABI string __create_message(const local_time<_Duration>& __time, const local_info& __info) {
56+
return std::format(
57+
R"({} is in a gap between
58+
{} {} and
59+
{} {} which are both equivalent to
60+
{} UTC)",
61+
__time,
62+
local_seconds{__info.first.end.time_since_epoch()} + __info.first.offset,
63+
__info.first.abbrev,
64+
local_seconds{__info.second.begin.time_since_epoch()} + __info.second.offset,
65+
__info.second.abbrev,
66+
__info.first.end);
67+
}
68+
};
69+
70+
template <class _Duration>
71+
_LIBCPP_NORETURN _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI void __throw_nonexistent_local_time(
72+
[[maybe_unused]] const local_time<_Duration>& __time, [[maybe_unused]] const local_info& __info) {
73+
# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
74+
throw nonexistent_local_time(__time, __info);
75+
# else
76+
_LIBCPP_VERBOSE_ABORT("nonexistent_local_time was thrown in -fno-exceptions mode");
77+
# endif
78+
}
79+
80+
class ambiguous_local_time : public runtime_error {
81+
public:
82+
template <class _Duration>
83+
_LIBCPP_HIDE_FROM_ABI ambiguous_local_time(const local_time<_Duration>& __time, const local_info& __info)
84+
: runtime_error{__create_message(__time, __info)} {
85+
// [time.zone.exception.ambig]/2
86+
// Preconditions: i.result == local_info::ambiguous is true.
87+
// The value of __info.result is not used.
88+
_LIBCPP_ASSERT_PEDANTIC(__info.result == local_info::ambiguous,
89+
"creating an ambiguous_local_time from a local_info that is not ambiguous");
90+
}
91+
92+
_LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI ~ambiguous_local_time() override; // exported as key function
93+
94+
private:
95+
template <class _Duration>
96+
_LIBCPP_HIDE_FROM_ABI string __create_message(const local_time<_Duration>& __time, const local_info& __info) {
97+
return std::format(
98+
// There are two spaces after the full-stop; this has been verified
99+
// in the sources of the Standard.
100+
R"({0} is ambiguous. It could be
101+
{0} {1} == {2} UTC or
102+
{0} {3} == {4} UTC)",
103+
__time,
104+
__info.first.abbrev,
105+
__time - __info.first.offset,
106+
__info.second.abbrev,
107+
__time - __info.second.offset);
108+
}
109+
};
110+
111+
template <class _Duration>
112+
_LIBCPP_NORETURN _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI void __throw_ambiguous_local_time(
113+
[[maybe_unused]] const local_time<_Duration>& __time, [[maybe_unused]] const local_info& __info) {
114+
# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
115+
throw ambiguous_local_time(__time, __info);
116+
# else
117+
_LIBCPP_VERBOSE_ABORT("ambiguous_local_time was thrown in -fno-exceptions mode");
118+
# endif
119+
}
120+
121+
} // namespace chrono
122+
123+
# endif // _LIBCPP_STD_VER >= 20
124+
125+
_LIBCPP_END_NAMESPACE_STD
126+
127+
#endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
128+
129+
#endif // _LIBCPP___CHRONO_EXCEPTION_H

libcxx/include/__chrono/time_zone.h

+26
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@
1818

1919
# include <__chrono/calendar.h>
2020
# include <__chrono/duration.h>
21+
# include <__chrono/exception.h>
2122
# include <__chrono/local_info.h>
2223
# include <__chrono/sys_info.h>
2324
# include <__chrono/system_clock.h>
2425
# include <__compare/strong_order.h>
2526
# include <__config>
2627
# include <__memory/unique_ptr.h>
28+
# include <__type_traits/common_type.h>
2729
# include <string_view>
2830

2931
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -70,6 +72,30 @@ class _LIBCPP_AVAILABILITY_TZDB time_zone {
7072
return __get_info(chrono::time_point_cast<seconds>(__time));
7173
}
7274

75+
// Since the interface promisses throwing, don't add nodiscard.
76+
template <class _Duration>
77+
_LIBCPP_HIDE_FROM_ABI sys_time<common_type_t<_Duration, seconds>> to_sys(const local_time<_Duration>& __time) const {
78+
local_info __info = get_info(__time);
79+
switch (__info.result) {
80+
case local_info::unique:
81+
return sys_time<common_type_t<_Duration, seconds>>{__time.time_since_epoch() - __info.first.offset};
82+
83+
case local_info::nonexistent:
84+
chrono::__throw_nonexistent_local_time(__time, __info);
85+
86+
case local_info::ambiguous:
87+
chrono::__throw_ambiguous_local_time(__time, __info);
88+
}
89+
90+
// TODO TZDB The Standard does not specify anything in these cases.
91+
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
92+
__info.result != -1, "cannot convert the local time; it would be before the minimum system clock value");
93+
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
94+
__info.result != -2, "cannot convert the local time; it would be after the maximum system clock value");
95+
96+
return {};
97+
}
98+
7399
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI const __impl& __implementation() const noexcept { return *__impl_; }
74100

75101
private:

libcxx/include/chrono

+9
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,10 @@ const time_zone* current_zone()
724724
const tzdb& reload_tzdb(); // C++20
725725
string remote_version(); // C++20
726726
727+
// [time.zone.exception], exception classes
728+
class nonexistent_local_time; // C++20
729+
class ambiguous_local_time; // C++20
730+
727731
// [time.zone.info], information classes
728732
struct sys_info { // C++20
729733
sys_seconds begin;
@@ -766,6 +770,10 @@ class time_zone {
766770
767771
template<class Duration>
768772
local_info get_info(const local_time<Duration>& tp) const;
773+
774+
template<class Duration>
775+
sys_time<common_type_t<Duration, seconds>>
776+
to_sys(const local_time<Duration>& tp) const;
769777
};
770778
bool operator==(const time_zone& x, const time_zone& y) noexcept; // C++20
771779
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
916924
# include <__chrono/calendar.h>
917925
# include <__chrono/day.h>
918926
# include <__chrono/hh_mm_ss.h>
927+
# include <__chrono/exception.h>
919928
# include <__chrono/literals.h>
920929
# include <__chrono/local_info.h>
921930
# include <__chrono/month.h>

libcxx/include/module.modulemap

+5-1
Original file line numberDiff line numberDiff line change
@@ -1112,6 +1112,7 @@ module std_private_chrono_duration [system] {
11121112
header "__chrono/duration.h"
11131113
export std_private_type_traits_is_convertible
11141114
}
1115+
module std_private_chrono_exception [system] { header "__chrono/exception.h" }
11151116
module std_private_chrono_file_clock [system] { header "__chrono/file_clock.h" }
11161117
module std_private_chrono_formatter [system] {
11171118
header "__chrono/formatter.h"
@@ -1124,7 +1125,10 @@ module std_private_chrono_high_resolution_clock [system] {
11241125
}
11251126
module std_private_chrono_leap_second [system] { header "__chrono/leap_second.h" }
11261127
module std_private_chrono_literals [system] { header "__chrono/literals.h" }
1127-
module std_private_chrono_local_info [system] { header "__chrono/local_info.h" }
1128+
module std_private_chrono_local_info [system] {
1129+
header "__chrono/local_info.h"
1130+
export std_private_chrono_sys_info
1131+
}
11281132
module std_private_chrono_month [system] { header "__chrono/month.h" }
11291133
module std_private_chrono_month_weekday [system] { header "__chrono/month_weekday.h" }
11301134
module std_private_chrono_monthday [system] { header "__chrono/monthday.h" }

libcxx/modules/std/chrono.inc

-2
Original file line numberDiff line numberDiff line change
@@ -208,11 +208,9 @@ export namespace std {
208208
using std::chrono::reload_tzdb;
209209
using std::chrono::remote_version;
210210

211-
# if 0
212211
// [time.zone.exception], exception classes
213212
using std::chrono::ambiguous_local_time;
214213
using std::chrono::nonexistent_local_time;
215-
# endif // if 0
216214

217215
// [time.zone.info], information classes
218216
using std::chrono::local_info;

libcxx/src/CMakeLists.txt

+3
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,9 @@ if (LIBCXX_ENABLE_LOCALIZATION AND LIBCXX_ENABLE_FILESYSTEM AND LIBCXX_ENABLE_TI
339339
include/tzdb/types_private.h
340340
include/tzdb/tzdb_list_private.h
341341
include/tzdb/tzdb_private.h
342+
# TODO TZDB The exception could be moved in chrono once the TZDB library
343+
# is no longer experimental.
344+
chrono_exception.cpp
342345
time_zone.cpp
343346
tzdb.cpp
344347
tzdb_list.cpp

libcxx/src/chrono_exception.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
#include <chrono>
10+
11+
_LIBCPP_BEGIN_NAMESPACE_STD
12+
13+
namespace chrono {
14+
15+
_LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI nonexistent_local_time::~nonexistent_local_time() = default;
16+
_LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI ambiguous_local_time::~ambiguous_local_time() = default;
17+
18+
} // namespace chrono
19+
20+
_LIBCPP_END_NAMESPACE_STD
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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+
11+
// REQUIRES: has-unix-headers
12+
// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
13+
// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
14+
15+
// XFAIL: libcpp-has-no-experimental-tzdb
16+
17+
// <chrono>
18+
19+
// class ambiguous_local_time
20+
//
21+
// template<class Duration>
22+
// ambiguous_local_time(const local_time<Duration>& tp, const local_info& i);
23+
24+
#include <chrono>
25+
26+
#include "check_assertion.h"
27+
28+
// [time.zone.exception.ambig]/2
29+
// Preconditions: i.result == local_info::ambiguous is true.
30+
int main(int, char**) {
31+
TEST_LIBCPP_ASSERT_FAILURE(
32+
(std::chrono::ambiguous_local_time{
33+
std::chrono::local_seconds{},
34+
std::chrono::local_info{-1, // this is not one of the "named" result values
35+
std::chrono::sys_info{},
36+
std::chrono::sys_info{}}}),
37+
"creating an ambiguous_local_time from a local_info that is not ambiguous");
38+
39+
TEST_LIBCPP_ASSERT_FAILURE(
40+
(std::chrono::ambiguous_local_time{
41+
std::chrono::local_seconds{},
42+
std::chrono::local_info{std::chrono::local_info::unique, std::chrono::sys_info{}, std::chrono::sys_info{}}}),
43+
"creating an ambiguous_local_time from a local_info that is not ambiguous");
44+
45+
TEST_LIBCPP_ASSERT_FAILURE(
46+
(std::chrono::ambiguous_local_time{
47+
std::chrono::local_seconds{},
48+
std::chrono::local_info{
49+
std::chrono::local_info::nonexistent, std::chrono::sys_info{}, std::chrono::sys_info{}}}),
50+
"creating an ambiguous_local_time from a local_info that is not ambiguous");
51+
52+
return 0;
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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+
11+
// REQUIRES: has-unix-headers
12+
// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
13+
// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
14+
15+
// XFAIL: libcpp-has-no-experimental-tzdb
16+
17+
// <chrono>
18+
19+
// class nonexistent_local_time
20+
//
21+
// template<class Duration>
22+
// nonexistent_local_time(const local_time<Duration>& tp, const local_info& i);
23+
24+
#include <chrono>
25+
26+
#include "check_assertion.h"
27+
28+
// [time.zone.exception.nonexist]/2
29+
// Preconditions: i.result == local_info::nonexistent is true.
30+
int main(int, char**) {
31+
TEST_LIBCPP_ASSERT_FAILURE(
32+
(std::chrono::nonexistent_local_time{
33+
std::chrono::local_seconds{},
34+
std::chrono::local_info{-1, // this is not one of the "named" result values
35+
std::chrono::sys_info{},
36+
std::chrono::sys_info{}}}),
37+
"creating an nonexistent_local_time from a local_info that is not non-existent");
38+
39+
TEST_LIBCPP_ASSERT_FAILURE(
40+
(std::chrono::nonexistent_local_time{
41+
std::chrono::local_seconds{},
42+
std::chrono::local_info{std::chrono::local_info::unique, std::chrono::sys_info{}, std::chrono::sys_info{}}}),
43+
"creating an nonexistent_local_time from a local_info that is not non-existent");
44+
45+
TEST_LIBCPP_ASSERT_FAILURE(
46+
(std::chrono::nonexistent_local_time{
47+
std::chrono::local_seconds{},
48+
std::chrono::local_info{
49+
std::chrono::local_info::ambiguous, std::chrono::sys_info{}, std::chrono::sys_info{}}}),
50+
"creating an nonexistent_local_time from a local_info that is not non-existent");
51+
52+
return 0;
53+
}

0 commit comments

Comments
 (0)