Skip to content

Commit 6f7976c

Browse files
authored
[libc++][TZDB] Adds sys_info formatter. (#85896)
Implements parts of: - P0355 Extending <chrono> to Calendars and Time Zones - P1361 Integration of chrono with text formatting
1 parent 800f105 commit 6f7976c

File tree

11 files changed

+347
-7
lines changed

11 files changed

+347
-7
lines changed

libcxx/docs/Status/FormatPaper.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
2424
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::year_month_weekday``",,Mark de Wever,|Complete|,16.0
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
27-
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::sys_info``",A ``<chrono>`` implementation,Mark de Wever,,
27+
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::sys_info``",,Mark de Wever,|Complete|,19.0
2828
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local_info``",A ``<chrono>`` implementation,Mark de Wever,,
2929
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::zoned_time<Duration, TimeZonePtr>``",A ``<chrono>`` implementation,Mark de Wever,,
3030

libcxx/include/__chrono/convert_to_tm.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <__chrono/month_weekday.h>
2121
#include <__chrono/monthday.h>
2222
#include <__chrono/statically_widen.h>
23+
#include <__chrono/sys_info.h>
2324
#include <__chrono/system_clock.h>
2425
#include <__chrono/time_point.h>
2526
#include <__chrono/weekday.h>
@@ -171,6 +172,10 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoT& __value) {
171172
if (__value.hours().count() > std::numeric_limits<decltype(__result.tm_hour)>::max())
172173
std::__throw_format_error("Formatting hh_mm_ss, encountered an hour overflow");
173174
__result.tm_hour = __value.hours().count();
175+
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
176+
} else if constexpr (same_as<_ChronoT, chrono::sys_info>) {
177+
// Has no time information.
178+
# endif
174179
} else
175180
static_assert(sizeof(_ChronoT) == 0, "Add the missing type specialization");
176181

libcxx/include/__chrono/formatter.h

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <__chrono/ostream.h>
2525
#include <__chrono/parser_std_format_spec.h>
2626
#include <__chrono/statically_widen.h>
27+
#include <__chrono/sys_info.h>
2728
#include <__chrono/system_clock.h>
2829
#include <__chrono/time_point.h>
2930
#include <__chrono/weekday.h>
@@ -186,10 +187,11 @@ __format_zone_offset(basic_stringstream<_CharT>& __sstr, chrono::seconds __offse
186187

187188
chrono::hh_mm_ss __hms{__offset};
188189
std::ostreambuf_iterator<_CharT> __out_it{__sstr};
190+
// Note HMS does not allow formatting hours > 23, but the offset is not limited to 24H.
191+
std::format_to(__out_it, _LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), __hms.hours().count());
189192
if (__modifier)
190-
std::format_to(__out_it, _LIBCPP_STATICALLY_WIDEN(_CharT, "{:%H:%M}"), __hms);
191-
else
192-
std::format_to(__out_it, _LIBCPP_STATICALLY_WIDEN(_CharT, "{:%H%M}"), __hms);
193+
__sstr << _CharT(':');
194+
std::format_to(__out_it, _LIBCPP_STATICALLY_WIDEN(_CharT, "{:02}"), __hms.minutes().count());
193195
}
194196

195197
// Helper to store the time zone information needed for formatting.
@@ -202,7 +204,12 @@ struct _LIBCPP_HIDE_FROM_ABI __time_zone {
202204

203205
template <class _Tp>
204206
_LIBCPP_HIDE_FROM_ABI __time_zone __convert_to_time_zone([[maybe_unused]] const _Tp& __value) {
205-
return {"UTC", chrono::seconds{0}};
207+
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
208+
if constexpr (same_as<_Tp, chrono::sys_info>)
209+
return {__value.abbrev, __value.offset};
210+
else
211+
# endif
212+
return {"UTC", chrono::seconds{0}};
206213
}
207214

208215
template <class _CharT, class _Tp>
@@ -409,6 +416,10 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_ok(const _Tp& __value) {
409416
return __value.weekday().ok();
410417
else if constexpr (__is_hh_mm_ss<_Tp>)
411418
return true;
419+
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
420+
else if constexpr (same_as<_Tp, chrono::sys_info>)
421+
return true;
422+
# endif
412423
else
413424
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
414425
}
@@ -449,6 +460,10 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_name_ok(const _Tp& __value) {
449460
return __value.weekday().ok();
450461
else if constexpr (__is_hh_mm_ss<_Tp>)
451462
return true;
463+
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
464+
else if constexpr (same_as<_Tp, chrono::sys_info>)
465+
return true;
466+
# endif
452467
else
453468
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
454469
}
@@ -489,6 +504,10 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __date_ok(const _Tp& __value) {
489504
return __value.ok();
490505
else if constexpr (__is_hh_mm_ss<_Tp>)
491506
return true;
507+
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
508+
else if constexpr (same_as<_Tp, chrono::sys_info>)
509+
return true;
510+
# endif
492511
else
493512
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
494513
}
@@ -529,6 +548,10 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __month_name_ok(const _Tp& __value) {
529548
return __value.month().ok();
530549
else if constexpr (__is_hh_mm_ss<_Tp>)
531550
return true;
551+
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
552+
else if constexpr (same_as<_Tp, chrono::sys_info>)
553+
return true;
554+
# endif
532555
else
533556
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
534557
}
@@ -858,6 +881,20 @@ struct formatter<chrono::hh_mm_ss<_Duration>, _CharT> : public __formatter_chron
858881
return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time);
859882
}
860883
};
884+
885+
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
886+
template <__fmt_char_type _CharT>
887+
struct formatter<chrono::sys_info, _CharT> : public __formatter_chrono<_CharT> {
888+
public:
889+
using _Base = __formatter_chrono<_CharT>;
890+
891+
template <class _ParseContext>
892+
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
893+
return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time_zone);
894+
}
895+
};
896+
# endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
897+
861898
#endif // if _LIBCPP_STD_VER >= 20
862899

863900
_LIBCPP_END_NAMESPACE_STD

libcxx/include/__chrono/ostream.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <__chrono/month_weekday.h>
2020
#include <__chrono/monthday.h>
2121
#include <__chrono/statically_widen.h>
22+
#include <__chrono/sys_info.h>
2223
#include <__chrono/system_clock.h>
2324
#include <__chrono/weekday.h>
2425
#include <__chrono/year.h>
@@ -262,6 +263,25 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const hh_mm_ss<_Duration> __hms
262263
return __os << std::format(__os.getloc(), _LIBCPP_STATICALLY_WIDEN(_CharT, "{:L%T}"), __hms);
263264
}
264265

266+
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
267+
268+
template <class _CharT, class _Traits>
269+
_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
270+
operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_info& __info) {
271+
// __info.abbrev is always std::basic_string<char>.
272+
// Since these strings typically are short the conversion should be cheap.
273+
std::basic_string<_CharT> __abbrev{__info.abbrev.begin(), __info.abbrev.end()};
274+
return __os << std::format(
275+
_LIBCPP_STATICALLY_WIDEN(_CharT, "[{:%F %T}, {:%F %T}) {:%T} {:%Q%q} \"{}\""),
276+
__info.begin,
277+
__info.end,
278+
hh_mm_ss{__info.offset},
279+
__info.save,
280+
__abbrev);
281+
}
282+
283+
# endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
284+
265285
} // namespace chrono
266286

267287
#endif // if _LIBCPP_STD_VER >= 20

libcxx/include/__chrono/sys_info.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ struct sys_info {
4242

4343
} // namespace chrono
4444

45-
# endif //_LIBCPP_STD_VER >= 20
45+
# endif // _LIBCPP_STD_VER >= 20
4646

4747
_LIBCPP_END_NAMESPACE_STD
4848

libcxx/include/chrono

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,10 @@ struct sys_info {
733733
string abbrev;
734734
};
735735
736+
template<class charT, class traits> // C++20
737+
basic_ostream<charT, traits>&
738+
operator<<(basic_ostream<charT, traits>& os, const sys_info& si);
739+
736740
// 25.10.5, class time_zone // C++20
737741
enum class choose {earliest, latest};
738742
class time_zone {
@@ -829,6 +833,7 @@ namespace std {
829833
template<class charT> struct formatter<chrono::year_month_weekday_last, charT>; // C++20
830834
template<class Rep, class Period, class charT>
831835
struct formatter<chrono::hh_mm_ss<duration<Rep, Period>>, charT>; // C++20
836+
template<class charT> struct formatter<chrono::sys_info, charT>; // C++20
832837
} // namespace std
833838
834839
namespace chrono {
Lines changed: 74 additions & 0 deletions
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-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 sys_info& r);
22+
23+
// [time.zone.info.sys]
24+
// 7 Effects: Streams out the sys_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::sys_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+
56+
test(SV("[-10484-10-16 15:30:08, 14423-03-17 15:30:07) 00:00:00 0min \"TZ\""),
57+
tz::sys_info{tz::sys_seconds::min(), tz::sys_seconds::max(), 0s, 0min, "TZ"});
58+
59+
test(SV("[1970-01-01 00:00:00, 2038-12-31 00:00:00) 12:23:45 -67min \"DMY\""),
60+
tz::sys_info{static_cast<tz::sys_days>(tz::year_month_day{1970y, tz::January, 1d}),
61+
static_cast<tz::sys_days>(tz::year_month_day{2038y, tz::December, 31d}),
62+
12h + 23min + 45s,
63+
-67min,
64+
"DMY"});
65+
}
66+
67+
int main(int, const char**) {
68+
test<char>();
69+
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
70+
test<wchar_t>();
71+
#endif
72+
73+
return 0;
74+
}

0 commit comments

Comments
 (0)