aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomasz Kamiński <tkaminsk@redhat.com>2025-06-13 13:28:30 +0200
committerTomasz Kamiński <tkaminsk@redhat.com>2025-06-13 17:20:30 +0200
commit33e3139f5df09a13b8b13844d8e21ef3bb4d618e (patch)
tree5d2296482d3a64c6efd58322ee44a6b1af148bff
parent5e8168a659ecc63804984d20365b4df401d9675b (diff)
downloadgcc-33e3139f5df09a13b8b13844d8e21ef3bb4d618e.zip
gcc-33e3139f5df09a13b8b13844d8e21ef3bb4d618e.tar.gz
gcc-33e3139f5df09a13b8b13844d8e21ef3bb4d618e.tar.bz2
libstdc++: Format %r, %x and %X using locale's time_put facet [PR120648]
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.
-rw-r--r--libstdc++-v3/include/bits/chrono_io.h128
-rw-r--r--libstdc++-v3/testsuite/std/time/format/pr117214.cc43
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 81ec441..abbf4efc 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>
@@ -1349,25 +1355,6 @@ namespace __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,
_FormatContext& __ctx, bool __secs) const
{
@@ -1547,53 +1534,6 @@ namespace __format
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,
_FormatContext&, bool __mod = false) const
{
diff --git a/libstdc++-v3/testsuite/std/time/format/pr117214.cc b/libstdc++-v3/testsuite/std/time/format/pr117214.cc
index 87c703d..79109c3 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();
}