Skip to content

Commit 8a21d59

Browse files
authored
[libc++][TZDB] Adds local_info formatter. (#86256)
This adds the local_info type and its formatting options. The usage of the local_info object will be done in separate patches. Implements parts of: - P0355 Extending to Calendars and Time Zones - P1361 Integration of chrono with text formatting
1 parent af0b69f commit 8a21d59

File tree

15 files changed

+467
-2
lines changed

15 files changed

+467
-2
lines changed

libcxx/docs/Status/FormatPaper.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
2525
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::year_month_weekday_last``",,Mark de Wever,|Complete|,16.0
2626
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::hh_mm_ss<duration<Rep, Period>>``",,Mark de Wever,|Complete|,17.0
2727
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::sys_info``",,Mark de Wever,|Complete|,19.0
28-
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local_info``",A ``<chrono>`` implementation,Mark de Wever,,
28+
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local_info``",,Mark de Wever,|Complete|,19.0
2929
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::zoned_time<Duration, TimeZonePtr>``",A ``<chrono>`` implementation,Mark de Wever,,
3030

3131
"`P2693R1 <https://wg21.link/P2693R1>`__","Formatting ``thread::id`` and ``stacktrace``"

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ set(files
270270
__chrono/high_resolution_clock.h
271271
__chrono/leap_second.h
272272
__chrono/literals.h
273+
__chrono/local_info.h
273274
__chrono/month.h
274275
__chrono/month_weekday.h
275276
__chrono/monthday.h

libcxx/include/__chrono/convert_to_tm.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <__chrono/duration.h>
1717
#include <__chrono/file_clock.h>
1818
#include <__chrono/hh_mm_ss.h>
19+
#include <__chrono/local_info.h>
1920
#include <__chrono/month.h>
2021
#include <__chrono/month_weekday.h>
2122
#include <__chrono/monthday.h>
@@ -175,6 +176,8 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoT& __value) {
175176
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
176177
} else if constexpr (same_as<_ChronoT, chrono::sys_info>) {
177178
// Has no time information.
179+
} else if constexpr (same_as<_ChronoT, chrono::local_info>) {
180+
// Has no time information.
178181
# endif
179182
} else
180183
static_assert(sizeof(_ChronoT) == 0, "Add the missing type specialization");

libcxx/include/__chrono/formatter.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <__chrono/duration.h>
1919
#include <__chrono/file_clock.h>
2020
#include <__chrono/hh_mm_ss.h>
21+
#include <__chrono/local_info.h>
2122
#include <__chrono/month.h>
2223
#include <__chrono/month_weekday.h>
2324
#include <__chrono/monthday.h>
@@ -419,6 +420,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_ok(const _Tp& __value) {
419420
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
420421
else if constexpr (same_as<_Tp, chrono::sys_info>)
421422
return true;
423+
else if constexpr (same_as<_Tp, chrono::local_info>)
424+
return true;
422425
# endif
423426
else
424427
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
@@ -463,6 +466,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_name_ok(const _Tp& __value) {
463466
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
464467
else if constexpr (same_as<_Tp, chrono::sys_info>)
465468
return true;
469+
else if constexpr (same_as<_Tp, chrono::local_info>)
470+
return true;
466471
# endif
467472
else
468473
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
@@ -507,6 +512,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __date_ok(const _Tp& __value) {
507512
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
508513
else if constexpr (same_as<_Tp, chrono::sys_info>)
509514
return true;
515+
else if constexpr (same_as<_Tp, chrono::local_info>)
516+
return true;
510517
# endif
511518
else
512519
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
@@ -551,6 +558,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __month_name_ok(const _Tp& __value) {
551558
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
552559
else if constexpr (same_as<_Tp, chrono::sys_info>)
553560
return true;
561+
else if constexpr (same_as<_Tp, chrono::local_info>)
562+
return true;
554563
# endif
555564
else
556565
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
@@ -893,6 +902,17 @@ struct formatter<chrono::sys_info, _CharT> : public __formatter_chrono<_CharT> {
893902
return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time_zone);
894903
}
895904
};
905+
906+
template <__fmt_char_type _CharT>
907+
struct formatter<chrono::local_info, _CharT> : public __formatter_chrono<_CharT> {
908+
public:
909+
using _Base = __formatter_chrono<_CharT>;
910+
911+
template <class _ParseContext>
912+
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
913+
return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags{});
914+
}
915+
};
896916
# endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
897917

898918
#endif // if _LIBCPP_STD_VER >= 20

libcxx/include/__chrono/local_info.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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_LOCAL_INFO_H
13+
#define _LIBCPP___CHRONO_LOCAL_INFO_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_INCOMPLETE_TZDB)
18+
19+
# include <__chrono/sys_info.h>
20+
# include <__config>
21+
22+
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
23+
# pragma GCC system_header
24+
# endif
25+
26+
_LIBCPP_BEGIN_NAMESPACE_STD
27+
28+
# if _LIBCPP_STD_VER >= 20
29+
30+
namespace chrono {
31+
32+
struct local_info {
33+
static constexpr int unique = 0;
34+
static constexpr int nonexistent = 1;
35+
static constexpr int ambiguous = 2;
36+
37+
int result;
38+
sys_info first;
39+
sys_info second;
40+
};
41+
42+
} // namespace chrono
43+
44+
# endif // _LIBCPP_STD_VER >= 20
45+
46+
_LIBCPP_END_NAMESPACE_STD
47+
48+
#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
49+
50+
#endif // _LIBCPP___CHRONO_LOCAL_INFO_H

libcxx/include/__chrono/ostream.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <__chrono/duration.h>
1616
#include <__chrono/file_clock.h>
1717
#include <__chrono/hh_mm_ss.h>
18+
#include <__chrono/local_info.h>
1819
#include <__chrono/month.h>
1920
#include <__chrono/month_weekday.h>
2021
#include <__chrono/monthday.h>
@@ -280,6 +281,27 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_info& __info) {
280281
__abbrev);
281282
}
282283

284+
template <class _CharT, class _Traits>
285+
_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
286+
operator<<(basic_ostream<_CharT, _Traits>& __os, const local_info& __info) {
287+
auto __result = [&]() -> basic_string<_CharT> {
288+
switch (__info.result) {
289+
case local_info::unique:
290+
return _LIBCPP_STATICALLY_WIDEN(_CharT, "unique");
291+
case local_info::nonexistent:
292+
return _LIBCPP_STATICALLY_WIDEN(_CharT, "non-existent");
293+
case local_info::ambiguous:
294+
return _LIBCPP_STATICALLY_WIDEN(_CharT, "ambiguous");
295+
296+
default:
297+
return std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "unspecified result ({})"), __info.result);
298+
};
299+
};
300+
301+
return __os << std::format(
302+
_LIBCPP_STATICALLY_WIDEN(_CharT, "{}: {{{}, {}}}"), __result(), __info.first, __info.second);
303+
}
304+
283305
# endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
284306

285307
} // namespace chrono

libcxx/include/chrono

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,20 @@ template<class charT, class traits>
737737
basic_ostream<charT, traits>&
738738
operator<<(basic_ostream<charT, traits>& os, const sys_info& si);
739739
740+
struct local_info { // C++20
741+
static constexpr int unique = 0;
742+
static constexpr int nonexistent = 1;
743+
static constexpr int ambiguous = 2;
744+
745+
int result;
746+
sys_info first;
747+
sys_info second;
748+
};
749+
750+
template<class charT, class traits> // C++20
751+
basic_ostream<charT, traits>&
752+
operator<<(basic_ostream<charT, traits>& os, const local_info& li);
753+
740754
// 25.10.5, class time_zone // C++20
741755
enum class choose {earliest, latest};
742756
class time_zone {
@@ -834,6 +848,7 @@ namespace std {
834848
template<class Rep, class Period, class charT>
835849
struct formatter<chrono::hh_mm_ss<duration<Rep, Period>>, charT>; // C++20
836850
template<class charT> struct formatter<chrono::sys_info, charT>; // C++20
851+
template<class charT> struct formatter<chrono::local_info, charT>; // C++20
837852
} // namespace std
838853
839854
namespace chrono {
@@ -899,6 +914,7 @@ constexpr chrono::year operator ""y(unsigned lo
899914
# include <__chrono/day.h>
900915
# include <__chrono/hh_mm_ss.h>
901916
# include <__chrono/literals.h>
917+
# include <__chrono/local_info.h>
902918
# include <__chrono/month.h>
903919
# include <__chrono/month_weekday.h>
904920
# include <__chrono/monthday.h>

libcxx/include/libcxx.imp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@
267267
{ include: [ "<__chrono/high_resolution_clock.h>", "private", "<chrono>", "public" ] },
268268
{ include: [ "<__chrono/leap_second.h>", "private", "<chrono>", "public" ] },
269269
{ include: [ "<__chrono/literals.h>", "private", "<chrono>", "public" ] },
270+
{ include: [ "<__chrono/local_info.h>", "private", "<chrono>", "public" ] },
270271
{ include: [ "<__chrono/month.h>", "private", "<chrono>", "public" ] },
271272
{ include: [ "<__chrono/month_weekday.h>", "private", "<chrono>", "public" ] },
272273
{ include: [ "<__chrono/monthday.h>", "private", "<chrono>", "public" ] },

libcxx/include/module.modulemap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,7 @@ module std_private_chrono_high_resolution_clock [system] {
11241124
}
11251125
module std_private_chrono_leap_second [system] { header "__chrono/leap_second.h" }
11261126
module std_private_chrono_literals [system] { header "__chrono/literals.h" }
1127+
module std_private_chrono_local_info [system] { header "__chrono/local_info.h" }
11271128
module std_private_chrono_month [system] { header "__chrono/month.h" }
11281129
module std_private_chrono_month_weekday [system] { header "__chrono/month_weekday.h" }
11291130
module std_private_chrono_monthday [system] { header "__chrono/monthday.h" }

libcxx/modules/std/chrono.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ export namespace std {
215215
# endif // if 0
216216

217217
// [time.zone.info], information classes
218+
using std::chrono::local_info;
218219
using std::chrono::sys_info;
219220

220221
# if 0
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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-localization
11+
12+
// TODO FMT This test should not require std::to_chars(floating-point)
13+
// XFAIL: availability-fp_to_chars-missing
14+
15+
// XFAIL: libcpp-has-no-incomplete-tzdb
16+
17+
// <chrono>
18+
19+
// template<class charT, class traits>
20+
// basic_ostream<charT, traits>&
21+
// operator<<(basic_ostream<charT, traits>& os, const local_info& r);
22+
23+
// [time.zone.info.local]
24+
// 7 Effects: Streams out the local_info object r in an unspecified format.
25+
// 8 Returns: os.
26+
//
27+
// Tests the output produced by this function.
28+
29+
#include <cassert>
30+
#include <chrono>
31+
#include <memory>
32+
#include <sstream>
33+
34+
#include "assert_macros.h"
35+
#include "test_macros.h"
36+
#include "make_string.h"
37+
#include "concat_macros.h"
38+
39+
#define SV(S) MAKE_STRING_VIEW(CharT, S)
40+
41+
template <class CharT>
42+
static void test(std::basic_string_view<CharT> expected, std::chrono::local_info&& info) {
43+
std::basic_stringstream<CharT> sstr;
44+
sstr << info;
45+
std::basic_string<CharT> output = sstr.str();
46+
47+
TEST_REQUIRE(expected == output,
48+
TEST_WRITE_CONCATENATED("\nExpected output ", expected, "\nActual output ", output, '\n'));
49+
}
50+
51+
template <class CharT>
52+
static void test() {
53+
using namespace std::literals::chrono_literals;
54+
namespace tz = std::chrono;
55+
// result values matching the "known" results
56+
test(SV("unique: "
57+
"{[-10484-10-16 15:30:08, 14423-03-17 15:30:07) 00:00:00 0min \"TZ\", "
58+
"[1970-01-01 00:00:00, 1970-01-01 00:00:00) 00:00:00 0min \"\"}"),
59+
tz::local_info{tz::local_info::unique,
60+
tz::sys_info{tz::sys_seconds::min(), tz::sys_seconds::max(), 0s, 0min, "TZ"},
61+
tz::sys_info{}});
62+
63+
test(SV("non-existent: "
64+
"{[1970-01-01 00:00:00, 2038-12-31 00:00:00) 12:23:45 -67min \"NEG\", "
65+
"[1970-01-01 00:00:00, 2038-12-31 00:00:00) -12:23:45 67min \"POS\"}"),
66+
tz::local_info{
67+
tz::local_info::nonexistent,
68+
tz::sys_info{static_cast<tz::sys_days>(tz::year_month_day{1970y, tz::January, 1d}),
69+
static_cast<tz::sys_days>(tz::year_month_day{2038y, tz::December, 31d}),
70+
12h + 23min + 45s,
71+
-67min,
72+
"NEG"},
73+
tz::sys_info{static_cast<tz::sys_days>(tz::year_month_day{1970y, tz::January, 1d}),
74+
static_cast<tz::sys_days>(tz::year_month_day{2038y, tz::December, 31d}),
75+
-(12h + 23min + 45s),
76+
67min,
77+
"POS"}});
78+
79+
test(SV("ambiguous: "
80+
"{[1970-01-01 00:00:00, 2038-12-31 00:00:00) 12:23:45 -67min \"NEG\", "
81+
"[1970-01-01 00:00:00, 2038-12-31 00:00:00) -12:23:45 67min \"POS\"}"),
82+
tz::local_info{
83+
tz::local_info::ambiguous,
84+
tz::sys_info{static_cast<tz::sys_days>(tz::year_month_day{1970y, tz::January, 1d}),
85+
static_cast<tz::sys_days>(tz::year_month_day{2038y, tz::December, 31d}),
86+
12h + 23min + 45s,
87+
-67min,
88+
"NEG"},
89+
tz::sys_info{static_cast<tz::sys_days>(tz::year_month_day{1970y, tz::January, 1d}),
90+
static_cast<tz::sys_days>(tz::year_month_day{2038y, tz::December, 31d}),
91+
-(12h + 23min + 45s),
92+
67min,
93+
"POS"}});
94+
95+
// result values not matching the "known" results
96+
test(
97+
SV("unspecified result (-1): "
98+
"{[-10484-10-16 15:30:08, 14423-03-17 15:30:07) 00:00:00 0min \"TZ\", "
99+
"[1970-01-01 00:00:00, 1970-01-01 00:00:00) 00:00:00 0min \"\"}"),
100+
tz::local_info{-1, tz::sys_info{tz::sys_seconds::min(), tz::sys_seconds::max(), 0s, 0min, "TZ"}, tz::sys_info{}});
101+
test(SV("unspecified result (3): "
102+
"{[-10484-10-16 15:30:08, 14423-03-17 15:30:07) 00:00:00 0min \"TZ\", "
103+
"[1970-01-01 00:00:00, 1970-01-01 00:00:00) 00:00:00 0min \"\"}"),
104+
tz::local_info{3, tz::sys_info{tz::sys_seconds::min(), tz::sys_seconds::max(), 0s, 0min, "TZ"}, tz::sys_info{}});
105+
}
106+
107+
int main(int, const char**) {
108+
test<char>();
109+
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
110+
test<wchar_t>();
111+
#endif
112+
113+
return 0;
114+
}

0 commit comments

Comments
 (0)