This patch extract calls to _M_locale_fmt and construction of the struct tm, from the functions dedicated to each specifier, to main format loop in _M_format_to functions. This removes duplicated code repeated for specifiers.
To allow _M_locale_fmt to only be called if localized formatting is enabled ('L' is present in chrono-format-spec), we provide a implementations for locale specific specifiers (%c, %r, %x, %X) that produces the same result as locale::classic(): * %c is implemented as separate _M_c method * %r is implemented as separate _M_r method * %x is implemented together with %D, as they provide same behavior, * %X is implemented together with %R as _M_R_X, as both of them do not include subseconds. The handling of subseconds was also extracted to _M_subsecs function that is used by _M_S and _M_T specifier. The _M_T is now implemented in terms of _M_R_X (printing time without subseconds) and _M_subs. The __mod responsible for triggering localized formatting was removed from method handling most of specifiers, except: * _M_S (for %S) for which it determines if subseconds should be included, * _M_z (for %z) for which it determines if ':' is used as separator. PR libstdc++/110739 libstdc++-v3/ChangeLog: * include/bits/chrono_io.h (__formatter_chrono::_M_use_locale_fmt): Define. (__formatter_chrono::_M_locale_fmt): Moved to front of the class. (__formatter_chrono::_M_format_to): Construct and initialize struct tm and call _M_locale_fmt if needed. (__formatter_chrono::_M_c_r_x_X): Split into separate methods. (__formatter_chrono::_M_c, __formatter_chrono::_M_r): Define. (__formatter_chrono::_M_D): Renamed to _M_D_x. (__formatter_chrono::_M_D_x): Renamed from _M_D. (__formatter_chrono::_M_R_T): Split into _M_R_X and _M_T. (__formatter_chrono::_M_R_X): Extracted from _M_R_T. (__formatter_chrono::_M_T): Define in terms of _M_R_X and _M_subsecs. (__formatter_chrono::_M_subsecs): Extracted from _M_S. (__formatter_chrono::_M_S): Replaced __mod with __subs argument, removed _M_locale_fmt call, and delegate to _M_subsecs. (__formatter_chrono::_M_C_y_Y, __formatter_chrono::_M_d_e) (__formatter_chrono::_M_H_I, __formatter_chrono::_M_m) (__formatter_chrono::_M_u_w, __formatter_chrono::_M_U_V_W): Remove __mod argument and call to _M_locale_fmt. --- libstdc++-v3/include/bits/chrono_io.h | 340 +++++++++++++------------- 1 file changed, 172 insertions(+), 168 deletions(-) diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h index 35e95906e6a..d451bde722d 100644 --- a/libstdc++-v3/include/bits/chrono_io.h +++ b/libstdc++-v3/include/bits/chrono_io.h @@ -906,6 +906,40 @@ namespace __format return __format::__write(std::move(__out), __s); } + [[__gnu__::__always_inline__]] + static bool + _S_localized_spec(_CharT __conv, _CharT __mod) + { + switch (__conv) + { + case 'c': + case 'r': + case 'x': + case 'X': + return true; + case 'z': + return false; + default: + return (bool)__mod; + }; + } + + // Use the formatting locale's std::time_put facet to produce + // a locale-specific representation. + template<typename _Iter> + _Iter + _M_locale_fmt(_Iter __out, const locale& __loc, const struct tm& __tm, + char __fmt, char __mod) const + { + basic_ostringstream<_CharT> __os; + __os.imbue(__loc); + const auto& __tp = use_facet<time_put<_CharT>>(__loc); + __tp.put(__os, __os, _S_space, &__tm, __fmt, __mod); + if (__os) + __out = _M_write(std::move(__out), __loc, __os.view()); + return __out; + } + template<typename _Out, typename _FormatContext> _Out _M_format_to(const _ChronoData<_CharT>& __t, _Out __out, @@ -923,6 +957,36 @@ namespace __format return std::move(__out); }; + struct tm __tm{}; + bool __use_locale_fmt = false; + if (_M_spec._M_localized && _M_spec._M_locale_specific) + if (__fc.locale() != locale::classic()) + { + __use_locale_fmt = true; + + __tm.tm_year = (int)__t._M_year - 1900; + __tm.tm_yday = __t._M_day_of_year.count(); + __tm.tm_mon = (unsigned)__t._M_month - 1; + __tm.tm_mday = (unsigned)__t._M_day; + __tm.tm_wday = __t._M_weekday.c_encoding(); + __tm.tm_hour = __t._M_hours.count(); + __tm.tm_min = __t._M_minutes.count(); + __tm.tm_sec = __t._M_seconds.count(); + + // Some locales use %Z in their %c format but we don't want strftime + // to use the system's local time zone (from /etc/localtime or $TZ) + // as the output for %Z. Setting tm_isdst to -1 says there is no + // time zone info available for the time in __tm. + __tm.tm_isdst = -1; + +#ifdef _GLIBCXX_USE_STRUCT_TM_TM_ZONE + // POSIX.1-2024 adds tm.tm_zone which will be used for %Z. + // BSD has had tm_zone since 1987 but as char* so cast away const. + if (__t._M_zone_cstr) + __tm.tm_zone = const_cast<char*>(__t._M_zone_cstr); +#endif + } + // Characters to output for "%n", "%t" and "%%" specifiers. constexpr const _CharT* __literals = _GLIBCXX_WIDEN("\n\t%"); @@ -932,7 +996,10 @@ namespace __format do { _CharT __c = *__first++; - switch (__c) + if (__use_locale_fmt && _S_localized_spec(__c, __mod)) [[unlikely]] + __out = _M_locale_fmt(std::move(__out), __fc.locale(), + __tm, __c, __mod); + else switch (__c) { // %\0 is extension for handling weekday index case '\0': @@ -948,22 +1015,20 @@ namespace __format __out = _M_b_B(__t._M_month, std::move(__out), __fc, __c == 'B'); break; case 'c': - case 'r': - case 'x': - case 'X': - __out = _M_c_r_x_X(__t, std::move(__out), __fc, __c, __mod); + __out = _M_c(__t, std::move(__out), __fc); break; case 'C': case 'y': case 'Y': - __out = _M_C_y_Y(__t._M_year, std::move(__out), __fc, __c, __mod); + __out = _M_C_y_Y(__t._M_year, std::move(__out), __fc, __c); break; case 'd': case 'e': - __out = _M_d_e(__t._M_day, std::move(__out), __fc, __c, __mod == 'O'); + __out = _M_d_e(__t._M_day, std::move(__out), __fc, __c); break; case 'D': - __out = _M_D(__t, std::move(__out), __fc); + case 'x': + __out = _M_D_x(__t, std::move(__out), __fc); break; case 'F': __out = _M_F(__t, std::move(__out), __fc); @@ -974,16 +1039,16 @@ namespace __format break; case 'H': case 'I': - __out = _M_H_I(__t._M_hours, __print_sign(), __fc, __c, __mod == 'O'); + __out = _M_H_I(__t._M_hours, __print_sign(), __fc, __c); break; case 'j': __out = _M_j(__t, __print_sign(), __fc); break; case 'm': - __out = _M_m(__t._M_month, std::move(__out), __fc, __mod == 'O'); + __out = _M_m(__t._M_month, std::move(__out), __fc); break; case 'M': - __out = _M_M(__t._M_minutes, __print_sign(), __fc, __mod == 'O'); + __out = _M_M(__t._M_minutes, __print_sign(), __fc); break; case 'p': __out = _M_p(__t._M_hours, std::move(__out), __fc); @@ -994,22 +1059,28 @@ namespace __format case 'Q': __out = _M_Q(__t, __print_sign(), __fc); break; + case 'r': + __out = _M_r(__t, __print_sign(), __fc); + break; case 'R': + case 'X': + __out = _M_R_X(__t, __print_sign(), __fc, __c != 'R'); + break; case 'T': - __out = _M_R_T(__t, __print_sign(), __fc, __c == 'T'); + __out = _M_T(__t, __print_sign(), __fc); break; + case 'S': - __out = _M_S(__t, __print_sign(), __fc, __mod == 'O'); + __out = _M_S(__t, __print_sign(), __fc, __mod != 'O'); break; case 'u': case 'w': - __out = _M_u_w(__t._M_weekday, std::move(__out), __fc, __c, __mod == 'O'); + __out = _M_u_w(__t._M_weekday, std::move(__out), __fc, __c); break; case 'U': case 'V': case 'W': - __out = _M_U_V_W(__t, std::move(__out), __fc, __c, - __mod == 'O'); + __out = _M_U_V_W(__t, std::move(__out), __fc, __c); break; case 'z': __out = _M_z(__t._M_zone_offset, std::move(__out), __fc, (bool)__mod); @@ -1132,50 +1203,27 @@ namespace __format template<typename _FormatContext> typename _FormatContext::iterator - _M_c_r_x_X(const _ChronoData<_CharT>& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx, _CharT __conv, _CharT __mod) const + _M_c(const _ChronoData<_CharT>& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx) const { - // %c Locale's date and time representation. - // %Ec Locale's alternate date and time representation. - // %r Locale's 12-hour clock time. - // %x Locale's date rep - // %Ex Locale's alternative date representation. - // %X Locale's time rep - // %EX Locale's alternative time representation. - - using namespace chrono; - struct tm __tm{}; - - // Some locales use %Z in their %c format but we don't want strftime - // to use the system's local time zone (from /etc/localtime or $TZ) - // as the output for %Z. Setting tm_isdst to -1 says there is no - // time zone info available for the time in __tm. - __tm.tm_isdst = -1; - -#ifdef _GLIBCXX_USE_STRUCT_TM_TM_ZONE - // POSIX.1-2024 adds tm.tm_zone which will be used for %Z. - // BSD has had tm_zone since 1987 but as char* so cast away const. - if (__t._M_zone_cstr) - __tm.tm_zone = const_cast<char*>(__t._M_zone_cstr); -#endif - - __tm.tm_year = (int)__t._M_year - 1900; - __tm.tm_yday = __t._M_day_of_year.count(); - __tm.tm_mon = (unsigned)__t._M_month - 1; - __tm.tm_mday = (unsigned)__t._M_day; - __tm.tm_wday = __t._M_weekday.c_encoding(); - __tm.tm_hour = __t._M_hours.count(); - __tm.tm_min = __t._M_minutes.count(); - __tm.tm_sec = __t._M_seconds.count(); - - return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm, - __conv, __mod); + // %c Locale's date and time representation, for C-locale: %a %b %e %T %Y + // %Ec Locale's alternate date and time representation, for C-locale same as above + + __out = _M_a_A(__t._M_weekday, std::move(__out), __ctx, false); + *__out = _S_space; + __out = _M_b_B(__t._M_month, std::move(++__out), __ctx, false); + *__out = _S_space; + __out = _M_d_e(__t._M_day, std::move(++__out), __ctx, 'e'); + *__out = _S_space; + __out = _M_R_X(__t, std::move(++__out), __ctx, true); + *__out = _S_space; + return _M_C_y_Y(__t._M_year, std::move(__out), __ctx, 'Y'); } template<typename _FormatContext> typename _FormatContext::iterator _M_C_y_Y(chrono::year __y, typename _FormatContext::iterator __out, - _FormatContext& __ctx, _CharT __conv, _CharT __mod = 0) const + _FormatContext& __ctx, _CharT __conv) const { // %C Year divided by 100 using floored division. // %EC Locale's alternative preresentation of the century (era name). @@ -1185,15 +1233,6 @@ namespace __format // %Y Year as a decimal number. // %EY Locale's alternative full year representation. - if (__mod && _M_spec._M_localized) [[unlikely]] - if (auto __loc = __ctx.locale(); __loc != locale::classic()) - { - struct tm __tm{}; - __tm.tm_year = (int)__y - 1900; - return _M_locale_fmt(std::move(__out), __loc, __tm, - __conv, __mod); - } - int __yi = (int)__y; const bool __is_neg = __yi < 0; __yi = __builtin_abs(__yi); @@ -1235,9 +1274,13 @@ namespace __format template<typename _FormatContext> typename _FormatContext::iterator - _M_D(const _ChronoData<_CharT>& __t, typename _FormatContext::iterator __out, - _FormatContext&) const + _M_D_x(const _ChronoData<_CharT>& __t, typename _FormatContext::iterator __out, + _FormatContext&) const { + // %D Equivalent to %m/%d/%y + // %x Locale's date rep, for C-locale: %m/%d/%y + // %Ex Locale's alternative date representation, for C-locale same as above + auto __di = (unsigned)__t._M_day; auto __mi = (unsigned)__t._M_month; auto __yi = __builtin_abs((int)__t._M_year) % 100; @@ -1267,7 +1310,7 @@ namespace __format template<typename _FormatContext> typename _FormatContext::iterator _M_d_e(chrono::day __d, typename _FormatContext::iterator __out, - _FormatContext& __ctx, _CharT __conv, bool __mod = false) const + _FormatContext& __ctx, _CharT __conv) const { // %d The day of month as a decimal number. // %Od Locale's alternative representation. @@ -1276,15 +1319,6 @@ namespace __format unsigned __i = (unsigned)__d; - if (__mod && _M_spec._M_localized) [[unlikely]] - if (auto __loc = __ctx.locale(); __loc != locale::classic()) - { - struct tm __tm{}; - __tm.tm_mday = __i; - return _M_locale_fmt(std::move(__out), __loc, __tm, - (char)__conv, 'O'); - } - _CharT __buf[3]; auto __sv = _S_str_d2(__buf, __i); if (__conv == _CharT('e') && __i < 10) @@ -1360,7 +1394,7 @@ namespace __format template<typename _FormatContext> typename _FormatContext::iterator _M_H_I(chrono::hours __h, typename _FormatContext::iterator __out, - _FormatContext& __ctx, _CharT __conv, bool __mod = false) const + _FormatContext& __ctx, _CharT __conv) const { // %H The hour (24-hour clock) as a decimal number. // %OH Locale's alternative representation. @@ -1369,15 +1403,6 @@ namespace __format int __i = __h.count(); - if (__mod && _M_spec._M_localized) [[unlikely]] - if (auto __loc = __ctx.locale(); __loc != locale::classic()) - { - struct tm __tm{}; - __tm.tm_hour = __i; - return _M_locale_fmt(std::move(__out), __loc, __tm, - (char)__conv, 'O'); - } - if (__conv == _CharT('I')) { __i %= 12; @@ -1409,22 +1434,13 @@ namespace __format template<typename _FormatContext> typename _FormatContext::iterator _M_m(chrono::month __m, typename _FormatContext::iterator __out, - _FormatContext& __ctx, bool __mod) const + _FormatContext& __ctx) const { // %m month as a decimal number. // %Om Locale's alternative representation. auto __i = (unsigned)__m; - if (__mod && _M_spec._M_localized) [[unlikely]] // %Om - if (auto __loc = __ctx.locale(); __loc != locale::classic()) - { - struct tm __tm{}; - __tm.tm_mon = __i - 1; - return _M_locale_fmt(std::move(__out), __loc, __tm, - 'm', 'O'); - } - _CharT __buf[3]; return __format::__write(std::move(__out), _S_str_d2(__buf, __i)); } @@ -1432,22 +1448,12 @@ namespace __format template<typename _FormatContext> typename _FormatContext::iterator _M_M(chrono::minutes __m, typename _FormatContext::iterator __out, - _FormatContext& __ctx, bool __mod) const + _FormatContext& __ctx) const { // %M The minute as a decimal number. // %OM Locale's alternative representation. auto __i = __m.count(); - - if (__mod && _M_spec._M_localized) [[unlikely]] // %OM - if (auto __loc = __ctx.locale(); __loc != locale::classic()) - { - struct tm __tm{}; - __tm.tm_min = __i; - return _M_locale_fmt(std::move(__out), __loc, __tm, - 'M', 'O'); - } - return __format::__write(std::move(__out), _S_two_digits(__i)); } @@ -1488,17 +1494,42 @@ namespace __format template<typename _FormatContext> typename _FormatContext::iterator - _M_R_T(const _ChronoData<_CharT>& __t, typename _FormatContext::iterator __out, + _M_r(const _ChronoData<_CharT>& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx) const + { + // %r Locale's 12-hour clock time, for C-locale: %I:%M:%S %p + auto __hi = __t._M_hours.count() % 12; + if (__hi == 0) + __hi = 12; + + _CharT __buf[9]; + __buf[2] = _S_colon; + __buf[5] = _S_colon; + __buf[8] = _S_space; + _S_fill_two_digits(__buf, __hi); + _S_fill_two_digits(__buf + 3, __t._M_minutes.count()); + _S_fill_two_digits(__buf + 6, __t._M_seconds.count()); + + __string_view __sv(__buf, 9); + __out = __format::__write(std::move(__out), __sv); + return _M_p(__t._M_hours, std::move(__out), __ctx); + } + + template<typename _FormatContext> + typename _FormatContext::iterator + _M_R_X(const _ChronoData<_CharT>& __t, typename _FormatContext::iterator __out, _FormatContext& __ctx, bool __secs) const { - // %R Equivalent to %H:%M - // %T Equivalent to %H:%M:%S + // %R Equivalent to %H:%M + // %X Locale's time rep, for C-locale: %H:%M:%S (without subseconds) + // %EX Locale's alternative time representation, for C-locale same as above + auto __hi = __t._M_hours.count(); - _CharT __buf[6]; + _CharT __buf[8]; __buf[2] = _S_colon; __buf[5] = _S_colon; - __string_view __sv(__buf, 5 + __secs); + __string_view __sv(__buf, 8); if (__hi >= 100) [[unlikely]] { @@ -1509,39 +1540,39 @@ namespace __format _S_fill_two_digits(__buf, __hi); _S_fill_two_digits(__buf + 3, __t._M_minutes.count()); - __out = __format::__write(std::move(__out), __sv); if (__secs) - __out = _M_S(__t, std::move(__out), __ctx); - return __out; + _S_fill_two_digits(__buf + 6, __t._M_seconds.count()); + else + __sv.remove_suffix(3); + + return __format::__write(std::move(__out), __sv); } template<typename _FormatContext> typename _FormatContext::iterator _M_S(const _ChronoData<_CharT>& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx, bool __mod = false) const + _FormatContext& __ctx, bool __subs = true) const { - // %S Seconds as a decimal number. // %OS The locale's alternative representation. auto __s = __t._M_seconds; - if (__mod && _M_spec._M_localized) [[unlikely]] // %OS - if (auto __loc = __ctx.locale(); __loc != locale::classic()) - { - struct tm __tm{}; - __tm.tm_sec = (int)__s.count(); - return _M_locale_fmt(std::move(__out), __loc, __tm, - 'S', 'O'); - } - __out = __format::__write(std::move(__out), - _S_two_digits(__s.count())); + _S_two_digits(__s.count())); + if (__subs) + __out = _M_subsecs(__t, std::move(__out), __ctx); + return __out; + } + template<typename _FormatContext> + typename _FormatContext::iterator + _M_subsecs(const _ChronoData<_CharT>& __t, + typename _FormatContext::iterator __out, + _FormatContext& __ctx) const + { unsigned __prec = _M_spec._M_prec_kind != _WP_none ? _M_spec._M_get_precision(__ctx) : _M_spec._M_prec; - - // %OS formats don't include subseconds - if (__prec == 0 || __mod) + if (__prec == 0) return __out; _CharT __dot = _S_dot; @@ -1595,25 +1626,25 @@ namespace __format // %t handled in _M_format + template<typename _FormatContext> + typename _FormatContext::iterator + _M_T(const _ChronoData<_CharT>& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx) const + { + // %T Equivalent to %H:%M:%S, with subseconds + __out = _M_R_X(__t, std::move(__out), __ctx, true); + return _M_subsecs(__t, std::move(__out), __ctx); + } + template<typename _FormatContext> typename _FormatContext::iterator _M_u_w(chrono::weekday __wd, typename _FormatContext::iterator __out, - _FormatContext& __ctx, _CharT __conv, bool __mod = false) const + _FormatContext& __ctx, _CharT __conv) const { // %u ISO weekday as a decimal number (1-7), where Monday is 1. // %Ou Locale's alternative numeric rep. // %w Weekday as a decimal number (0-6), where Sunday is 0. // %Ow Locale's alternative numeric rep. - - if (__mod && _M_spec._M_localized) [[unlikely]] - if (auto __loc = __ctx.locale(); __loc != locale::classic()) - { - struct tm __tm{}; - __tm.tm_wday = __wd.c_encoding(); - return _M_locale_fmt(std::move(__out), __loc, __tm, - (char)__conv, 'O'); - } - unsigned __wdi = __conv == 'u' ? __wd.iso_encoding() : __wd.c_encoding(); _CharT __buf[3]; @@ -1623,7 +1654,7 @@ namespace __format template<typename _FormatContext> typename _FormatContext::iterator _M_U_V_W(const _ChronoData<_CharT>& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx, _CharT __conv, bool __mod = false) const + _FormatContext& __ctx, _CharT __conv) const { // %U Week number of the year as a decimal number, from first Sunday. // %OU Locale's alternative numeric rep. @@ -1633,17 +1664,6 @@ namespace __format // %OW Locale's alternative numeric rep. using namespace chrono; - if (__mod && _M_spec._M_localized) [[unlikely]] - if (auto __loc = __ctx.locale(); __loc != locale::classic()) - { - struct tm __tm{}; - __tm.tm_year = (int)__t._M_year - 1900; - __tm.tm_yday = __t._M_day_of_year.count(); - __tm.tm_wday = __t._M_weekday.c_encoding(); - return _M_locale_fmt(std::move(__out), __loc, __tm, - (char)__conv, 'O'); - } - auto __d = __t._M_ldays; local_days __first; // First day of week 1. if (__conv == 'V') // W01 begins on Monday before first Thursday. @@ -1755,22 +1775,6 @@ namespace __format __buf[2] = _S_chars[__n % 10]; return __string_view(__buf.data(), 3); } - - // Use the formatting locale's std::time_put facet to produce - // a locale-specific representation. - template<typename _Iter> - _Iter - _M_locale_fmt(_Iter __out, const locale& __loc, const struct tm& __tm, - char __fmt, char __mod) const - { - basic_ostringstream<_CharT> __os; - __os.imbue(__loc); - const auto& __tp = use_facet<time_put<_CharT>>(__loc); - __tp.put(__os, __os, _S_space, &__tm, __fmt, __mod); - if (__os) - __out = _M_write(std::move(__out), __loc, __os.view()); - return __out; - } }; template<typename _CharT> -- 2.49.0