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 _LIBCPP_HAS_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_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
35
36# if _LIBCPP_STD_VER >= 20
37
38namespace chrono {
39
40class nonexistent_local_time : public runtime_error {
41public:
42 template <class _Duration>
43 _LIBCPP_HIDE_FROM_ABI nonexistent_local_time(const local_time<_Duration>& __time, const local_info& __info)
44 : runtime_error{__create_message(__time, __info)} {
45 // [time.zone.exception.nonexist]/2
46 // Preconditions: i.result == local_info::nonexistent is true.
47 // The value of __info.result is not used.
48 _LIBCPP_ASSERT_PEDANTIC(__info.result == local_info::nonexistent,
49 "creating an nonexistent_local_time from a local_info that is not non-existent");
50 }
51
52 _LIBCPP_HIDE_FROM_ABI nonexistent_local_time(const nonexistent_local_time&) = default;
53 _LIBCPP_HIDE_FROM_ABI nonexistent_local_time& operator=(const nonexistent_local_time&) = default;
54
55 _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI ~nonexistent_local_time() override; // exported as key function
56
57private:
58 template <class _Duration>
59 _LIBCPP_HIDE_FROM_ABI string __create_message(const local_time<_Duration>& __time, const local_info& __info) {
60 return std::format(
61 R"({} is in a gap between
62{} {} and
63{} {} which are both equivalent to
64{} UTC)",
65 __time,
66 local_seconds{__info.first.end.time_since_epoch()} + __info.first.offset,
67 __info.first.abbrev,
68 local_seconds{__info.second.begin.time_since_epoch()} + __info.second.offset,
69 __info.second.abbrev,
70 __info.first.end);
71 }
72};
73
74template <class _Duration>
75[[noreturn]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI void __throw_nonexistent_local_time(
76 [[maybe_unused]] const local_time<_Duration>& __time, [[maybe_unused]] const local_info& __info) {
77# if _LIBCPP_HAS_EXCEPTIONS
78 throw nonexistent_local_time(__time, __info);
79# else
80 _LIBCPP_VERBOSE_ABORT("nonexistent_local_time was thrown in -fno-exceptions mode");
81# endif
82}
83
84class ambiguous_local_time : public runtime_error {
85public:
86 template <class _Duration>
87 _LIBCPP_HIDE_FROM_ABI ambiguous_local_time(const local_time<_Duration>& __time, const local_info& __info)
88 : runtime_error{__create_message(__time, __info)} {
89 // [time.zone.exception.ambig]/2
90 // Preconditions: i.result == local_info::ambiguous is true.
91 // The value of __info.result is not used.
92 _LIBCPP_ASSERT_PEDANTIC(__info.result == local_info::ambiguous,
93 "creating an ambiguous_local_time from a local_info that is not ambiguous");
94 }
95
96 _LIBCPP_HIDE_FROM_ABI ambiguous_local_time(const ambiguous_local_time&) = default;
97 _LIBCPP_HIDE_FROM_ABI ambiguous_local_time& operator=(const ambiguous_local_time&) = default;
98
99 _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI ~ambiguous_local_time() override; // exported as key function
100
101private:
102 template <class _Duration>
103 _LIBCPP_HIDE_FROM_ABI string __create_message(const local_time<_Duration>& __time, const local_info& __info) {
104 return std::format(
105 // There are two spaces after the full-stop; this has been verified
106 // in the sources of the Standard.
107 R"({0} is ambiguous. It could be
108{0} {1} == {2} UTC or
109{0} {3} == {4} UTC)",
110 __time,
111 __info.first.abbrev,
112 __time - __info.first.offset,
113 __info.second.abbrev,
114 __time - __info.second.offset);
115 }
116};
117
118template <class _Duration>
119[[noreturn]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI void __throw_ambiguous_local_time(
120 [[maybe_unused]] const local_time<_Duration>& __time, [[maybe_unused]] const local_info& __info) {
121# if _LIBCPP_HAS_EXCEPTIONS
122 throw ambiguous_local_time(__time, __info);
123# else
124 _LIBCPP_VERBOSE_ABORT("ambiguous_local_time was thrown in -fno-exceptions mode");
125# endif
126}
127
128} // namespace chrono
129
130# endif // _LIBCPP_STD_VER >= 20
131
132_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
133_LIBCPP_END_NAMESPACE_STD
134
135#endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
136
137#endif // _LIBCPP___CHRONO_EXCEPTION_H
138