aboutsummaryrefslogtreecommitdiff
path: root/libstdc++-v3/include
diff options
context:
space:
mode:
authorJonathan Wakely <jwakely@redhat.com>2024-07-26 18:11:26 +0100
committerJonathan Wakely <redi@gcc.gnu.org>2024-07-30 21:14:28 +0100
commit8f05ada7dfb9a40d4333a2aa9ccb5ddcdf8e2b06 (patch)
tree7c26413c30662d43baadcd2380a9bc1bb8256c0b /libstdc++-v3/include
parenta9e472c6b748abde55b5ecde2e2d98dcb2f96ded (diff)
downloadgcc-8f05ada7dfb9a40d4333a2aa9ccb5ddcdf8e2b06.zip
gcc-8f05ada7dfb9a40d4333a2aa9ccb5ddcdf8e2b06.tar.gz
gcc-8f05ada7dfb9a40d4333a2aa9ccb5ddcdf8e2b06.tar.bz2
libstdc++: Fix std::format output for std::chrono::zoned_time
When formatting a chrono::zoned_time with an empty chrono-specs, we were only formatting its _M_time member, but the ostream insertion operator uses the format "{:L%F %T %Z}" which includes the time zone abbreviation. The %Z should also be used when formatting with an empty chrono-specs. This commit makes _M_format_to_ostream handle __local_time_fmt specializations directly, rather than calling itself recursively to format the _M_time member. We need to be able to customize the output of _M_format_to_ostream for __local_time_fmt, because we use that type for gps_time and tai_time as well as for zoned_time and __local_time_fmt. When formatting gps_time and tai_time we don't want to include the time zone abbreviation in the "{}" output, but for zoned_time we do want to. We can reuse the __is_neg flag passed to _M_format_to_ostream (via _M_format) to say that we want the time zone abbreviation. Currently the __is_neg flag is only used for duration specializations, so it's available for __local_time_fmt to use. In addition to fixing the zoned_time output to use %Z, this commit also changes the __local_time_fmt output to use %Z. Previously it didn't use it, just like zoned_time. The standard doesn't actually say how to format local-time-format-t for an empty chrono-specs, but this behaviour seems sensible and is what I'm proposing as part of LWG 4124. While testing this I noticed that some chrono types were not being tested with empty chrono-specs, so this adds more tests. I also noticed that std/time/clock/local/io.cc was testing tai_time instead of local_time, which was completely wrong. That's fixed now too. libstdc++-v3/ChangeLog: * include/bits/chrono_io.h (__local_fmt_t): Remove unused declaration. (__formatter_chrono::_M_format_to_ostream): Add explicit handling for specializations of __local_time_fmt, including the time zone abbreviation in the output if __is_neg is true. (formatter<chrono::tai_time<D>>::format): Add comment. (formatter<chrono::gps_time<D>>::format): Likewise. (formatter<chrono::__detail::__local_time_fmt::format): Call _M_format with true for the __is_neg flag. * testsuite/std/time/clock/gps/io.cc: Remove unused variable. * testsuite/std/time/clock/local/io.cc: Fix test error that checked tai_time instead of local_time. Add tests for local-time-format-t formatting. * testsuite/std/time/clock/system/io.cc: Check empty chrono-specs. * testsuite/std/time/clock/tai/io.cc: Likewise. * testsuite/std/time/zoned_time/io.cc: Likewise.
Diffstat (limited to 'libstdc++-v3/include')
-rw-r--r--libstdc++-v3/include/bits/chrono_io.h46
1 files changed, 35 insertions, 11 deletions
diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h
index 72c66a0..e7e7deb 100644
--- a/libstdc++-v3/include/bits/chrono_io.h
+++ b/libstdc++-v3/include/bits/chrono_io.h
@@ -156,6 +156,7 @@ namespace __detail
namespace __detail
{
// An unspecified type returned by `chrono::local_time_format`.
+ // This is called `local-time-format-t` in the standard.
template<typename _Duration>
struct __local_time_fmt
{
@@ -163,8 +164,6 @@ namespace __detail
const string* _M_abbrev;
const seconds* _M_offset_sec;
};
-
- struct __local_fmt_t;
}
/// @endcond
@@ -695,13 +694,34 @@ namespace __format
using ::std::chrono::__detail::__utc_leap_second;
using ::std::chrono::__detail::__local_time_fmt;
+ basic_ostringstream<_CharT> __os;
+ __os.imbue(_M_locale(__fc));
+
if constexpr (__is_specialization_of<_Tp, __local_time_fmt>)
- return _M_format_to_ostream(__t._M_time, __fc, false);
- else
{
- basic_ostringstream<_CharT> __os;
- __os.imbue(_M_locale(__fc));
+ // Format as "{:L%F %T}"
+ auto __days = chrono::floor<chrono::days>(__t._M_time);
+ __os << chrono::year_month_day(__days) << ' '
+ << chrono::hh_mm_ss(__t._M_time - __days);
+ // For __local_time_fmt the __is_neg flags says whether to
+ // append " %Z" to the result.
+ if (__is_neg)
+ {
+ if (!__t._M_abbrev) [[unlikely]]
+ __format::__no_timezone_available();
+ else if constexpr (is_same_v<_CharT, char>)
+ __os << ' ' << *__t._M_abbrev;
+ else
+ {
+ __os << L' ';
+ for (char __c : *__t._M_abbrev)
+ __os << __c;
+ }
+ }
+ }
+ else
+ {
if constexpr (__is_specialization_of<_Tp, __utc_leap_second>)
__os << __t._M_date << ' ' << __t._M_time;
else if constexpr (chrono::__is_time_point_v<_Tp>)
@@ -727,11 +747,11 @@ namespace __format
__os << _S_plus_minus[1];
__os << __t;
}
-
- auto __str = std::move(__os).str();
- return __format::__write_padded_as_spec(__str, __str.size(),
- __fc, _M_spec);
}
+
+ auto __str = std::move(__os).str();
+ return __format::__write_padded_as_spec(__str, __str.size(),
+ __fc, _M_spec);
}
static constexpr const _CharT* _S_chars
@@ -2008,6 +2028,8 @@ namespace __format
_FormatContext& __fc) const
{
// Convert to __local_time_fmt with abbrev "TAI" and offset 0s.
+ // We use __local_time_fmt and not sys_time (as the standard implies)
+ // because %Z for sys_time would print "UTC" and we want "TAI" here.
// Offset is 1970y/January/1 - 1958y/January/1
constexpr chrono::days __tai_offset = chrono::days(4383);
@@ -2038,6 +2060,8 @@ namespace __format
_FormatContext& __fc) const
{
// Convert to __local_time_fmt with abbrev "GPS" and offset 0s.
+ // We use __local_time_fmt and not sys_time (as the standard implies)
+ // because %Z for sys_time would print "UTC" and we want "GPS" here.
// Offset is 1980y/January/Sunday[1] - 1970y/January/1
constexpr chrono::days __gps_offset = chrono::days(3657);
@@ -2104,7 +2128,7 @@ namespace __format
typename _FormatContext::iterator
format(const chrono::__detail::__local_time_fmt<_Duration>& __t,
_FormatContext& __ctx) const
- { return _M_f._M_format(__t, __ctx); }
+ { return _M_f._M_format(__t, __ctx, /* use %Z for {} */ true); }
private:
__format::__formatter_chrono<_CharT> _M_f;