Skip to content

Commit 9a052b4

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 de736d9 commit 9a052b4

File tree

23 files changed

+1027
-70
lines changed

23 files changed

+1027
-70
lines changed

libcxx/include/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ set(files
250250
__chrono/convert_to_tm.h
251251
__chrono/day.h
252252
__chrono/duration.h
253+
__chrono/exception.h
253254
__chrono/file_clock.h
254255
__chrono/formatter.h
255256
__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 <__chrono/calendar.h>
20+
# include <__chrono/local_info.h>
21+
# include <__chrono/time_point.h>
22+
# include <__config>
23+
# include <__configuration/availability.h>
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

+27
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,31 @@ class _LIBCPP_AVAILABILITY_TZDB time_zone {
7072
return __get_info(chrono::time_point_cast<seconds>(__time));
7173
}
7274

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

75102
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
@@ -915,6 +923,7 @@ constexpr chrono::year operator ""y(unsigned lo
915923
#if _LIBCPP_STD_VER >= 20
916924
# include <__chrono/calendar.h>
917925
# include <__chrono/day.h>
926+
# include <__chrono/exception.h>
918927
# include <__chrono/hh_mm_ss.h>
919928
# include <__chrono/literals.h>
920929
# include <__chrono/local_info.h>

libcxx/include/module.modulemap

+5-1
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,7 @@ module std_private_chrono_duration [system] {
11011101
header "__chrono/duration.h"
11021102
export std_private_type_traits_is_convertible
11031103
}
1104+
module std_private_chrono_exception [system] { header "__chrono/exception.h" }
11041105
module std_private_chrono_file_clock [system] { header "__chrono/file_clock.h" }
11051106
module std_private_chrono_formatter [system] {
11061107
header "__chrono/formatter.h"
@@ -1113,7 +1114,10 @@ module std_private_chrono_high_resolution_clock [system] {
11131114
}
11141115
module std_private_chrono_leap_second [system] { header "__chrono/leap_second.h" }
11151116
module std_private_chrono_literals [system] { header "__chrono/literals.h" }
1116-
module std_private_chrono_local_info [system] { header "__chrono/local_info.h" }
1117+
module std_private_chrono_local_info [system] {
1118+
header "__chrono/local_info.h"
1119+
export std_private_chrono_sys_info
1120+
}
11171121
module std_private_chrono_month [system] { header "__chrono/month.h" }
11181122
module std_private_chrono_month_weekday [system] { header "__chrono/month_weekday.h" }
11191123
module std_private_chrono_monthday [system] { header "__chrono/monthday.h" }

libcxx/modules/std/chrono.inc

+3-4
Original file line numberDiff line numberDiff line change
@@ -209,13 +209,12 @@ export namespace std {
209209
using std::chrono::reload_tzdb;
210210
using std::chrono::remote_version;
211211

212-
# if 0
212+
# endif // !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) &&
213+
// !defined(_LIBCPP_HAS_NO_LOCALIZATION)
214+
213215
// [time.zone.exception], exception classes
214216
using std::chrono::ambiguous_local_time;
215217
using std::chrono::nonexistent_local_time;
216-
# endif // if 0
217-
# endif // !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) &&
218-
// !defined(_LIBCPP_HAS_NO_LOCALIZATION)
219218

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

libcxx/src/CMakeLists.txt

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

libcxx/src/chrono_exception.cpp

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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
16+
_LIBCPP_EXPORTED_FROM_ABI nonexistent_local_time::~nonexistent_local_time() = default; // key function
17+
_LIBCPP_AVAILABILITY_TZDB
18+
_LIBCPP_EXPORTED_FROM_ABI ambiguous_local_time::~ambiguous_local_time() = default; // key function
19+
20+
} // namespace chrono
21+
22+
_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)