Similarly to issue reported for %c in PR117214, the format string for locale
specific time (%r, %X) and date (%x) representations may contain specifiers
not accepted by chrono-spec, leading to exception being thrown. This
happened for following conversion specifier and locale combinations:
 * %r, %X for aa_DJ.UTF-8, ar_SA.UTF-8
 * %x for ca_AD.UTF-8, my_MM.UTF-8

This fix follows approach from r15-8490-gc24a1d5, and uses time_put to emit
localized date format. The existing _M_c is reworked to handle all locale
dependent conversion specifies, by accepting them as argument. It is also
renamed to _M_c_r_x_X.

        PR libstdc++/120648

libstdc++-v3/ChangeLog:

        * include/bits/chrono_io.h (__formatter_chrono::_M_format_to):
        Handle %c, %r, %x and %X by passing them to _M_c_r_x_X.
        (__formatter_chrono::_M_c_r_x_X): Reworked from _M_c.
        (__formatter_chrono::_M_c): Renamed into above.
        (__formatter_chrono::_M_r, __formatter_chrono::_M_x)
        (__formatter_chrono::_M_X): Removed.
        * testsuite/std/time/format/pr117214.cc: New tests for %r, %x,
        %X with date, time and durations.
---
Testing on x86_64-linux. Alll std/time* tests passed.
OK for trunk if all tests passes?

 libstdc++-v3/include/bits/chrono_io.h         | 128 +++++-------------
 .../testsuite/std/time/format/pr117214.cc     |  43 +++++-
 2 files changed, 72 insertions(+), 99 deletions(-)

diff --git a/libstdc++-v3/include/bits/chrono_io.h 
b/libstdc++-v3/include/bits/chrono_io.h
index 81ec4414f48..abbf4efcc3b 100644
--- a/libstdc++-v3/include/bits/chrono_io.h
+++ b/libstdc++-v3/include/bits/chrono_io.h
@@ -688,7 +688,10 @@ namespace __format
                  __out = _M_b_B(__t, std::move(__out), __fc, __c == 'B');
                  break;
                case 'c':
-                 __out = _M_c(__t, std::move(__out), __fc, __mod == 'E');
+               case 'r':
+               case 'x':
+               case 'X':
+                 __out = _M_c_r_x_X(__t, std::move(__out), __fc, __c, __mod);
                  break;
                case 'C':
                case 'y':
@@ -739,9 +742,6 @@ namespace __format
                    __throw_format_error("chrono format error: argument is "
                                         "not a duration");
                  break;
-               case 'r':
-                 __out = _M_r(__t, __print_sign(), __fc);
-                 break;
                case 'R':
                case 'T':
                  __out = _M_R_T(__t, __print_sign(), __fc, __c == 'T');
@@ -759,12 +759,6 @@ namespace __format
                  __out = _M_U_V_W(__t, std::move(__out), __fc, __c,
                                   __mod == 'O');
                  break;
-               case 'x':
-                 __out = _M_x(__t, std::move(__out), __fc, __mod == 'E');
-                 break;
-               case 'X':
-                 __out = _M_X(__t, __print_sign(), __fc, __mod == 'E');
-                 break;
                case 'z':
                  __out = _M_z(__t, std::move(__out), __fc, (bool)__mod);
                  break;
@@ -954,11 +948,16 @@ namespace __format
 
       template<typename _Tp, typename _FormatContext>
        typename _FormatContext::iterator
-       _M_c(const _Tp& __t, typename _FormatContext::iterator __out,
-            _FormatContext& __ctx, bool __mod = false) const
+       _M_c_r_x_X(const _Tp& __t, typename _FormatContext::iterator __out,
+                  _FormatContext& __ctx, _CharT __conv, _CharT __mod) 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;
          using ::std::chrono::__detail::__utc_leap_second;
@@ -992,23 +991,30 @@ namespace __format
            __tm.tm_zone = const_cast<char*>("UTC");
 #endif
 
-         auto __d = _S_days(__t); // Either sys_days or local_days.
-         using _TDays = decltype(__d);
-         const year_month_day __ymd(__d);
-         const auto __y = __ymd.year();
-         const auto __hms = _S_hms(__t);
+         if (__conv == 'c' || __conv == 'x')
+         {
+           auto __d = _S_days(__t); // Either sys_days or local_days.
+           using _TDays = decltype(__d);
+           const year_month_day __ymd(__d);
+           const auto __y = __ymd.year();
+
+           __tm.tm_year = (int)__y - 1900;
+           __tm.tm_yday = (__d - _TDays(__y/January/1)).count();
+           __tm.tm_mon = (unsigned)__ymd.month() - 1;
+           __tm.tm_mday = (unsigned)__ymd.day();
+           __tm.tm_wday = weekday(__d).c_encoding();
+         }
+
+         if (__conv != 'x')
+         {
+           const auto __hms = _S_hms(__t);
+           __tm.tm_hour = __hms.hours().count();
+           __tm.tm_min = __hms.minutes().count();
+           __tm.tm_sec = __hms.seconds().count();
+         }
 
-         __tm.tm_year = (int)__y - 1900;
-         __tm.tm_yday = (__d - _TDays(__y/January/1)).count();
-         __tm.tm_mon = (unsigned)__ymd.month() - 1;
-         __tm.tm_mday = (unsigned)__ymd.day();
-         __tm.tm_wday = weekday(__d).c_encoding();
-         __tm.tm_hour = __hms.hours().count();
-         __tm.tm_min = __hms.minutes().count();
-         __tm.tm_sec = __hms.seconds().count();
-
-         return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm, 'c',
-                              __mod ? 'E' : '\0');
+         return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm,
+                              __conv, __mod);
        }
 
       template<typename _Tp, typename _FormatContext>
@@ -1347,25 +1353,6 @@ namespace __format
 
       // %Q handled in _M_format
 
-      template<typename _Tp, typename _FormatContext>
-       typename _FormatContext::iterator
-       _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;
-         __tp._M_am_pm_format(&__ampm_fmt);
-         basic_string<_CharT> __fmt(_S_empty_spec);
-         __fmt.insert(1u, 1u, _S_colon);
-         __fmt.insert(2u, __ampm_fmt);
-         using _FmtStr = _Runtime_format_string<_CharT>;
-         return _M_write(std::move(__out), __loc,
-                         std::format(__loc, _FmtStr(__fmt), __t));
-       }
-
       template<typename _Tp, typename _FormatContext>
        typename _FormatContext::iterator
        _M_R_T(const _Tp& __t, typename _FormatContext::iterator __out,
@@ -1545,53 +1532,6 @@ namespace __format
          return __format::__write(std::move(__out), __sv);
        }
 
-      template<typename _Tp, typename _FormatContext>
-       typename _FormatContext::iterator
-       _M_x(const _Tp& __t, typename _FormatContext::iterator __out,
-            _FormatContext& __ctx, bool __mod = false) const
-       {
-         // %x  Locale's date rep
-         // %Ex Locale's alternative date representation.
-         locale __loc = _M_locale(__ctx);
-         const auto& __tp = use_facet<__timepunct<_CharT>>(__loc);
-         const _CharT* __date_reps[2];
-         __tp._M_date_formats(__date_reps);
-         const _CharT* __rep = __date_reps[__mod];
-         if (!*__rep)
-           return _M_D(__t, std::move(__out), __ctx);
-
-         basic_string<_CharT> __fmt(_S_empty_spec);
-         __fmt.insert(1u, 1u, _S_colon);
-         __fmt.insert(2u, __rep);
-         using _FmtStr = _Runtime_format_string<_CharT>;
-         return _M_write(std::move(__out), __loc,
-                         std::format(__loc, _FmtStr(__fmt), __t));
-       }
-
-      template<typename _Tp, typename _FormatContext>
-       typename _FormatContext::iterator
-       _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];
-         __tp._M_time_formats(__time_reps);
-         const _CharT* __rep = __time_reps[__mod];
-         if (!*__rep)
-           return _M_R_T(__t, std::move(__out), __ctx, true);
-
-         basic_string<_CharT> __fmt(_S_empty_spec);
-         __fmt.insert(1u, 1u, _S_colon);
-         __fmt.insert(2u, __rep);
-         using _FmtStr = _Runtime_format_string<_CharT>;
-         return _M_write(std::move(__out), __loc,
-                         std::format(__loc, _FmtStr(__fmt), __t));
-       }
-
       template<typename _Tp, typename _FormatContext>
        typename _FormatContext::iterator
        _M_z(const _Tp& __t, typename _FormatContext::iterator __out,
diff --git a/libstdc++-v3/testsuite/std/time/format/pr117214.cc 
b/libstdc++-v3/testsuite/std/time/format/pr117214.cc
index 87c703dd255..79109c39151 100644
--- a/libstdc++-v3/testsuite/std/time/format/pr117214.cc
+++ b/libstdc++-v3/testsuite/std/time/format/pr117214.cc
@@ -7,10 +7,14 @@
 
 #include <chrono>
 #include <locale>
+#include <span>
 #include <testsuite_hooks.h>
 
+
+template<typename ChronoType>
 void
-test_c()
+test_locale_formats(const ChronoType& t,
+                   std::span<const char* const> test_specifiers)
 {
   const char *test_locales[] = {
     "aa_DJ.UTF-8",
@@ -19,15 +23,44 @@ test_c()
     "az_IR.UTF-8",
     "my_MM.UTF-8",
   };
-  std::chrono::sys_seconds t{std::chrono::seconds{1}};
 
+  auto format_args = std::make_format_args(t);
   for (auto locale_name : test_locales)
   {
-    auto s = std::format(std::locale(locale_name), "{:L%c}", t);
-    VERIFY( !s.empty() );
+    std::locale loc(locale_name);
+    for (auto specifier : test_specifiers)
+    {
+      auto s = std::vformat(loc, specifier, format_args);
+      VERIFY( !s.empty() );
+    }
   }
 }
 
+void
+test_locale_formats()
+{
+  using namespace std::chrono;
+
+  const char* test_specifiers[] = {
+    "{:L%x}", "{:L%Ex}",
+    "{:L%c}", "{:L%Ec}",
+    "{:L%X}", "{:L%EX}",
+    "{:L%r}",
+  };
+  auto date_time_specifiers = std::span(test_specifiers);
+  auto date_specifiers = date_time_specifiers.subspan(0, 2);
+  auto time_specifiers = date_time_specifiers.subspan(4);
+
+  auto ymd = 2020y/November/12d;
+  test_locale_formats(ymd, date_specifiers);
+
+  auto tod = 25h + 10min + 12s;
+  test_locale_formats(tod, time_specifiers);
+
+  auto tp = sys_days(ymd) + tod;
+  test_locale_formats(tp, date_time_specifiers);
+}
+
 #include <stdlib.h>
 #include <time.h>
 
@@ -93,7 +126,7 @@ test_c_local()
 
 int main()
 {
-  test_c();
+  test_locale_formats();
   test_c_zoned();
   test_c_local();
 }
-- 
2.49.0

Reply via email to