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