From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2181) id C556F3860759; Fri, 28 Jul 2023 17:32:46 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org C556F3860759 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1690565566; bh=aOAi3hhVrsVN/nrGzWCUtlZYzQfhxQsZy/WXhblbW4Y=; h=From:To:Subject:Date:From; b=HToiVpznj8be9pDNB73eLTDf10QwXI+51OZwx2TEWwgItOPR4ebEJUbIrASBFRHgU 1UwT5G1vkcW//zatRDe6s7DPDJHjx6IdB+7Q0Plaxq/Ky/719fVaPUTyaIU9e0q4JE 2qgw81UGYza27vVV12bfEWXAOoK5nM8tcbZzfKwE= MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Jonathan Wakely To: gcc-cvs@gcc.gnu.org, libstdc++-cvs@gcc.gnu.org Subject: [gcc r13-7630] libstdc++: Implement correct locale-specific chrono formatting [PR110719] X-Act-Checkin: gcc X-Git-Author: Jonathan Wakely X-Git-Refname: refs/heads/releases/gcc-13 X-Git-Oldrev: 74f1c016f44daf18e4bed76b283cd5bfd0b5c8c7 X-Git-Newrev: cb01a31ab2779b0252c4945924ba2163d9150642 Message-Id: <20230728173246.C556F3860759@sourceware.org> Date: Fri, 28 Jul 2023 17:32:46 +0000 (GMT) List-Id: https://gcc.gnu.org/g:cb01a31ab2779b0252c4945924ba2163d9150642 commit r13-7630-gcb01a31ab2779b0252c4945924ba2163d9150642 Author: Jonathan Wakely Date: Tue Jul 18 12:46:07 2023 +0100 libstdc++: Implement correct locale-specific chrono formatting [PR110719] This fixes some TODOs in the C++20 format support, where the locale-specific output was incorrect or unimplemented. The approach taken here is to either use the formatting locale's std::time_put facet to do the formatting, or to remove subsecond precision from time points so that locale-specific formats don't print fractional seconds. This ensures that we are consistent with what the std::time_put facet would print (which never includes fractional seconds) even if we actually reimplement the formatting by hand instead of using the facet. This also fixes a misplaced statement that allowed modifiers for %Z which should have been on %z instead. There was also some ill-formed code in an untested branch for formatting time zone names to wide characters. A new test for zoned_time I/O has been added to exercise that code properly. libstdc++-v3/ChangeLog: PR libstdc++/110719 * include/bits/chrono_io.h (__formatter_chrono::_M_parse): Fix allowed modifiers for %z and %Z. Fix -Wparentheses and -Wnarrowing warnings. (__formatter_chrono::_M_format): Call new functions for %d, %e, %H, %I, %m and %M. (__formatter_chrono::_M_c): Use _S_floor_seconds to remove subsecond precision. (__formatter_chrono::_M_C_y_Y): Use _M_locale_fmt to handle modifiers. (__formatter_chrono::_M_e): Replace with _M_d_e and use _M_locale_fmt. (__formatter_chrono::_M_I): Replace with _M_H_I and use _M_locale_fmt. (__formatter_chrono::_M_m): New function. (__formatter_chrono::_M_M): New function. (__formatter_chrono::_M_r): Use _M_locale_fmt. (__formatter_chrono::_M_S): Likewise. (__formatter_chrono::_M_u_w): Likewise. (__formatter_chrono::_M_U_V_W): Likewise. (__formatter_chrono::_M_X): Use _S_floor_seconds. (__formatter_chrono::_M_Z): Fix untested branch for wchar_t. (__formatter_chrono::_S_altnum): Remove function. (__formatter_chrono::_S_dd_zero_fill): Remove function. (__formatter_chrono::_S_floor_seconds): New function. (__formatter_chrono::_M_locale_fmt): New function. * testsuite/std/time/clock/system/io.cc: Adjust expected output for locale-specific formats and check modified formats. * testsuite/std/time/clock/utc/io.cc: Likewise. * testsuite/std/time/zoned_time/io.cc: New test. (cherry picked from commit f4bce119f617dc4663fb43f55784908daf16b4b6) Diff: --- libstdc++-v3/include/bits/chrono_io.h | 295 +++++++++++++-------- libstdc++-v3/testsuite/std/time/clock/system/io.cc | 20 +- libstdc++-v3/testsuite/std/time/clock/utc/io.cc | 12 +- libstdc++-v3/testsuite/std/time/zoned_time/io.cc | 64 +++++ 4 files changed, 272 insertions(+), 119 deletions(-) diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h index 5f06a6d76b4..43eeab42869 100644 --- a/libstdc++-v3/include/bits/chrono_io.h +++ b/libstdc++-v3/include/bits/chrono_io.h @@ -414,11 +414,10 @@ namespace __format break; case 'z': __needed = _TimeZone; - __allowed_mods = _Mod_E; + __allowed_mods = _Mod_E_O; break; case 'Z': __needed = _TimeZone; - __allowed_mods = _Mod_E_O; break; case 'n': case 't': @@ -439,7 +438,7 @@ namespace __format } if ((__mod == 'E' && !(__allowed_mods & _Mod_E)) - || __mod == 'O' && !(__allowed_mods & _Mod_O)) + || (__mod == 'O' && !(__allowed_mods & _Mod_O))) __throw_format_error("chrono format error: invalid " " modifier in chrono-specs"); __mod = _CharT(); @@ -471,7 +470,8 @@ namespace __format "chrono-specs"); _M_spec = __spec; - _M_spec._M_chrono_specs = {__chrono_specs, __first - __chrono_specs}; + _M_spec._M_chrono_specs + = __string_view(__chrono_specs, __first - __chrono_specs); return __first; } @@ -551,18 +551,12 @@ namespace __format __out = _M_C_y_Y(__t, std::move(__out), __fc, __c, __mod); break; case 'd': - // %d The day of month as a decimal number. - // %Od Locale's alternative representation. - __out = _S_dd_zero_fill((unsigned)_S_day(__t), - std::move(__out), - __fc, __mod == 'O'); + case 'e': + __out = _M_d_e(__t, std::move(__out), __fc, __c, __mod == 'O'); break; case 'D': __out = _M_D(__t, std::move(__out), __fc); break; - case 'e': - __out = _M_e(__t, std::move(__out), __fc, __mod == 'O'); - break; case 'F': __out = _M_F(__t, std::move(__out), __fc); break; @@ -571,29 +565,17 @@ namespace __format __out = _M_g_G(__t, std::move(__out), __fc, __c == 'G'); break; case 'H': - // %H The hour (24-hour clock) as a decimal number. - // %OH Locale's alternative representation. - __out = _S_dd_zero_fill(_S_hms(__t).hours().count(), - __print_sign(), __fc, __mod == 'O'); - break; case 'I': - __out = _M_I(__t, __print_sign(), __fc, __mod == 'O'); + __out = _M_H_I(__t, __print_sign(), __fc, __c, __mod == 'O'); break; case 'j': __out = _M_j(__t, __print_sign(), __fc); break; case 'm': - // %m month as a decimal number. - // %Om Locale's alternative representation. - __out = _S_dd_zero_fill((unsigned)_S_month(__t), - std::move(__out), __fc, - __mod == 'O'); + __out = _M_m(__t, std::move(__out), __fc, __mod == 'O'); break; case 'M': - // %M The minute as a decimal number. - // %OM Locale's alternative representation. - __out = _S_dd_zero_fill(_S_hms(__t).minutes().count(), - __print_sign(), __fc, __mod == 'O'); + __out = _M_M(__t, __print_sign(), __fc, __mod == 'O'); break; case 'p': __out = _M_p(__t, std::move(__out), __fc); @@ -790,12 +772,13 @@ namespace __format template typename _FormatContext::iterator - _M_c(const _Tp& __t, typename _FormatContext::iterator __out, + _M_c(const _Tp& __tt, typename _FormatContext::iterator __out, _FormatContext& __ctx, bool __mod = false) const { // %c Locale's date and time representation. // %Ec Locale's alternate date and time representation. + auto __t = _S_floor_seconds(__tt); locale __loc = _M_locale(__ctx); const auto& __tp = use_facet<__timepunct<_CharT>>(__loc); const _CharT* __formats[2]; @@ -813,22 +796,24 @@ namespace __format template typename _FormatContext::iterator _M_C_y_Y(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx, char __conv, char __mod = 0) const + _FormatContext& __ctx, _CharT __conv, _CharT __mod = 0) const { // %C Year divided by 100 using floored division. // %EC Locale's alternative preresentation of the century (era name). // %y Last two decimal digits of the year. - // %OY Locale's alternative represenation. + // %Oy Locale's alternative representation. // %Ey Locale's alternative representation of offset from %EC. // %Y Year as a decimal number. - // %EY Locale's alternative full year represenation. + // %EY Locale's alternative full year representation. chrono::year __y = _S_year(__t); - if (__mod == 'E') + if (__mod) [[unlikely]] { - // TODO: %EC, %Ey or %EY - // return __out; + struct tm __tm{}; + __tm.tm_year = (int)__y - 1900; + return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm, + __conv, __mod); } basic_string<_CharT> __s; @@ -852,9 +837,6 @@ namespace __format if (__conv == 'Y' || __conv == 'y') __s += _S_two_digits(__yi % 100); - if (__mod == 'O') // %OY - _S_altnum(_M_locale(__ctx), __s, __is_neg); - return __format::__write(std::move(__out), __string_view(__s)); } @@ -878,19 +860,33 @@ namespace __format template typename _FormatContext::iterator - _M_e(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx, bool __mod = false) const + _M_d_e(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, _CharT __conv, bool __mod = false) const { + // %d The day of month as a decimal number. + // %Od Locale's alternative representation. // %e Day of month as decimal number, padded with space. // %Oe Locale's alternative digits. + chrono::day __d = _S_day(__t); unsigned __i = (unsigned)__d; + + if (__mod) [[unlikely]] + { + struct tm __tm{}; + __tm.tm_mday = __i; + return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm, + (char)__conv, 'O'); + } + auto __sv = _S_two_digits(__i); - basic_string<_CharT> __s; - if (__mod) - __sv = _S_altnum(_M_locale(__ctx), __s.assign(__sv)); - if (__i < 10) - __sv = __s = {_S_space, __sv[1]}; + _CharT __buf[2]; + if (__conv == _CharT('e') && __i < 10) + { + __buf[0] = _S_space; + __buf[1] = __sv[1]; + __sv = {__buf, 2}; + } return __format::__write(std::move(__out), __sv); } @@ -933,26 +929,39 @@ namespace __format template typename _FormatContext::iterator - _M_I(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx, bool __mod = false) const + _M_H_I(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, _CharT __conv, bool __mod = false) const { - auto __hms = _S_hms(__t); + // %H The hour (24-hour clock) as a decimal number. + // %OH Locale's alternative representation. + // %I The hour (12-hour clock) as a decimal number. + // %OI Locale's alternative representation. + + const auto __hms = _S_hms(__t); int __i = __hms.hours().count(); - if (__i == 0) - __i = 12; - else if (__i > 12) - __i -= 12; - auto __sv = _S_two_digits(__i); - basic_string<_CharT> __s; - if (__mod) - __sv = _S_altnum(_M_locale(__ctx), __s.assign(__sv)); - return __format::__write(std::move(__out), __sv); + + if (__mod) [[unlikely]] + { + struct tm __tm{}; + __tm.tm_hour = __i; + return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm, + (char)__conv, 'O'); + } + + if (__conv == _CharT('I')) + { + if (__i == 0) + __i = 12; + else if (__i > 12) + __i -= 12; + } + return __format::__write(std::move(__out), _S_two_digits(__i)); } template typename _FormatContext::iterator _M_j(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx) const + _FormatContext&) const { if constexpr (chrono::__is_duration_v<_Tp>) { @@ -978,6 +987,50 @@ namespace __format } } + template + typename _FormatContext::iterator + _M_m(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __mod) const + { + // %m month as a decimal number. + // %Om Locale's alternative representation. + + auto __m = _S_month(__t); + auto __i = (unsigned)__m; + + if (__mod) [[unlikely]] // %Om + { + struct tm __tm{}; + __tm.tm_mon = __i - 1; + return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm, + 'm', 'O'); + } + + return __format::__write(std::move(__out), _S_two_digits(__i)); + } + + template + typename _FormatContext::iterator + _M_M(const _Tp& __t, typename _FormatContext::iterator __out, + _FormatContext& __ctx, bool __mod) const + { + // %M The minute as a decimal number. + // %OM Locale's alternative representation. + + auto __m = _S_hms(__t).minutes(); + auto __i = __m.count(); + + if (__mod) [[unlikely]] // %OM + { + struct tm __tm{}; + __tm.tm_min = __i; + return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm, + 'M', 'O'); + } + + return __format::__write(std::move(__out), _S_two_digits(__i)); + } + template typename _FormatContext::iterator _M_p(const _Tp& __t, typename _FormatContext::iterator __out, @@ -995,7 +1048,7 @@ namespace __format template typename _FormatContext::iterator - _M_q(const _Tp& __t, typename _FormatContext::iterator __out, + _M_q(const _Tp&, typename _FormatContext::iterator __out, _FormatContext& __ctx) const { // %q The duration's unit suffix @@ -1025,12 +1078,15 @@ namespace __format } } + // %Q handled in _M_format + template typename _FormatContext::iterator - _M_r(const _Tp& __t, typename _FormatContext::iterator __out, + _M_r(const _Tp& __tt, typename _FormatContext::iterator __out, _FormatContext& __ctx) const { // %r locale's 12-hour clock time. + auto __t = _S_floor_seconds(__tt); locale __loc = _M_locale(__ctx); const auto& __tp = use_facet<__timepunct<_CharT>>(__loc); const _CharT* __ampm_fmt; @@ -1075,10 +1131,19 @@ namespace __format _FormatContext& __ctx, bool __mod = false) const { // %S Seconds as a decimal number. - // %OS (TODO) The locale's alternative representation. + // %OS The locale's alternative representation. auto __hms = _S_hms(__t); - __out = _S_dd_zero_fill(__hms.seconds().count(), - std::move(__out), __ctx, __mod); + + if (__mod) [[unlikely]] // %OS + { + struct tm __tm{}; + __tm.tm_sec = (int)__hms.seconds().count(); + return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm, + 'S', 'O'); + } + + __out = __format::__write(std::move(__out), + _S_two_digits(__hms.seconds().count())); using rep = typename decltype(__hms)::precision::rep; if constexpr (__hms.fractional_width != 0) { @@ -1126,14 +1191,21 @@ namespace __format // %Ou Locale's alternative numeric rep. // %w Weekday as a decimal number (0-6), where Sunday is 0. // %Ow Locale's alternative numeric rep. + chrono::weekday __wd = _S_weekday(__t); + + if (__mod) [[unlikely]] + { + struct tm __tm{}; + __tm.tm_wday = __wd.c_encoding(); + return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm, + (char)__conv, 'O'); + } + unsigned __wdi = __conv == 'u' ? __wd.iso_encoding() : __wd.c_encoding(); - basic_string<_CharT> __s(1, _S_digit(__wdi)); - if (__mod) - _S_altnum(_M_locale(__ctx), __s); - return __format::__write(std::move(__out), __string_view(__s)); - return __out; + const _CharT __d = _S_digit(__wdi); + return __format::__write(std::move(__out), __string_view(&__d, 1)); } template @@ -1151,6 +1223,18 @@ namespace __format auto __d = _S_days(__t); using _TDays = decltype(__d); // Either sys_days or local_days. + if (__mod) [[unlikely]] + { + const year_month_day __ymd(__d); + const year __y = __ymd.year(); + struct tm __tm{}; + __tm.tm_year = (int)__y - 1900; + __tm.tm_yday = (__d - _TDays(__y/January/1)).count(); + __tm.tm_wday = weekday(__d).c_encoding(); + return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm, + (char)__conv, 'O'); + } + _TDays __first; // First day of week 1. if (__conv == 'V') // W01 begins on Monday before first Thursday. { @@ -1172,9 +1256,6 @@ namespace __format } auto __weeks = chrono::floor(__d - __first); __string_view __sv = _S_two_digits(__weeks.count() + 1); - basic_string<_CharT> __s; - if (__mod) - __sv = _S_altnum(_M_locale(__ctx), __s.assign(__sv)); return __format::__write(std::move(__out), __sv); } @@ -1202,11 +1283,12 @@ namespace __format template typename _FormatContext::iterator - _M_X(const _Tp& __t, typename _FormatContext::iterator __out, + _M_X(const _Tp& __tt, typename _FormatContext::iterator __out, _FormatContext& __ctx, bool __mod = false) const { // %X Locale's time rep // %EX Locale's alternative time representation. + auto __t = _S_floor_seconds(__tt); locale __loc = _M_locale(__ctx); const auto& __tp = use_facet<__timepunct<_CharT>>(__loc); const _CharT* __time_reps[2]; @@ -1225,7 +1307,7 @@ namespace __format template typename _FormatContext::iterator _M_z(const _Tp& __t, typename _FormatContext::iterator __out, - _FormatContext& __ctx, bool __mod = false) const + _FormatContext&, bool __mod = false) const { using ::std::chrono::__detail::__utc_leap_second; using ::std::chrono::__detail::__local_time_fmt; @@ -1289,9 +1371,9 @@ namespace __format else { string_view __sv = *__t._M_abbrev; - basic_string<_CharT> __ws(__sv.size()); + basic_string<_CharT> __ws(__sv.size(), _CharT()); auto& __ct = use_facet>(_M_locale(__ctx)); - __ct.widen(__sv.data(), __sv.size(), __ws.data()); + __ct.widen(__sv.begin(), __sv.end(), __ws.data()); __wsv = __ws; } return __format::__write(std::move(__out), __wsv); @@ -1329,39 +1411,6 @@ namespace __format }; } - // Convert a numeric string to the locale's alternative numeric symbols. - static basic_string_view<_CharT> - _S_altnum(const locale& __loc, basic_string<_CharT>& __s, - bool __is_neg = false) - { - if (__loc == locale::classic()) - return __s; - -#if 0 // TODO how can we access numpunct_cache?! Need to go via std::time_put? - auto& __np = use_facet<__numpunct_cache<_CharT>>(__loc); - auto __nums = __np._M_atoms_out; // alts for "-+xX01234..." - if (__is_neg) - __s[0] = __nums[0]; - __nums += 4; // now points to alternate digits - for (int __i = __is_neg; __i < __s.size(); ++__i) - __s[__i] = __nums[__s[__i] - '0']; -#endif - return __s; - } - - // Write two digits, zero-filled. - template - typename _FormatContext::iterator - _S_dd_zero_fill(int __val, typename _FormatContext::iterator __out, - _FormatContext& __ctx, bool __alt_num) const - { - auto __sv = _S_two_digits(__val); - basic_string<_CharT> __s; - if (__alt_num) - __sv = _S_altnum(_M_locale(__ctx), __s.assign(__sv)); - return __format::__write(std::move(__out), __sv); - } - // Accessors for the components of chrono types: // Returns a hh_mm_ss. @@ -1490,6 +1539,38 @@ namespace __format else return weekday(_S_days(__t)); } + + // Remove subsecond precision from a time_point. + template + static auto + _S_floor_seconds(const _Tp& __t) + { + using chrono::__detail::__local_time_fmt; + if constexpr (chrono::__is_time_point_v<_Tp>) + if constexpr (_Tp::period::den != 1) + return chrono::floor(__t); + else + return __t; + else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>) + return _S_floor_seconds(__t._M_time); + else + return __t; + } + + // Use the formatting locale's std::time_put facet to produce + // a locale-specific representation. + template + _Iter + _M_locale_fmt(_Iter __out, const locale& __loc, const struct tm& __tm, + char __fmt, char __mod) const + { + basic_ostringstream<_CharT> __os; + const auto& __tp = use_facet>(__loc); + __tp.put(__os, __os, _S_space, &__tm, __fmt, __mod); + if (__os) + __out = __format::__write(std::move(__out), __os.view()); + return __out; + } }; } // namespace __format diff --git a/libstdc++-v3/testsuite/std/time/clock/system/io.cc b/libstdc++-v3/testsuite/std/time/clock/system/io.cc index e7feebc9a3c..7bb6851c7de 100644 --- a/libstdc++-v3/testsuite/std/time/clock/system/io.cc +++ b/libstdc++-v3/testsuite/std/time/clock/system/io.cc @@ -47,22 +47,30 @@ test_format() " | %H | %I | %j | %m | %M | %p | %r | %R" " | %S | %T | %u | %U | %V | %w | %W | %x" " | %X | %y | %Y | %z | %Z}", t); - VERIFY( s == "Mon | Monday | Dec | December | Mon Dec 19 17:26:25.708 2022" + VERIFY( s == "Mon | Monday | Dec | December | Mon Dec 19 17:26:25 2022" " | 20 | 19 | 12/19/22 | 19 | 2022-12-19 | 22 | 2022 | Dec" - " | 17 | 05 | 353 | 12 | 26 | PM | 05:26:25.708 PM | 17:26" + " | 17 | 05 | 353 | 12 | 26 | PM | 05:26:25 PM | 17:26" " | 25.708 | 17:26:25.708 | 1 | 51 | 51 | 1 | 51 | 12/19/22" - " | 17:26:25.708 | 22 | 2022 | +0000 | UTC" ); + " | 17:26:25 | 22 | 2022 | +0000 | UTC" ); std::wstring ws = std::format(L"{:%a | %A | %b | %B | %c" " | %C | %d | %D | %e | %F | %g | %G | %h" " | %H | %I | %j | %m | %M | %p | %r | %R" " | %S | %T | %u | %U | %V | %w | %W | %x" " | %X | %y | %Y | %z | %Z}", t); - VERIFY( ws == L"Mon | Monday | Dec | December | Mon Dec 19 17:26:25.708 2022" + VERIFY( ws == L"Mon | Monday | Dec | December | Mon Dec 19 17:26:25 2022" " | 20 | 19 | 12/19/22 | 19 | 2022-12-19 | 22 | 2022 | Dec" - " | 17 | 05 | 353 | 12 | 26 | PM | 05:26:25.708 PM | 17:26" + " | 17 | 05 | 353 | 12 | 26 | PM | 05:26:25 PM | 17:26" " | 25.708 | 17:26:25.708 | 1 | 51 | 51 | 1 | 51 | 12/19/22" - " | 17:26:25.708 | 22 | 2022 | +0000 | UTC" ); + " | 17:26:25 | 22 | 2022 | +0000 | UTC" ); + + auto loc = std::locale::classic(); + auto smod = std::format(loc, "{:%Ec %EC %Od %Oe %OH %OI %Om %OM %OS %Ou %OU" + " %Ow %OW %Ex %EX %Oy %Ey %EY %Ez %Oz}", t); + s = std::format("{:%c %C %d %e %H %I %m %M %S %u %U" + " %w %W %x %X %y %y %Y +00:00 +00:00}", + std::chrono::time_point_cast(t)); + VERIFY( smod == s ); } int main() diff --git a/libstdc++-v3/testsuite/std/time/clock/utc/io.cc b/libstdc++-v3/testsuite/std/time/clock/utc/io.cc index 933cba65f44..977643f1147 100644 --- a/libstdc++-v3/testsuite/std/time/clock/utc/io.cc +++ b/libstdc++-v3/testsuite/std/time/clock/utc/io.cc @@ -89,18 +89,18 @@ test_format() } std::string s = ss.str(); - VERIFY( s == "Mon | Monday | Dec | December | Mon Dec 19 17:26:25.708 2022" + VERIFY( s == "Mon | Monday | Dec | December | Mon Dec 19 17:26:25 2022" " | 20 | 19 | 12/19/22 | 19 | 2022-12-19 | 22 | 2022 | Dec" - " | 17 | 05 | 353 | 12 | 26 | PM | 05:26:25.708 PM | 17:26" + " | 17 | 05 | 353 | 12 | 26 | PM | 05:26:25 PM | 17:26" " | 25.708 | 17:26:25.708 | 1 | 51 | 51 | 1 | 51 | 12/19/22" - " | 17:26:25.708 | 22 | 2022 | +0000 | UTC | " ); + " | 17:26:25 | 22 | 2022 | +0000 | UTC | " ); std::wstring ws = wss.str(); - VERIFY( ws == L"Mon | Monday | Dec | December | Mon Dec 19 17:26:25.708 2022" + VERIFY( ws == L"Mon | Monday | Dec | December | Mon Dec 19 17:26:25 2022" " | 20 | 19 | 12/19/22 | 19 | 2022-12-19 | 22 | 2022 | Dec" - " | 17 | 05 | 353 | 12 | 26 | PM | 05:26:25.708 PM | 17:26" + " | 17 | 05 | 353 | 12 | 26 | PM | 05:26:25 PM | 17:26" " | 25.708 | 17:26:25.708 | 1 | 51 | 51 | 1 | 51 | 12/19/22" - " | 17:26:25.708 | 22 | 2022 | +0000 | UTC | " ); + " | 17:26:25 | 22 | 2022 | +0000 | UTC | " ); std::chrono::utc_seconds leap(1483228800s + 26s); // 1 Jan 2017 s = std::format("{:%T}", leap - 1s); diff --git a/libstdc++-v3/testsuite/std/time/zoned_time/io.cc b/libstdc++-v3/testsuite/std/time/zoned_time/io.cc new file mode 100644 index 00000000000..ad85122e85d --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/zoned_time/io.cc @@ -0,0 +1,64 @@ +// { dg-options "-std=gnu++20" } +// { dg-do run { target c++20 } } +// { dg-require-effective-target cxx11_abi } + +#include +#include +#include + +void +test_ostream() +{ + using namespace std::chrono; + std::stringstream ss; + zoned_time zt("America/New_York", sys_seconds{946'706'523s}); + ss << zt; + VERIFY( ss.str() == "2000-01-01 01:02:03 EST" ); +} + +void +test_format() +{ + using namespace std::chrono; + sys_time t(1671470785708ms); + auto zone = "America/New_York"; + zoned_time zt(zone, t); + + // Every conversion specifier is valid for a sys_time except %q and %Q. + + std::string s = std::format("{:%a | %A | %b | %B | %c" + " | %C | %d | %D | %e | %F | %g | %G | %h" + " | %H | %I | %j | %m | %M | %p | %r | %R" + " | %S | %T | %u | %U | %V | %w | %W | %x" + " | %X | %y | %Y | %z | %Z}", zt); + VERIFY( s == "Mon | Monday | Dec | December | Mon Dec 19 12:26:25 2022" + " | 20 | 19 | 12/19/22 | 19 | 2022-12-19 | 22 | 2022 | Dec" + " | 12 | 12 | 353 | 12 | 26 | PM | 12:26:25 PM | 12:26" + " | 25.708 | 12:26:25.708 | 1 | 51 | 51 | 1 | 51 | 12/19/22" + " | 12:26:25 | 22 | 2022 | -0500 | EST" ); + + std::wstring ws = std::format(L"{:%a | %A | %b | %B | %c" + " | %C | %d | %D | %e | %F | %g | %G | %h" + " | %H | %I | %j | %m | %M | %p | %r | %R" + " | %S | %T | %u | %U | %V | %w | %W | %x" + " | %X | %y | %Y | %z | %Z}", zt); + VERIFY( ws == L"Mon | Monday | Dec | December | Mon Dec 19 12:26:25 2022" + " | 20 | 19 | 12/19/22 | 19 | 2022-12-19 | 22 | 2022 | Dec" + " | 12 | 12 | 353 | 12 | 26 | PM | 12:26:25 PM | 12:26" + " | 25.708 | 12:26:25.708 | 1 | 51 | 51 | 1 | 51 | 12/19/22" + " | 12:26:25 | 22 | 2022 | -0500 | EST" ); + + auto loc = std::locale::classic(); + auto smod = std::format(loc, "{:%Ec %EC %Od %Oe %OH %OI %Om %OM %OS %Ou %OU" + " %Ow %OW %Ex %EX %Oy %Ey %EY %Ez %Oz}", zt); + s = std::format("{:%c %C %d %e %H %I %m %M %S %u %U" + " %w %W %x %X %y %y %Y -05:00 -05:00}", + zoned_time(zone, time_point_cast(t))); + VERIFY( smod == s ); +} + +int main() +{ + test_ostream(); + test_format(); +}