Skip to content

[libc++][TZDB] Adds sys_info formatter. #85896

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libcxx/docs/Status/FormatPaper.csv
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::year_month_weekday``",,Mark de Wever,|Complete|,16.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::year_month_weekday_last``",,Mark de Wever,|Complete|,16.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::hh_mm_ss<duration<Rep, Period>>``",,Mark de Wever,|Complete|,17.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::sys_info``",A ``<chrono>`` implementation,Mark de Wever,,
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::sys_info``",,Mark de Wever,|Complete|,19.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local_info``",A ``<chrono>`` implementation,Mark de Wever,,
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::zoned_time<Duration, TimeZonePtr>``",A ``<chrono>`` implementation,Mark de Wever,,

Expand Down
5 changes: 5 additions & 0 deletions libcxx/include/__chrono/convert_to_tm.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <__chrono/month_weekday.h>
#include <__chrono/monthday.h>
#include <__chrono/statically_widen.h>
#include <__chrono/sys_info.h>
#include <__chrono/system_clock.h>
#include <__chrono/time_point.h>
#include <__chrono/weekday.h>
Expand Down Expand Up @@ -171,6 +172,10 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoT& __value) {
if (__value.hours().count() > std::numeric_limits<decltype(__result.tm_hour)>::max())
std::__throw_format_error("Formatting hh_mm_ss, encountered an hour overflow");
__result.tm_hour = __value.hours().count();
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
} else if constexpr (same_as<_ChronoT, chrono::sys_info>) {
// Has no time information.
# endif
} else
static_assert(sizeof(_ChronoT) == 0, "Add the missing type specialization");

Expand Down
45 changes: 41 additions & 4 deletions libcxx/include/__chrono/formatter.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <__chrono/ostream.h>
#include <__chrono/parser_std_format_spec.h>
#include <__chrono/statically_widen.h>
#include <__chrono/sys_info.h>
#include <__chrono/system_clock.h>
#include <__chrono/time_point.h>
#include <__chrono/weekday.h>
Expand Down Expand Up @@ -186,10 +187,11 @@ __format_zone_offset(basic_stringstream<_CharT>& __sstr, chrono::seconds __offse

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

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

template <class _Tp>
_LIBCPP_HIDE_FROM_ABI __time_zone __convert_to_time_zone([[maybe_unused]] const _Tp& __value) {
return {"UTC", chrono::seconds{0}};
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
if constexpr (same_as<_Tp, chrono::sys_info>)
return {__value.abbrev, __value.offset};
else
# endif
return {"UTC", chrono::seconds{0}};
}

template <class _CharT, class _Tp>
Expand Down Expand Up @@ -409,6 +416,10 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_ok(const _Tp& __value) {
return __value.weekday().ok();
else if constexpr (__is_hh_mm_ss<_Tp>)
return true;
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
else if constexpr (same_as<_Tp, chrono::sys_info>)
return true;
# endif
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
}
Expand Down Expand Up @@ -449,6 +460,10 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_name_ok(const _Tp& __value) {
return __value.weekday().ok();
else if constexpr (__is_hh_mm_ss<_Tp>)
return true;
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
else if constexpr (same_as<_Tp, chrono::sys_info>)
return true;
# endif
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
}
Expand Down Expand Up @@ -489,6 +504,10 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __date_ok(const _Tp& __value) {
return __value.ok();
else if constexpr (__is_hh_mm_ss<_Tp>)
return true;
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
else if constexpr (same_as<_Tp, chrono::sys_info>)
return true;
# endif
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
}
Expand Down Expand Up @@ -529,6 +548,10 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __month_name_ok(const _Tp& __value) {
return __value.month().ok();
else if constexpr (__is_hh_mm_ss<_Tp>)
return true;
# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
else if constexpr (same_as<_Tp, chrono::sys_info>)
return true;
# endif
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
}
Expand Down Expand Up @@ -858,6 +881,20 @@ struct formatter<chrono::hh_mm_ss<_Duration>, _CharT> : public __formatter_chron
return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time);
}
};

# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
template <__fmt_char_type _CharT>
struct formatter<chrono::sys_info, _CharT> : public __formatter_chrono<_CharT> {
public:
using _Base = __formatter_chrono<_CharT>;

template <class _ParseContext>
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time_zone);
}
};
# endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)

#endif // if _LIBCPP_STD_VER >= 20

_LIBCPP_END_NAMESPACE_STD
Expand Down
20 changes: 20 additions & 0 deletions libcxx/include/__chrono/ostream.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <__chrono/month_weekday.h>
#include <__chrono/monthday.h>
#include <__chrono/statically_widen.h>
#include <__chrono/sys_info.h>
#include <__chrono/system_clock.h>
#include <__chrono/weekday.h>
#include <__chrono/year.h>
Expand Down Expand Up @@ -262,6 +263,25 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const hh_mm_ss<_Duration> __hms
return __os << std::format(__os.getloc(), _LIBCPP_STATICALLY_WIDEN(_CharT, "{:L%T}"), __hms);
}

# if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)

template <class _CharT, class _Traits>
_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_info& __info) {
// __info.abbrev is always std::basic_string<char>.
// Since these strings typically are short the conversion should be cheap.
std::basic_string<_CharT> __abbrev{__info.abbrev.begin(), __info.abbrev.end()};
return __os << std::format(
_LIBCPP_STATICALLY_WIDEN(_CharT, "[{:%F %T}, {:%F %T}) {:%T} {:%Q%q} \"{}\""),
__info.begin,
__info.end,
hh_mm_ss{__info.offset},
__info.save,
__abbrev);
}

# endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)

} // namespace chrono

#endif // if _LIBCPP_STD_VER >= 20
Expand Down
2 changes: 1 addition & 1 deletion libcxx/include/__chrono/sys_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ struct sys_info {

} // namespace chrono

# endif //_LIBCPP_STD_VER >= 20
# endif // _LIBCPP_STD_VER >= 20

_LIBCPP_END_NAMESPACE_STD

Expand Down
5 changes: 5 additions & 0 deletions libcxx/include/chrono
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,10 @@ struct sys_info {
string abbrev;
};

template<class charT, class traits> // C++20
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>& os, const sys_info& si);

// 25.10.5, class time_zone // C++20
enum class choose {earliest, latest};
class time_zone {
Expand Down Expand Up @@ -829,6 +833,7 @@ namespace std {
template<class charT> struct formatter<chrono::year_month_weekday_last, charT>; // C++20
template<class Rep, class Period, class charT>
struct formatter<chrono::hh_mm_ss<duration<Rep, Period>>, charT>; // C++20
template<class charT> struct formatter<chrono::sys_info, charT>; // C++20
} // namespace std

namespace chrono {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: no-localization

// TODO FMT This test should not require std::to_chars(floating-point)
// XFAIL: availability-fp_to_chars-missing

// XFAIL: libcpp-has-no-incomplete-tzdb

// <chrono>

// template<class charT, class traits>
// basic_ostream<charT, traits>&
// operator<<(basic_ostream<charT, traits>& os, const sys_info& r);

// [time.zone.info.sys]
// 7 Effects: Streams out the sys_info object r in an unspecified format.
// 8 Returns: os.
//
// Tests the output produced by this function.

#include <cassert>
#include <chrono>
#include <memory>
#include <sstream>

#include "assert_macros.h"
#include "test_macros.h"
#include "make_string.h"
#include "concat_macros.h"

#define SV(S) MAKE_STRING_VIEW(CharT, S)

template <class CharT>
static void test(std::basic_string_view<CharT> expected, std::chrono::sys_info&& info) {
std::basic_stringstream<CharT> sstr;
sstr << info;
std::basic_string<CharT> output = sstr.str();

TEST_REQUIRE(expected == output,
TEST_WRITE_CONCATENATED("\nExpected output ", expected, "\nActual output ", output, '\n'));
}

template <class CharT>
static void test() {
using namespace std::literals::chrono_literals;
namespace tz = std::chrono;

test(SV("[-10484-10-16 15:30:08, 14423-03-17 15:30:07) 00:00:00 0min \"TZ\""),
tz::sys_info{tz::sys_seconds::min(), tz::sys_seconds::max(), 0s, 0min, "TZ"});

test(SV("[1970-01-01 00:00:00, 2038-12-31 00:00:00) 12:23:45 -67min \"DMY\""),
tz::sys_info{static_cast<tz::sys_days>(tz::year_month_day{1970y, tz::January, 1d}),
static_cast<tz::sys_days>(tz::year_month_day{2038y, tz::December, 31d}),
12h + 23min + 45s,
-67min,
"DMY"});
}

int main(int, const char**) {
test<char>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test<wchar_t>();
#endif

return 0;
}
Loading