This patch change implementation of the formatters for time points and hh_mm_ss, so they no longer delegate to operator<< for ostream in case of empty chrono-spec. As in case of calendar types, the formatters for specific type now provide __formatter_chrono with default _ChronoSpec that are used in case if empty chron-spec.
The configuration of __defSpec is straight forward, expect for the sys_time, and local_time that print time, if the duration is convertible to days, which is equivalent to setting _M_chrono_specs "%F" instead of "%F %T". Furthermore, certain sys_time<Dur> do not support ostream operator, and should not be formattable with empty spec - in such case default _M_chron_spec, allowing the issue to still be detected in _M_parse. Finally, _ChronoFormats are extended to cover required format strings. libstdc++-v3/ChangeLog: * include/bits/chrono_io.h (_ChronoFormats::_S_ftz) (_ChronoFormats::_S_ft, _ChronoFormats::_S_t): Define. (__formatter_chrono::_M_format_to_ostream): Remove handling for time_points. (std::formatter<chrono::hh_mm_ss<_Dur>, _CharT>) (std::formatter<chrono::sys_time<_Dur>, _CharT>) (std::formatter<chrono::utc_time<_Dur>, _CharT>) (std::formatter<chrono::tai_time<_Dur>, _CharT>) (std::formatter<chrono::gps_time<_Dur>, _CharT>) (std::formatter<chrono::file_time<_Dur>, _CharT>) (std::formatter<chrono::local_time<_Dur>, _CharT>) (std::formatter<chrono::__detail::__local_time_fmt<_Dur>, _CharT>) (std::formatter<chrono::zoned_time<_Dur>, _CharT>): Define __defSpec, and pass it as argument to _M_prase and constructor of __formatter_chrono. --- libstdc++-v3/include/bits/chrono_io.h | 220 ++++++++++++++++---------- 1 file changed, 135 insertions(+), 85 deletions(-) diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h index e36cf1ffd31..24c7dfb91c9 100644 --- a/libstdc++-v3/include/bits/chrono_io.h +++ b/libstdc++-v3/include/bits/chrono_io.h @@ -243,10 +243,25 @@ namespace __format { using _String_view = basic_string_view<_CharT>; + static consteval + _String_view + _S_ftz() noexcept + { return _GLIBCXX_WIDEN("%F %T %Z"); } + + static consteval + _String_view + _S_ft() noexcept + { return _S_ftz().substr(0, 5); } + static consteval _String_view _S_f() noexcept - { return _GLIBCXX_WIDEN("%F"); } + { return _S_ftz().substr(0, 2); } + + static consteval + _String_view + _S_t() noexcept + { return _S_ftz().substr(3, 2); } static consteval _String_view @@ -836,58 +851,17 @@ namespace __format __os.imbue(_M_locale(__fc)); if constexpr (__is_specialization_of<_Tp, __local_time_fmt>) - { - // Format as "{:L%F %T}" - auto __days = chrono::floor<chrono::days>(__t._M_time); - __os << chrono::year_month_day(__days) << ' ' - << chrono::hh_mm_ss(__t._M_time - __days); - - // For __local_time_fmt the __is_neg flags says whether to - // append " %Z" to the result. - if (__is_neg) - { - if (!__t._M_abbrev) [[unlikely]] - __format::__no_timezone_available(); - else if constexpr (is_same_v<_CharT, char>) - __os << ' ' << *__t._M_abbrev; - else - { - __os << L' '; - for (char __c : *__t._M_abbrev) - __os << __c; - } - } - } + __builtin_trap(); + else if constexpr (__is_specialization_of<_Tp, __utc_leap_second>) + __builtin_trap(); + else if constexpr (chrono::__is_time_point_v<_Tp>) + __builtin_trap(); else { - if constexpr (__is_specialization_of<_Tp, __utc_leap_second>) - __os << __t._M_date << ' ' << __t._M_time; - else if constexpr (chrono::__is_time_point_v<_Tp>) - { - // Need to be careful here because not all specializations - // of chrono::sys_time can be written to an ostream. - // For the specializations of time_point that can be - // formatted with an empty chrono-specs, either it's a - // sys_time with period greater or equal to days: - if constexpr (is_convertible_v<_Tp, chrono::sys_days>) - __os << _S_date(__t); - // Or a local_time with period greater or equal to days: - else if constexpr (is_convertible_v<_Tp, chrono::local_days>) - __os << _S_date(__t); - else // Or it's formatted as "{:L%F %T}": - { - auto __days = chrono::floor<chrono::days>(__t); - __os << chrono::year_month_day(__days) << ' ' - << chrono::hh_mm_ss(__t - __days); - } - } - else - { - if constexpr (chrono::__is_duration_v<_Tp>) - if (__is_neg) [[unlikely]] - __os << _S_plus_minus[1]; - __os << __t; - } + if constexpr (chrono::__is_duration_v<_Tp>) + if (__is_neg) [[unlikely]] + __os << _S_plus_minus[1]; + __os << __t; } auto __str = std::move(__os).str(); @@ -2438,7 +2412,7 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_TimeOfDay); } + { return _M_f._M_parse(__pc, __format::_TimeOfDay, __defSpec); } template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator @@ -2447,7 +2421,15 @@ namespace __format { return _M_f._M_format(__t, __fc); } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + __format::_ChronoSpec<_CharT> __res{}; + __res._M_localized = true; + __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_t(); + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; #if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI @@ -2492,7 +2474,7 @@ namespace __format constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) { - auto __next = _M_f._M_parse(__pc, __format::_ZonedDateTime); + auto __next = _M_f._M_parse(__pc, __format::_ZonedDateTime, __defSpec); if constexpr (!__stream_insertable) if (_M_f._M_spec._M_chrono_specs.empty()) __format::__invalid_chrono_spec(); // chrono-specs can't be empty @@ -2510,7 +2492,22 @@ namespace __format = requires (basic_ostream<_CharT>& __os, chrono::sys_time<_Duration> __t) { __os << __t; }; - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + __format::_ChronoSpec<_CharT> __res{}; + if constexpr (!__stream_insertable) + return __res; + else if constexpr (is_convertible_v<_Duration, chrono::days>) + __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_f(); + else + { + __res._M_localized = true; + __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ft(); + } + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; template<typename _Duration, __format::__char _CharT> @@ -2519,11 +2516,11 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_ZonedDateTime); } + { return _M_f._M_parse(__pc, __format::_ZonedDateTime, __defSpec); } template<typename _Out> - typename basic_format_context<_Out, _CharT>::iterator - format(const chrono::utc_time<_Duration>& __t, + typename basic_format_context<_Out, _CharT>::iterator + format(const chrono::utc_time<_Duration>& __t, basic_format_context<_Out, _CharT>& __fc) const { // Adjust by removing leap seconds to get equivalent sys_time. @@ -2544,7 +2541,15 @@ namespace __format private: friend formatter<chrono::__detail::__utc_leap_second<_Duration>, _CharT>; - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + __format::_ChronoSpec<_CharT> __res{}; + __res._M_localized = true; + __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ft(); + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; template<typename _Duration, __format::__char _CharT> @@ -2553,11 +2558,11 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_ZonedDateTime); } + { return _M_f._M_parse(__pc, __format::_ZonedDateTime, __defSpec); } template<typename _Out> - typename basic_format_context<_Out, _CharT>::iterator - format(const chrono::tai_time<_Duration>& __t, + typename basic_format_context<_Out, _CharT>::iterator + format(const chrono::tai_time<_Duration>& __t, basic_format_context<_Out, _CharT>& __fc) const { // Convert to __local_time_fmt with abbrev "TAI" and offset 0s. @@ -2575,7 +2580,15 @@ namespace __format } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + __format::_ChronoSpec<_CharT> __res{}; + __res._M_localized = true; + __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ft(); + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; template<typename _Duration, __format::__char _CharT> @@ -2584,11 +2597,11 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_ZonedDateTime); } + { return _M_f._M_parse(__pc, __format::_ZonedDateTime, __defSpec); } template<typename _Out> - typename basic_format_context<_Out, _CharT>::iterator - format(const chrono::gps_time<_Duration>& __t, + typename basic_format_context<_Out, _CharT>::iterator + format(const chrono::gps_time<_Duration>& __t, basic_format_context<_Out, _CharT>& __fc) const { // Convert to __local_time_fmt with abbrev "GPS" and offset 0s. @@ -2606,7 +2619,15 @@ namespace __format } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + __format::_ChronoSpec<_CharT> __res{}; + __res._M_localized = true; + __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ft(); + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; template<typename _Duration, __format::__char _CharT> @@ -2614,11 +2635,11 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_ZonedDateTime); } + { return _M_f._M_parse(__pc, __format::_ZonedDateTime, __defSpec); } template<typename _Out> - typename basic_format_context<_Out, _CharT>::iterator - format(const chrono::file_time<_Duration>& __t, + typename basic_format_context<_Out, _CharT>::iterator + format(const chrono::file_time<_Duration>& __t, basic_format_context<_Out, _CharT>& __fc) const { using namespace chrono; @@ -2626,24 +2647,45 @@ namespace __format } private: - __format::__formatter_chrono<_CharT> _M_f; - }; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + __format::_ChronoSpec<_CharT> __res{}; + __res._M_localized = true; + __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ft(); + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; + }; template<typename _Duration, __format::__char _CharT> struct formatter<chrono::local_time<_Duration>, _CharT> { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_DateTime); } + { return _M_f._M_parse(__pc, __format::_DateTime, __defSpec); } template<typename _Out> - typename basic_format_context<_Out, _CharT>::iterator - format(const chrono::local_time<_Duration>& __t, + typename basic_format_context<_Out, _CharT>::iterator + format(const chrono::local_time<_Duration>& __t, basic_format_context<_Out, _CharT>& __fc) const { return _M_f._M_format(__t, __fc); } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + __format::_ChronoSpec<_CharT> __res{}; + if constexpr (is_convertible_v<_Duration, chrono::days>) + __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_f(); + else + { + __res._M_localized = true; + __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ft(); + } + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; template<typename _Duration, __format::__char _CharT> @@ -2651,16 +2693,24 @@ namespace __format { constexpr typename basic_format_parse_context<_CharT>::iterator parse(basic_format_parse_context<_CharT>& __pc) - { return _M_f._M_parse(__pc, __format::_ZonedDateTime); } + { return _M_f._M_parse(__pc, __format::_ZonedDateTime, __defSpec); } template<typename _Out> - typename basic_format_context<_Out, _CharT>::iterator - format(const chrono::__detail::__local_time_fmt<_Duration>& __t, + typename basic_format_context<_Out, _CharT>::iterator + format(const chrono::__detail::__local_time_fmt<_Duration>& __t, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f._M_format(__t, __fc, /* use %Z for {} */ true); } + { return _M_f._M_format(__t, __fc); } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + __format::_ChronoSpec<_CharT> __res{}; + __res._M_localized = true; + __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ftz(); + return __res; + }(); + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; #if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI @@ -2669,8 +2719,8 @@ namespace __format : formatter<chrono::__detail::__local_time_fmt_for<_Duration>, _CharT> { template<typename _Out> - typename basic_format_context<_Out, _CharT>::iterator - format(const chrono::zoned_time<_Duration, _TimeZonePtr>& __tp, + typename basic_format_context<_Out, _CharT>::iterator + format(const chrono::zoned_time<_Duration, _TimeZonePtr>& __tp, basic_format_context<_Out, _CharT>& __fc) const { using _Ltf = chrono::__detail::__local_time_fmt_for<_Duration>; @@ -2690,8 +2740,8 @@ namespace __format : formatter<chrono::utc_time<_Duration>, _CharT> { template<typename _Out> - typename basic_format_context<_Out, _CharT>::iterator - format(const chrono::__detail::__utc_leap_second<_Duration>& __t, + typename basic_format_context<_Out, _CharT>::iterator + format(const chrono::__detail::__utc_leap_second<_Duration>& __t, basic_format_context<_Out, _CharT>& __fc) const { return this->_M_f._M_format(__t, __fc); } }; -- 2.49.0