[PATCH v2 1/2] libstdc++: Type-erase chrono-data for formatting [PR110739]
Jonathan Wakely
jwakely@redhat.com
Thu Jun 26 12:25:56 GMT 2025
On 26/06/25 11:39 +0200, Tomasz Kamiński wrote:
>This patch reworks the formatting for the chrono types, such that they are all
>formatted in terms of _ChronoData class, that includes all required fields.
>Populating each required field is performed in formatter for specific type,
>based on the chrono-spec used.
>
>To facilitate above, the _ChronoSpec now includes additional _M_needed field,
>that represnts the chrono data that is referenced by format spec (this value
>is also configured for __defSpec). This value differs from the value of
>__parts passed to _M_parse, which does include all fields that can be computed
>from input (e.g. weekday_indexed can be computed for year_month_day). Later
>it is used to fill _ChronoData, in particular _M_fill_* family of functions,
>to determine if given field needs to be set, and thus it's value needs to be
"its"
>computed.
>
>In consequence _ChronoParts enum was exteneded with additional values,
"extended"
>that allows more fine grained indentification:
"identification"
> * _TimeOfDay is separated into _HoursMinutesSeconds and _Subseconds,
> * _TimeZone is separated into _ZoneAbbrev and _ZoneOffset,
> * _LocalDays, _WeekdayIndex are defiend in included in _Date,
"defined"
> * _Duration is removed, and instead _EpochUnits and _UnitSuffix are
> introduced.
>Furthermore, to avoid name conflicts _ChonoParts is now defined as enum class,
>with additional operators that simplify uses.
I don't love overloading operator- to mean clearing bits, but it does
make clearing the bits very convenient. Maybe just add a comment
before operator-(_ChronoParts x, _ChronoParts y) saying that it
returns a copy of x with all bits from y unset. That comment will be
know that's what the function body
(Which is x&(x^y) I think, right?)
>
>In addition to fields that can be printed using chron-spec, _ChronoData stores:
"chrono-spec"
> * Total days in wall time (_M_ldays), day of year (_M_day_of_year) - used by
> struct tm construction, and for ISO calendar computation.
> * Total seconds in wall time (_M_lseconds) - this value may be different from
> sum of days, hours, minutes, seconds (e.g. see utc_time below). Included
> to allow future extension, like printing total minutes.
> * Total seconds since epoch - due offset different from above. Again to be
> used with future extension (e.g. %s as proposed in P2945R1).
> * Subseconds - count of attoseconds (10^(-18)), in addition to priting can
"printing"
> be used to compute fractional hours, minutes.
>The both total seconds fielkds we use single _TotalSeconds enumerator in
"fields"
>_ChronoParts, that when present in combination with _EpochUnits or _LocalDays
>indicates that _M_eseconds (_EpochSeconds) or _M_lseconds (_LocalSeconds) are
>provided/required.
>
>To handle type formatting of time since epoch ('%Q'|_EpochUnits), we use the
>format_args mechanism, where the result of +d.count() (see LWG4118) is erased
>into make_format_args to local __arg_store, that is later referenced by
>_M_ereps (_M_ereps.get(0)).
>
>To handle precision values, and in prepartion to allow user to configure ones,
>we store the precision as third element of _M_ereps (_M_ereps.get(2)), this
>allows duration with precision to be printed using "{0:{2}}". For subseconds
>the precision is handled differently depending on the representation:
> * for integral reps, _M_subseconds value is used to determine fractional value,
> precision is trimmed to 18 digits;
> * for floating-points, we _M_ereps stores duration<Rep> initialized with only
Strike "we"?
> fractional seconds, that is later formatted with precision.
>Always using _M_subseconds fields for integral duration, means that we do not
>use formattter for user-defined durations that are considered to be integral
>(see empty_spec.cc file change). To avoid potentially expensive computation
>of _M_subseconds, we make sure that _ChronoParts::_Subseconds is set only if
>_Subseconds are needed. In particular we remove this flag for localized ouput
>in _M_parse.
>
>Construction the _M_ereps as described above is handled by __formatter_duration,
"Construction of the"
>that is then used to format duration, hh_mm_ss and time_points specialization.
"specializations"
>This class also handles _UnitSuffix, the _M_units_suffix field is populated
>either with predefined suffix (chrono::__detail::__units_suffix) or one produced
>locally.
>
>Finally, formatters for types listed below contains type specific logic:
> * hh_mm_ss - we do not compute total duration and seconds, unless explicitly
> requested, as such computation may overflow;
> * utc_time - for time during leap second insertion, the _M_seconds field is
> increased to 60;
> * __local_time_fmt - exception is thrown if zone offset (_ZoneOffset) or
> abbrevation (_ZoneAbbrev) is requsted, but corresponding pointer is null,
> futhermore conversion from `char` to `wchar_t` for abbreviation is performed
> if needed.
I had other comments about the benefits of this change, and the
unintended change that Michael noticed, but your [PATCH 0/2] covers
the former and the latter is fixed in this v2 patch!
> PR libstdc++/110739
>
>libstdc++-v3/ChangeLog:
>
> * include/bits/chrono_io.h (__format::__no_timezone_available):
> Removed, replaced with separate throws in formatter for
> __local_time_fmt
> (__format::_ChronoParts): Defined additional enumertors and
> declared as enum class.
> (__format::operator&(_ChronoParts, _ChronoParts))
> (__format::operator&=(_ChronoParts&, _ChronoParts))
> (__format::operator-(_ChronoParts, _ChronoParts))
> (__format::operator-=(_ChronoParts&, _ChronoParts))
> (__format::operator==(_ChronoParts, decltype(nullptr)))
> (_ChronoSpec::_M_time_only, _ChronoSpec::_M_floating_point_rep)
> (_ChronoSpec::_M_custom_rep, _ChronoSpec::_M_needed)
> (_ChronoSpec::_M_needs, __format::_ChronoData): Define.
> (__format::__formatter_chrono): Redefine to accept _ChronoData.
> (__formatter_chrono::_M_format_to_ostream): Moved toi
> __formatter_duration.
> (__format::__formatter_duration): Define.
> (__formatter_chrono_info::format): Pass value-constructed
> _ChronoData.
> (std::formatter<chrono::day, _CharT>)
> (std::formatter<chrono::month, _CharT>)
> (std::formatter<chrono::year, _CharT>)
> (std::formatter<chrono::weekday, _CharT>)
> (std::formatter<chrono::weekday_indexed, _CharT>)
> (std::formatter<chrono::weekday_last, _CharT>)
> (std::formatter<chrono::month_day, _CharT>)
> (std::formatter<chrono::month_day_last, _CharT>)
> (std::formatter<chrono::month_weekday, _CharT>)
> (std::formatter<chrono::month_weekday_indexed, _CharT>)
> (std::formatter<chrono::month_weekday_last, _CharT>)
> (std::formatter<chrono::year_month, _CharT>)
> (std::formatter<chrono::year_month_day, _CharT>)
> (std::formatter<chrono::year_month_day_last, _CharT>)
> (std::formatter<chrono::year_month_weekday, _CharT>)
> (std::formatter<chrono::year_month_weekday_indexed, _CharT>)
> (std::formatter<chrono::year_month_weekday_last, _CharT>):
> Construct _ChronoData in format, and configure _M_needed in
> _ChronoSpec.
> (std::formatter<chrono::duration<_Rep, _Period>, _CharT>)
> (std::formatter<chrono::hh_mm_ss<_Duration>, _CharT>)
> (std::formatter<chrono::sys_time<_Duration>, _CharT>)
> (std::formatter<chrono::utc_time<_Duration>, _CharT>)
> (std::formatter<chrono::tai_time<_Duration>, _CharT>)
> (std::formatter<chrono::gps_time<_Duration>, _CharT>)
> (std::formatter<chrono::file_time<_Duration>, _CharT>)
> (std::formatter<chrono::local_time<_Duration>, _CharT>)
> (std::formatter<chrono::_detail::__local_time_fmt<_Duration>, _CharT>):
> Reworked in terms of __formatter_duration and _ChronoData.
> (std::formatter<chrono::_detail::__utc_leap_second<_Duration>, _CharT>):
> Removed.
> (_Parser<_Duration>::operator()): Adjusted for _ChronoParts
> being enum class.
> * include/std/chrono (__detail::__utc_leap_second): Removed,
> replaced with simply bumping _M_seconds in _ChronoData.
> * testsuite/std/time/format/empty_spec.cc: Updated %S integral
> ouput.
>---
>v2:
> * fixes _M_z that was priting minutes:seconds, instead of hours::seconds
Oh good catch! I didn't notice that in v1. Did you find that by
testing or inspection?
> * use uint_least64 as type of pow10_t
> * fix typo pointed by Michael Welsh Duggan
> * fix formatting
>
> libstdc++-v3/include/bits/chrono_io.h | 1952 ++++++++++-------
> libstdc++-v3/include/std/chrono | 24 -
> .../testsuite/std/time/format/empty_spec.cc | 16 +-
> 3 files changed, 1163 insertions(+), 829 deletions(-)
>
>diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h
>index 4eb00f4932d..35e95906e6a 100644
>--- a/libstdc++-v3/include/bits/chrono_io.h
>+++ b/libstdc++-v3/include/bits/chrono_io.h
>@@ -187,11 +187,6 @@ namespace __detail
> /// @cond undocumented
> namespace __format
> {
>- [[noreturn,__gnu__::__always_inline__]]
>- inline void
>- __no_timezone_available()
>- { __throw_format_error("format error: no timezone available for %Z or %z"); }
>-
> [[noreturn,__gnu__::__always_inline__]]
> inline void
> __not_valid_for_duration()
>@@ -204,40 +199,103 @@ namespace __format
> { __throw_format_error("format error: chrono-format-spec not valid for "
> "argument type"); }
>
>- template<typename _CharT>
>- struct _ChronoSpec : _Spec<_CharT>
>- {
>- // Placed in tail-padding of __format::_Spec<C>.
>- // This indicates that a locale-dependent conversion specifier such as
>- // %a is used in the chrono-specs. This is not the same as the
>- // _Spec<C>::_M_localized member which indicates that "L" was present
>- // in the format-spec, e.g. "{:L%a}" is localized and locale-specific,
>- // but "{:L}" is only localized and "{:%a}" is only locale-specific.
>- unsigned _M_locale_specific : 1;
>-
>- basic_string_view<_CharT> _M_chrono_specs;
>- };
>-
> // Represents the information provided by a chrono type.
> // e.g. month_weekday has month and weekday but no year or time of day,
> // hh_mm_ss has time of day but no date, sys_time is time_point+timezone.
>- enum _ChronoParts {
>- _Year = 1, _Month = 2, _Day = 4, _Weekday = 8, _TimeOfDay = 16,
>- _TimeZone = 32,
>- _Date = _Year | _Month | _Day | _Weekday,
>- _DateTime = _Date | _TimeOfDay,
>+ enum class _ChronoParts : unsigned short {
>+ _None = 0, _TotalSeconds = 1u, _Subseconds = 1u << 2,
>+
>+ // time since epoch
>+ _EpochUnits = 1u << 3, _UnitSuffix = 1u << 4,
>+ _EpochSeconds = _EpochUnits | _TotalSeconds,
>+
>+ // local (wall) time
>+ _LocalDays = 1u << 5,
>+ _LocalSeconds = _LocalDays | _TotalSeconds,
>+
>+ _Year = 1u << 6, _Month = 1u << 7, _Day = 1u << 8,
>+ _Weekday = 1u << 9, _WeekdayIndex = 1u << 10, _DayOfYear = 1u << 11,
>+ _IndexedWeekday = _Weekday | _WeekdayIndex,
>+ _YearMonthDay = _Year | _Month | _Day,
>+ _Date = _LocalDays | _YearMonthDay | _IndexedWeekday | _DayOfYear,
>+
>+ _HoursMinutesSeconds = 1u << 12,
>+ _TimeOfDay = _HoursMinutesSeconds | _Subseconds,
>+ _Time = _TimeOfDay | _TotalSeconds,
>+ _EpochTime = _Time | _EpochUnits | _UnitSuffix,
>+ _DateTime = _Date | _Time,
>+
>+ _ZoneAbbrev = 1u << 13, _ZoneOffset = 1u << 14,
>+ _TimeZone = _ZoneAbbrev | _ZoneOffset,
> _ZonedDateTime = _DateTime | _TimeZone,
>- _Duration = 128 // special case
> };
>
>+ [[__gnu__::__always_inline__]]
>+ constexpr _ChronoParts
>+ operator&(_ChronoParts __x, _ChronoParts __y) noexcept
>+ { return static_cast<_ChronoParts>((unsigned)__x & (unsigned)__y); }
>+
>+ [[__gnu__::__always_inline__]]
>+ constexpr _ChronoParts&
>+ operator&=(_ChronoParts& __x, _ChronoParts __y) noexcept
>+ { return __x = __x & __y; }
>+
>+ [[__gnu__::__always_inline__]]
> constexpr _ChronoParts
> operator|(_ChronoParts __x, _ChronoParts __y) noexcept
>- { return static_cast<_ChronoParts>((int)__x | (int)__y); }
>+ { return static_cast<_ChronoParts>((unsigned short)__x | (unsigned short)__y); }
>
>+ [[__gnu__::__always_inline__]]
> constexpr _ChronoParts&
> operator|=(_ChronoParts& __x, _ChronoParts __y) noexcept
> { return __x = __x | __y; }
>
>+ [[__gnu__::__always_inline__]]
>+ constexpr _ChronoParts
>+ operator-(_ChronoParts __x, _ChronoParts __y) noexcept
>+ { return static_cast<_ChronoParts>((unsigned short)__x & ~(unsigned short)__y); }
>+
>+ [[__gnu__::__always_inline__]]
>+ constexpr _ChronoParts&
>+ operator-=(_ChronoParts& __x, _ChronoParts __y) noexcept
>+ { return __x = __x - __y; }
>+
>+ [[__gnu__::__always_inline__]]
>+ constexpr bool
>+ operator==(_ChronoParts __x, decltype(nullptr)) noexcept
>+ { return (unsigned short)__x == 0; }
>+
>+ template<typename _CharT>
>+ struct _ChronoSpec : _Spec<_CharT>
>+ {
>+ // When _M_prec_kind is _WP_none, the _M_prec contains the default
>+ // value of fraction digits to be used for time '%S'.
>+
>+ // Placed in tail-padding of __format::_Spec<C>.
>+ // This indicates that a locale-dependent conversion specifier such as
>+ // %a is used in the chrono-specs. This is not the same as the
>+ // _Spec<C>::_M_localized member which indicates that "L" was present
>+ // in the format-spec, e.g. "{:L%a}" is localized and locale-specific,
>+ // but "{:L}" is only localized and "{:%a}" is only locale-specific.
>+ unsigned _M_locale_specific : 1;
>+ // Indicates that we are handling duration.
>+ unsigned _M_time_only : 1;
>+ // Indicates is duration should be treated as floating point.
"Indicates that"
>+ unsigned _M_floating_point_rep : 1;
>+ // Indicate that duration uses user-defined representation.
>+ unsigned _M_custom_rep : 1;
>+ unsigned _M_unused : 4;
>+
>+ // Chrono parts required by format specs
>+ _ChronoParts _M_needed;
>+ basic_string_view<_CharT> _M_chrono_specs;
>+
>+ [[__gnu__::__always_inline__]]
>+ constexpr bool
>+ _M_needs(_ChronoParts __parts) const
>+ { return (_M_needed & __parts) != 0; }
>+ };
>+
> template<typename _CharT>
> struct _ChronoFormats
> {
>@@ -340,6 +398,152 @@ namespace __format
> { return _S_yml().substr(3); }
> };
>
>+ template<typename _CharT>
>+ struct _ChronoData
>+ {
>+ static constexpr unsigned _S_max_prec = 18;
>+ using _Attoseconds = chrono::duration<__UINT_LEAST64_TYPE__, atto>;
>+
>+ using _FormatContext
>+ = basic_format_context<_Sink_iter<_CharT>, _CharT>;
>+ using _FormatArgs = basic_format_args<_FormatContext>;
>+ static inline auto _S_args = std::make_format_args<_FormatContext>();
>+
>+ _ChronoData() = default;
>+ _ChronoData(_ChronoData&&) = delete;
>+
>+ // time since epoch
>+ chrono::seconds _M_eseconds;
>+ // n.b. due offset being seconds or corser, local and epoch subseconds
"coarser"
>+ // has the same value
>+ _Attoseconds _M_subseconds;
>+ // _M_ereps.get(0) stores duration units
>+ // _M_ereps.get(1) stores subseconds units
>+ // _M_ereps.get(2) stores precision
>+ _FormatArgs _M_ereps = _S_args;
>+ basic_string_view<_CharT> _M_unit_suffix;
>+
>+ // local (wall) time
>+ chrono::local_seconds _M_lseconds;
>+ chrono::local_days _M_ldays;
>+
>+ chrono::year _M_year;
>+ chrono::month _M_month;
>+ chrono::day _M_day;
>+ chrono::weekday _M_weekday;
>+ unsigned char _M_weekday_index;
>+ chrono::days _M_day_of_year;
>+
>+ bool _M_is_neg;
>+ chrono::hours _M_hours;
>+ chrono::minutes _M_minutes;
>+ chrono::seconds _M_seconds;
>+
>+ chrono::seconds _M_zone_offset;
>+ basic_string_view<_CharT> _M_zone_abbrev;
>+ const char* _M_zone_cstr = "";
>+
>+ template<typename _YearMonth>
>+ [[__gnu__::__always_inline__]]
>+ _ChronoParts
>+ _M_fill_year_month(const _YearMonth& __ym, _ChronoParts __parts)
>+ {
>+ _M_year = __ym.year();
>+ __parts -= _ChronoParts::_Year;
>+ _M_month = __ym.month();
>+ __parts -= _ChronoParts::_Month;
>+ return __parts;
>+ }
>+
>+ [[__gnu__::__always_inline__]]
>+ _ChronoParts
>+ _M_fill_day(chrono::day __d, _ChronoParts __parts)
>+ {
>+ _M_day = __d;
>+ __parts -= _ChronoParts::_Day;
>+ _M_weekday_index = ((unsigned)__d + 6u) % 7u;
>+ __parts -= _ChronoParts::_WeekdayIndex;
>+ return __parts;
>+ }
>+
>+ [[__gnu__::__always_inline__]]
>+ _ChronoParts
>+ _M_fill_weekday(chrono::weekday_indexed __wi, _ChronoParts __parts)
>+ {
>+ _M_weekday = __wi.weekday();
>+ __parts -= _ChronoParts::_Weekday;
>+ _M_weekday_index = __wi.index();
>+ __parts -= _ChronoParts::_WeekdayIndex;
>+ return __parts;
>+ }
>+
>+ [[__gnu__::__always_inline__]]
>+ _ChronoParts
>+ _M_fill_aux(chrono::local_days __ld, _ChronoParts __parts)
>+ {
>+ using namespace chrono;
>+ if ((__parts & _ChronoParts::_Weekday) != 0)
>+ _M_weekday = weekday(__ld);
>+ __parts -= _ChronoParts::_Weekday;
>+ if ((__parts & _ChronoParts::_DayOfYear) != 0)
>+ // See "Calculating Ordinal Dates" at
>+ // https://github.com/HowardHinnant/date/wiki/Examples-and-Recipes
>+ _M_day_of_year = __ld - local_days(_M_year/January/0);
>+ __parts -= _ChronoParts::_DayOfYear;
>+ return __parts;
>+ }
>+
>+ [[__gnu__::__always_inline__]]
>+ _ChronoParts
>+ _M_fill_ldays(chrono::local_days __ld, _ChronoParts __parts)
>+ {
>+ _M_ldays = __ld;
>+ __parts -= _ChronoParts::_LocalDays;
>+ return _M_fill_aux(__ld, __parts);
>+ }
>+
>+ void
>+ _M_fill_time(chrono::seconds __d)
>+ {
>+ chrono::hh_mm_ss<chrono::seconds> __hms(__d);
>+ _M_hours = __hms.hours();
>+ _M_minutes = __hms.minutes();
>+ _M_seconds = __hms.seconds();
>+ }
>+
>+ void
>+ _M_fill_date_time(chrono::local_seconds __ls, _ChronoParts __parts)
>+ {
>+ _M_ldays = chrono::floor<chrono::days>(__ls);
>+ __parts -= _ChronoParts::_LocalDays;
>+ if ((__parts & _ChronoParts::_HoursMinutesSeconds) != 0)
>+ _M_fill_time(_M_lseconds - _M_ldays);
>+
>+ if ((__parts & _ChronoParts::_Date) != 0)
>+ {
>+ const chrono::year_month_day __ymd(_M_ldays);
>+ _M_fill_year_month(__ymd, __parts);
>+ _M_fill_day(__ymd.day(), __parts);
>+ _M_fill_aux(_M_ldays, __parts);
>+ }
>+ }
>+
>+ void
>+ _M_fill_zone(const char* __abbrev, const wchar_t* __wabbrev)
>+ {
>+ if constexpr (is_same_v<_CharT, char>)
>+ _M_zone_abbrev = __abbrev;
>+ else
>+ _M_zone_abbrev = __wabbrev;
>+ _M_zone_cstr = __abbrev;
>+ }
>+
>+ [[__gnu__::__always_inline__]]
>+ void
>+ _M_fill_utc_zone()
>+ { _M_fill_zone("UTC", L"UTC"); }
>+ };
>+
> // TODO rename this to chrono::__formatter? or chrono::__detail::__formatter?
> template<typename _CharT>
> struct __formatter_chrono
>@@ -357,7 +561,7 @@ namespace __format
> template<typename _ParseContext>
> constexpr typename _ParseContext::iterator
> _M_parse(_ParseContext& __pc, _ChronoParts __parts,
>- const _ChronoSpec<_CharT>& __def = {})
>+ const _ChronoSpec<_CharT>& __def)
> {
> auto __first = __pc.begin();
> auto __last = __pc.end();
>@@ -388,11 +592,18 @@ namespace __format
> if (__finished())
> return __first;
>
>- if (__parts & _ChronoParts::_Duration)
>+ if (*__first == '.')
> {
>- __first = __spec._M_parse_precision(__first, __last, __pc);
>- if (__finished())
>- return __first;
>+ if ((__parts & _ChronoParts::_EpochUnits) == 0
>+ || !__spec._M_floating_point_rep)
>+ __throw_format_error("format error: invalid precision for duration");
>+
>+ // Precision is allowed, but value is ignored.
>+ __first = _Spec<_CharT>()._M_parse_precision(__first, __last, __pc);
>+ // Still inditate that there was user supplied precision.
"indicate"
>+ __spec._M_prec_kind = _WP_value;
>+ if (__finished())
>+ return __first;
> }
>
> __spec._M_localized = false;
>@@ -418,7 +629,8 @@ namespace __format
> // against __parts (so fail for %Y if no year in parts).
> // Save range in __spec._M_chrono_specs.
> __spec._M_debug = false;
>- __spec._M_locale_specific = true;
>+ __spec._M_locale_specific = false;
>+ __spec._M_needed = _ChronoParts::_None;
> __spec._M_chrono_specs = __string_view();
>
> const auto __chrono_specs = __first++; // Skip leading '%'
>@@ -428,17 +640,18 @@ namespace __format
>
> _CharT __mod{};
> bool __conv = true;
>- int __needed = 0;
>- bool __locale_specific = false;
>-
> while (__first != __last)
> {
> enum _Mods { _Mod_none, _Mod_E, _Mod_O, _Mod_E_O };
> _Mods __allowed_mods = _Mod_none;
>
>+ _ChronoParts __needed = _ChronoParts::_None;
>+ bool __locale_specific = false;
>+
> _CharT __c = *__first++;
> switch (__c)
> {
>+ using enum _ChronoParts;
> case 'a':
> case 'A':
> __needed = _Weekday;
>@@ -451,7 +664,7 @@ namespace __format
> __locale_specific = true;
> break;
> case 'c':
>- __needed = _DateTime;
>+ __needed = _Date|_HoursMinutesSeconds;
> __allowed_mods = _Mod_E;
> __locale_specific = true;
> break;
>@@ -466,27 +679,27 @@ namespace __format
> break;
> case 'D':
> case 'F':
>- __needed = _Date;
>+ __needed = _YearMonthDay;
> break;
> case 'g':
> case 'G':
>- __needed = _Date;
>+ __needed = _LocalDays|_Weekday;
> break;
> case 'H':
> case 'I':
>- __needed = _TimeOfDay;
>+ __needed = _HoursMinutesSeconds;
> __allowed_mods = _Mod_O;
> break;
> case 'j':
>- if (!(__parts & _Duration))
>- __needed = _Date;
>+ __needed = !__spec._M_time_only ? _DayOfYear
>+ : _HoursMinutesSeconds;
This would be a tiny bit simpler without the negation, just:
__needed = __spec._M_time_only ? _HoursMinutesSeconds
: _DayOfYear;
Is there a reason to prefer !__spec._M_time_only that I'm missing?
> break;
> case 'm':
> __needed = _Month;
> __allowed_mods = _Mod_O;
> break;
> case 'M':
>- __needed = _TimeOfDay;
>+ __needed = _HoursMinutesSeconds;
> __allowed_mods = _Mod_O;
> break;
> case 'p':
>@@ -494,12 +707,16 @@ namespace __format
> __locale_specific = true;
> [[fallthrough]];
> case 'R':
>+ __needed = _HoursMinutesSeconds;
>+ break;
> case 'T':
> __needed = _TimeOfDay;
> break;
> case 'q':
>+ __needed = _UnitSuffix;
>+ break;
> case 'Q':
>- __needed = _Duration;
>+ __needed = _EpochUnits;
> break;
> case 'S':
> __needed = _TimeOfDay;
>@@ -513,7 +730,7 @@ namespace __format
> case 'U':
> case 'V':
> case 'W':
>- __needed = _Date;
>+ __needed = _LocalDays|_Year|_DayOfYear|_Weekday;
> __allowed_mods = _Mod_O;
> break;
> case 'x':
>@@ -522,7 +739,7 @@ namespace __format
> __allowed_mods = _Mod_E;
> break;
> case 'X':
>- __needed = _TimeOfDay;
>+ __needed = _HoursMinutesSeconds;
> __locale_specific = true;
> __allowed_mods = _Mod_E;
> break;
>@@ -535,11 +752,11 @@ namespace __format
> __allowed_mods = _Mod_E;
> break;
> case 'z':
>- __needed = _TimeZone;
>+ __needed = _ZoneOffset;
> __allowed_mods = _Mod_E_O;
> break;
> case 'Z':
>- __needed = _TimeZone;
>+ __needed = _ZoneAbbrev;
> break;
> case 'n':
> case 't':
>@@ -567,10 +784,16 @@ namespace __format
> __locale_specific = true;
> __mod = _CharT();
>
>+ // localized format do not include subseconds
"formats"
>+ if (__locale_specific)
>+ __needed -= _ChronoParts::_Subseconds;
>+
> if ((__parts & __needed) != __needed)
> __throw_format_error("chrono format error: format argument "
> "does not contain the information "
> "required by the chrono-specs");
>+ __spec._M_needed |= __needed;
>+ __spec._M_locale_specific |= __locale_specific;
>
> // Scan for next '%', ignoring literal-chars before it.
> size_t __pos = __string_view(__first, __last - __first).find('%');
>@@ -596,21 +819,14 @@ namespace __format
> _M_spec = __spec;
> _M_spec._M_chrono_specs
> = __string_view(__chrono_specs, __first - __chrono_specs);
>- _M_spec._M_locale_specific = __locale_specific;
>
> return __first;
> }
>
>- // TODO this function template is instantiated for every different _Tp.
>- // Consider creating a polymorphic interface for calendar types so
>- // that we instantiate fewer different specializations. Similar to
>- // _Sink_iter for std::format. Replace each _S_year, _S_day etc. with
>- // member functions of that type.
Hooray, this comment can be removed :-)
And there's no polymorphism, the new type just contains all the
fields.
> // pre: !_M_spec._M_chrono_specs.empty()
>- template<typename _Tp, typename _FormatContext>
>+ template<typename _FormatContext>
> typename _FormatContext::iterator
>- _M_format(const _Tp& __t, _FormatContext& __fc,
>- bool __is_neg = false) const
>+ _M_format(const _ChronoData<_CharT>& __t, _FormatContext& __fc) const
> {
> #if defined _GLIBCXX_USE_NL_LANGINFO_L && __CHAR_BIT__ == 8
> // _GLIBCXX_RESOLVE_LIB_DEFECTS
>@@ -626,36 +842,80 @@ namespace __format
> // in the locale's encoding to UTF-8.
> locale __loc = __fc.locale();
> if (__loc != locale::classic())
>- __fc._M_loc = __with_encoding_conversion(__loc);
>+ __fc._M_loc = __with_encoding_conversion(__loc);
> }
> #endif
>- // formatter<duration> passes the correct value of __is_neg
>- // for durations but for hh_mm_ss we decide it here.
>- if constexpr (__is_specialization_of<_Tp, chrono::hh_mm_ss>)
>- __is_neg = __t.is_negative();
>
> const size_t __padwidth = _M_spec._M_get_width(__fc);
> if (__padwidth == 0)
>- return _M_format_to(__t, __fc.out(), __fc, __is_neg);
>+ return _M_format_to(__t, __fc.out(), __fc);
>
> using _Out = typename _FormatContext::iterator;
> _Padding_sink<_Out, _CharT> __sink(__fc.out(), __padwidth);
>- _M_format_to(__t, __sink.out(), __fc, __is_neg);
>+ _M_format_to(__t, __sink.out(), __fc);
> return __sink._M_finish(_M_spec._M_align, _M_spec._M_fill);
> }
>
>- template<typename _Tp, typename _Out, typename _FormatContext>
>+
>+ _ChronoSpec<_CharT> _M_spec;
>+
>+ protected:
>+ static constexpr const _CharT* _S_chars
>+ = _GLIBCXX_WIDEN("0123456789.Lf:/ +-{}");
>+ static constexpr _CharT _S_dot = _S_chars[10];
>+ static constexpr _CharT _S_colon = _S_chars[13];
>+ static constexpr _CharT _S_slash = _S_chars[14];
>+ static constexpr _CharT _S_space = _S_chars[15];
>+ static constexpr const _CharT* _S_fp_fmt = _S_chars + 11;
>+ static constexpr const _CharT* _S_plus_minus = _S_chars + 16;
>+ static constexpr const _CharT* _S_minus_empty_spec = _S_chars + 17;
>+ static constexpr const _CharT* _S_empty_spec = _S_chars + 18;
>+
>+ // Return the formatting locale.
>+ template<typename _FormatContext>
>+ std::locale
>+ _M_locale(_FormatContext& __fc) const
>+ {
>+ if (!_M_spec._M_localized)
>+ return std::locale::classic();
>+ else
>+ return __fc.locale();
>+ }
>+
>+ private:
>+ template<typename _OutIter>
>+ _OutIter
>+ _M_write(_OutIter __out, const locale& __loc, __string_view __s) const
>+ {
>+#if defined _GLIBCXX_USE_NL_LANGINFO_L && __CHAR_BIT__ == 8
>+ __sso_string __buf;
>+ // _GLIBCXX_RESOLVE_LIB_DEFECTS
>+ // 3565. Handling of encodings in localized formatting
>+ // of chrono types is underspecified
>+ if constexpr (is_same_v<_CharT, char>)
>+ if constexpr (__unicode::__literal_encoding_is_utf8())
>+ if (_M_spec._M_localized && _M_spec._M_locale_specific
>+ && __loc != locale::classic())
>+ {
>+ extern string_view
>+ __locale_encoding_to_utf8(const locale&, string_view, void*);
>+
>+ __s = __locale_encoding_to_utf8(__loc, __s, &__buf);
>+ }
>+#endif
>+ return __format::__write(std::move(__out), __s);
>+ }
>+
>+ template<typename _Out, typename _FormatContext>
> _Out
>- _M_format_to(const _Tp& __t, _Out __out, _FormatContext& __fc,
>- bool __is_neg) const
>+ _M_format_to(const _ChronoData<_CharT>& __t, _Out __out,
>+ _FormatContext& __fc) const
> {
> auto __first = _M_spec._M_chrono_specs.begin();
> const auto __last = _M_spec._M_chrono_specs.end();
>
>- auto __print_sign = [&__is_neg, &__out] {
>- if constexpr (chrono::__is_duration_v<_Tp>
>- || __is_specialization_of<_Tp, chrono::hh_mm_ss>)
>- if (__is_neg)
>+ auto __print_sign = [__is_neg = __t._M_is_neg, &__out] () mutable {
>+ if (__is_neg)
> {
> *__out++ = _S_plus_minus[1];
> __is_neg = false;
>@@ -676,16 +936,16 @@ namespace __format
> {
> // %\0 is extension for handling weekday index
> case '\0':
>- __out = _M_wi(__t, std::move(__out), __fc);
>+ __out = _M_wi(__t._M_weekday_index, std::move(__out), __fc);
> break;
> case 'a':
> case 'A':
>- __out = _M_a_A(__t, std::move(__out), __fc, __c == 'A');
>+ __out = _M_a_A(__t._M_weekday, std::move(__out), __fc, __c == 'A');
> break;
> case 'b':
> case 'h':
> case 'B':
>- __out = _M_b_B(__t, std::move(__out), __fc, __c == 'B');
>+ __out = _M_b_B(__t._M_month, std::move(__out), __fc, __c == 'B');
> break;
> case 'c':
> case 'r':
>@@ -696,11 +956,11 @@ namespace __format
> case 'C':
> case 'y':
> case 'Y':
>- __out = _M_C_y_Y(__t, std::move(__out), __fc, __c, __mod);
>+ __out = _M_C_y_Y(__t._M_year, std::move(__out), __fc, __c, __mod);
> break;
> case 'd':
> case 'e':
>- __out = _M_d_e(__t, std::move(__out), __fc, __c, __mod == 'O');
>+ __out = _M_d_e(__t._M_day, std::move(__out), __fc, __c, __mod == 'O');
> break;
> case 'D':
> __out = _M_D(__t, std::move(__out), __fc);
>@@ -714,33 +974,25 @@ namespace __format
> break;
> case 'H':
> case 'I':
>- __out = _M_H_I(__t, __print_sign(), __fc, __c, __mod == 'O');
>+ __out = _M_H_I(__t._M_hours, __print_sign(), __fc, __c, __mod == 'O');
> break;
> case 'j':
> __out = _M_j(__t, __print_sign(), __fc);
> break;
> case 'm':
>- __out = _M_m(__t, std::move(__out), __fc, __mod == 'O');
>+ __out = _M_m(__t._M_month, std::move(__out), __fc, __mod == 'O');
> break;
> case 'M':
>- __out = _M_M(__t, __print_sign(), __fc, __mod == 'O');
>+ __out = _M_M(__t._M_minutes, __print_sign(), __fc, __mod == 'O');
> break;
> case 'p':
>- __out = _M_p(__t, std::move(__out), __fc);
>+ __out = _M_p(__t._M_hours, std::move(__out), __fc);
> break;
> case 'q':
>- __out = _M_q(__t, std::move(__out), __fc);
>+ __out = _M_q(__t._M_unit_suffix, std::move(__out), __fc);
> break;
> case 'Q':
>- // %Q The duration's numeric value.
>- if constexpr (chrono::__is_duration_v<_Tp>)
>- // _GLIBCXX_RESOLVE_LIB_DEFECTS
>- // 4118. How should duration formatters format custom rep?
>- __out = std::format_to(__print_sign(), _S_empty_spec,
>- +__t.count());
>- else
>- __throw_format_error("chrono format error: argument is "
>- "not a duration");
>+ __out = _M_Q(__t, __print_sign(), __fc);
> break;
> case 'R':
> case 'T':
>@@ -751,7 +1003,7 @@ namespace __format
> break;
> case 'u':
> case 'w':
>- __out = _M_u_w(__t, std::move(__out), __fc, __c, __mod == 'O');
>+ __out = _M_u_w(__t._M_weekday, std::move(__out), __fc, __c, __mod == 'O');
> break;
> case 'U':
> case 'V':
>@@ -760,10 +1012,10 @@ namespace __format
> __mod == 'O');
> break;
> case 'z':
>- __out = _M_z(__t, std::move(__out), __fc, (bool)__mod);
>+ __out = _M_z(__t._M_zone_offset, std::move(__out), __fc, (bool)__mod);
> break;
> case 'Z':
>- __out = _M_Z(__t, std::move(__out), __fc);
>+ __out = _M_Z(__t._M_zone_abbrev, std::move(__out), __fc);
> break;
> case 'n':
> *__out++ = __literals[0];
>@@ -804,78 +1056,12 @@ namespace __format
> return std::move(__out);
> }
>
>- // Format duration for empty chrono-specs, e.g. "{}" (C++20 [time.format] p6).
>- template<typename _Rep, typename _Period, typename _FormatContext>
>- typename _FormatContext::iterator
>- _M_format_to_ostream(const chrono::duration<_Rep, _Period>& __d,
>- bool __is_neg, _FormatContext& __fc) const
>- {
>- basic_ostringstream<_CharT> __os;
>- __os.imbue(_M_locale(__fc));
>-
>- if (__is_neg) [[unlikely]]
>- __os << _S_plus_minus[1];
>- __os << __d;
>-
>- auto __str = std::move(__os).str();
>- return __format::__write_padded_as_spec(__str, __str.size(),
>- __fc, _M_spec);
>- }
>-
>- _ChronoSpec<_CharT> _M_spec;
>-
>- private:
>- // Return the formatting locale.
> template<typename _FormatContext>
>- std::locale
>- _M_locale(_FormatContext& __fc) const
>- {
>- if (!_M_spec._M_localized)
>- return std::locale::classic();
>- else
>- return __fc.locale();
>- }
>-
>- static constexpr const _CharT* _S_chars
>- = _GLIBCXX_WIDEN("0123456789:/ +-{}");
>- static constexpr _CharT _S_colon = _S_chars[10];
>- static constexpr _CharT _S_slash = _S_chars[11];
>- static constexpr _CharT _S_space = _S_chars[12];
>- static constexpr const _CharT* _S_plus_minus = _S_chars + 13;
>- static constexpr const _CharT* _S_minus_empty_spec = _S_chars + 14;
>- static constexpr const _CharT* _S_empty_spec = _S_chars + 15;
>-
>- template<typename _OutIter>
>- _OutIter
>- _M_write(_OutIter __out, const locale& __loc, __string_view __s) const
>- {
>-#if defined _GLIBCXX_USE_NL_LANGINFO_L && __CHAR_BIT__ == 8
>- __sso_string __buf;
>- // _GLIBCXX_RESOLVE_LIB_DEFECTS
>- // 3565. Handling of encodings in localized formatting
>- // of chrono types is underspecified
>- if constexpr (is_same_v<_CharT, char>)
>- if constexpr (__unicode::__literal_encoding_is_utf8())
>- if (_M_spec._M_localized && _M_spec._M_locale_specific
>- && __loc != locale::classic())
>- {
>- extern string_view
>- __locale_encoding_to_utf8(const locale&, string_view, void*);
>-
>- __s = __locale_encoding_to_utf8(__loc, __s, &__buf);
>- }
>-#endif
>- return __format::__write(std::move(__out), __s);
>- }
>-
>- template<typename _Tp, typename _FormatContext>
> typename _FormatContext::iterator
>- _M_wi(const _Tp& __t, typename _FormatContext::iterator __out,
>- _FormatContext& __ctx) const
>+ _M_wi(unsigned __wi, typename _FormatContext::iterator __out,
>+ _FormatContext& __ctx) const
> {
> // %\0 Extension to format weekday index, used only by empty format spec
>- unsigned __wi = _S_weekday_index(__t);
>-
> _CharT __buf[3];
> __out = __format::__write(std::move(__out), _S_str_d1(__buf, __wi));
> if (_M_spec._M_debug && (__wi < 1 || __wi > 5))
>@@ -884,14 +1070,13 @@ namespace __format
> return std::move(__out);
> }
>
>- template<typename _Tp, typename _FormatContext>
>+ template<typename _FormatContext>
> typename _FormatContext::iterator
>- _M_a_A(const _Tp& __t, typename _FormatContext::iterator __out,
>+ _M_a_A(chrono::weekday __wd, typename _FormatContext::iterator __out,
> _FormatContext& __ctx, bool __full) const
> {
> // %a Locale's abbreviated weekday name.
> // %A Locale's full weekday name.
>- chrono::weekday __wd = _S_weekday(__t);
> if (!__wd.ok())
> {
> if (!_M_spec._M_debug)
>@@ -915,14 +1100,13 @@ namespace __format
> return _M_write(std::move(__out), __loc, __str);
> }
>
>- template<typename _Tp, typename _FormatContext>
>+ template<typename _FormatContext>
> typename _FormatContext::iterator
>- _M_b_B(const _Tp& __t, typename _FormatContext::iterator __out,
>+ _M_b_B(chrono::month __m, typename _FormatContext::iterator __out,
> _FormatContext& __ctx, bool __full) const
> {
> // %b Locale's abbreviated month name.
> // %B Locale's full month name.
>- chrono::month __m = _S_month(__t);
> if (!__m.ok())
> {
> if (!_M_spec._M_debug)
>@@ -946,9 +1130,9 @@ namespace __format
> return _M_write(std::move(__out), __loc, __str);
> }
>
>- template<typename _Tp, typename _FormatContext>
>+ template<typename _FormatContext>
> typename _FormatContext::iterator
>- _M_c_r_x_X(const _Tp& __t, typename _FormatContext::iterator __out,
>+ _M_c_r_x_X(const _ChronoData<_CharT>& __t, typename _FormatContext::iterator __out,
> _FormatContext& __ctx, _CharT __conv, _CharT __mod) const
> {
> // %c Locale's date and time representation.
>@@ -960,9 +1144,6 @@ namespace __format
> // %EX Locale's alternative time representation.
>
> using namespace chrono;
>- using ::std::chrono::__detail::__utc_leap_second;
>- using ::std::chrono::__detail::__local_time_fmt;
>-
> struct tm __tm{};
>
> // Some locales use %Z in their %c format but we don't want strftime
>@@ -974,53 +1155,27 @@ namespace __format
> #ifdef _GLIBCXX_USE_STRUCT_TM_TM_ZONE
> // POSIX.1-2024 adds tm.tm_zone which will be used for %Z.
> // BSD has had tm_zone since 1987 but as char* so cast away const.
>- if constexpr (__is_time_point_v<_Tp>)
>- {
>- // One of sys_time, utc_time, or local_time.
>- if constexpr (!is_same_v<typename _Tp::clock, local_t>)
>- __tm.tm_zone = const_cast<char*>("UTC");
>- }
>- else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>)
>- {
>- // local-time-format-t is used to provide time zone info for
>- // one of zoned_time, tai_time, gps_time, or local_time.
>- if (__t._M_abbrev)
>- __tm.tm_zone = const_cast<char*>(__t._M_abbrev->c_str());
>- }
>- else
>- __tm.tm_zone = const_cast<char*>("UTC");
>+ if (__t._M_zone_cstr)
>+ __tm.tm_zone = const_cast<char*>(__t._M_zone_cstr);
> #endif
>
>- 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)__t._M_year - 1900;
>+ __tm.tm_yday = __t._M_day_of_year.count();
>+ __tm.tm_mon = (unsigned)__t._M_month - 1;
>+ __tm.tm_mday = (unsigned)__t._M_day;
>+ __tm.tm_wday = __t._M_weekday.c_encoding();
>+ __tm.tm_hour = __t._M_hours.count();
>+ __tm.tm_min = __t._M_minutes.count();
>+ __tm.tm_sec = __t._M_seconds.count();
>
> return _M_locale_fmt(std::move(__out), _M_locale(__ctx), __tm,
> __conv, __mod);
> }
>
>- template<typename _Tp, typename _FormatContext>
>+ template<typename _FormatContext>
> typename _FormatContext::iterator
>- _M_C_y_Y(const _Tp& __t, typename _FormatContext::iterator __out,
>- _FormatContext& __ctx, _CharT __conv, _CharT __mod = 0) const
>+ _M_C_y_Y(chrono::year __y, typename _FormatContext::iterator __out,
>+ _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).
>@@ -1030,8 +1185,6 @@ namespace __format
> // %Y Year as a decimal number.
> // %EY Locale's alternative full year representation.
>
>- chrono::year __y = _S_year(__t);
>-
> if (__mod && _M_spec._M_localized) [[unlikely]]
> if (auto __loc = __ctx.locale(); __loc != locale::classic())
> {
>@@ -1080,15 +1233,14 @@ namespace __format
> return __out;
> }
>
>- template<typename _Tp, typename _FormatContext>
>+ template<typename _FormatContext>
> typename _FormatContext::iterator
>- _M_D(const _Tp& __t, typename _FormatContext::iterator __out,
>+ _M_D(const _ChronoData<_CharT>& __t, typename _FormatContext::iterator __out,
> _FormatContext&) const
> {
>- auto __ymd = _S_date(__t);
>- auto __di = (unsigned)__ymd.day();
>- auto __mi = (unsigned)__ymd.month();
>- auto __yi = __builtin_abs((int)__ymd.year()) % 100;
>+ auto __di = (unsigned)__t._M_day;
>+ auto __mi = (unsigned)__t._M_month;
>+ auto __yi = __builtin_abs((int)__t._M_year) % 100;
>
> if (__mi >= 100 || __di >= 100) [[unlikely]]
> {
>@@ -1112,9 +1264,9 @@ namespace __format
> return std::move(__out);
> }
>
>- template<typename _Tp, typename _FormatContext>
>+ template<typename _FormatContext>
> typename _FormatContext::iterator
>- _M_d_e(const _Tp& __t, typename _FormatContext::iterator __out,
>+ _M_d_e(chrono::day __d, typename _FormatContext::iterator __out,
> _FormatContext& __ctx, _CharT __conv, bool __mod = false) const
> {
> // %d The day of month as a decimal number.
>@@ -1122,7 +1274,6 @@ namespace __format
> // %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 && _M_spec._M_localized) [[unlikely]]
>@@ -1150,15 +1301,14 @@ namespace __format
> return std::move(__out);
> }
>
>- template<typename _Tp, typename _FormatContext>
>+ template<typename _FormatContext>
> typename _FormatContext::iterator
>- _M_F(const _Tp& __t, typename _FormatContext::iterator __out,
>+ _M_F(const _ChronoData<_CharT>& __t, typename _FormatContext::iterator __out,
> _FormatContext&) const
> {
>- auto __ymd = _S_date(__t);
>- auto __di = (unsigned)__ymd.day();
>- auto __mi = (unsigned)__ymd.month();
>- auto __yi = (int)__ymd.year();
>+ auto __di = (unsigned)__t._M_day;
>+ auto __mi = (unsigned)__t._M_month;
>+ auto __yi = (int)__t._M_year;
> const bool __is_neg = __yi < 0;
> __yi = __builtin_abs(__yi);
>
>@@ -1185,31 +1335,31 @@ namespace __format
> __out = __format::__write(std::move(__out), __sv);
> }
>
>- if (_M_spec._M_debug && !__ymd.ok())
>+ if (_M_spec._M_debug && !(__t._M_year/__t._M_month/__t._M_day).ok())
> __out = __format::__write(std::move(__out),
> __string_view(_GLIBCXX_WIDEN(" is not a valid date")));
> return std::move(__out);
> }
>
>- template<typename _Tp, typename _FormatContext>
>+ template<typename _FormatContext>
> typename _FormatContext::iterator
>- _M_g_G(const _Tp& __t, typename _FormatContext::iterator __out,
>+ _M_g_G(const _ChronoData<_CharT>& __t, typename _FormatContext::iterator __out,
> _FormatContext& __ctx, bool __full) const
> {
> // %g last two decimal digits of the ISO week-based year.
> // %G ISO week-based year.
> using namespace chrono;
>- auto __d = _S_days(__t);
>+ auto __d = __t._M_ldays;
> // Move to nearest Thursday:
>- __d -= (weekday(__d) - Monday) - days(3);
>+ __d -= (__t._M_weekday - Monday) - days(3);
> // ISO week-based year is the year that contains that Thursday:
> year __y = year_month_day(__d).year();
> return _M_C_y_Y(__y, std::move(__out), __ctx, "yY"[__full]);
> }
>
>- template<typename _Tp, typename _FormatContext>
>+ template<typename _FormatContext>
> typename _FormatContext::iterator
>- _M_H_I(const _Tp& __t, typename _FormatContext::iterator __out,
>+ _M_H_I(chrono::hours __h, typename _FormatContext::iterator __out,
> _FormatContext& __ctx, _CharT __conv, bool __mod = false) const
> {
> // %H The hour (24-hour clock) as a decimal number.
>@@ -1217,8 +1367,7 @@ namespace __format
> // %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();
>+ int __i = __h.count();
>
> if (__mod && _M_spec._M_localized) [[unlikely]]
> if (auto __loc = __ctx.locale(); __loc != locale::classic())
>@@ -1241,44 +1390,30 @@ namespace __format
> return __format::__write(std::move(__out), _S_two_digits(__i));
> }
>
>- template<typename _Tp, typename _FormatContext>
>+ template<typename _FormatContext>
> typename _FormatContext::iterator
>- _M_j(const _Tp& __t, typename _FormatContext::iterator __out,
>+ _M_j(const _ChronoData<_CharT>& __t, typename _FormatContext::iterator __out,
> _FormatContext&) const
> {
>- if constexpr (chrono::__is_duration_v<_Tp>)
>- {
>- // Decimal number of days, without padding.
>- unsigned __d = chrono::duration_cast<chrono::days>(__t).count();
>- return std::format_to(std::move(__out), _S_empty_spec, __d);
>- }
>- else
>- {
>- // Day of the year as a decimal number, padding with zero.
>- using namespace chrono;
>- auto __day = _S_days(__t);
>- auto __ymd = _S_date(__t);
>- days __d;
>- // See "Calculating Ordinal Dates" at
>- // https://github.com/HowardHinnant/date/wiki/Examples-and-Recipes
>- if constexpr (is_same_v<typename decltype(__day)::clock, local_t>)
>- __d = __day - local_days(__ymd.year()/January/0);
>- else
>- __d = __day - sys_days(__ymd.year()/January/0);
>- return std::format_to(std::move(__out), _GLIBCXX_WIDEN("{:03d}"),
>- __d.count());
>- }
>+ if (_M_spec._M_time_only)
>+ {
>+ // Decimal number of days, without padding.
>+ auto __d = chrono::floor<chrono::days>(__t._M_hours).count();
>+ return std::format_to(std::move(__out), _S_empty_spec, __d);
>+ }
>+
>+ return std::format_to(std::move(__out), _GLIBCXX_WIDEN("{:03d}"),
>+ __t._M_day_of_year.count());
> }
>
>- template<typename _Tp, typename _FormatContext>
>+ template<typename _FormatContext>
> typename _FormatContext::iterator
>- _M_m(const _Tp& __t, typename _FormatContext::iterator __out,
>+ _M_m(chrono::month __m, 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 && _M_spec._M_localized) [[unlikely]] // %Om
>@@ -1294,15 +1429,14 @@ namespace __format
> return __format::__write(std::move(__out), _S_str_d2(__buf, __i));
> }
>
>- template<typename _Tp, typename _FormatContext>
>+ template<typename _FormatContext>
> typename _FormatContext::iterator
>- _M_M(const _Tp& __t, typename _FormatContext::iterator __out,
>+ _M_M(chrono::minutes __m, 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 && _M_spec._M_localized) [[unlikely]] // %OM
>@@ -1317,14 +1451,13 @@ namespace __format
> return __format::__write(std::move(__out), _S_two_digits(__i));
> }
>
>- template<typename _Tp, typename _FormatContext>
>+ template<typename _FormatContext>
> typename _FormatContext::iterator
>- _M_p(const _Tp& __t, typename _FormatContext::iterator __out,
>+ _M_p(chrono::hours __h, typename _FormatContext::iterator __out,
> _FormatContext& __ctx) const
> {
> // %p The locale's equivalent of the AM/PM designations.
>- auto __hms = _S_hms(__t);
>- auto __hi = __hms.hours().count();
>+ auto __hi = __h.count();
> if (__hi >= 24) [[unlikely]]
> __hi %= 24;
>
>@@ -1335,35 +1468,34 @@ namespace __format
> return _M_write(std::move(__out), __loc, __ampm[__hi >= 12]);
> }
>
>- template<typename _Tp, typename _FormatContext>
>+ template<typename _FormatContext>
> typename _FormatContext::iterator
>- _M_q(const _Tp&, typename _FormatContext::iterator __out,
>+ _M_q(__string_view __us, typename _FormatContext::iterator __out,
> _FormatContext&) const
> {
> // %q The duration's unit suffix
>- if constexpr (!chrono::__is_duration_v<_Tp>)
>- __throw_format_error("format error: argument is not a duration");
>- else
>- {
>- namespace __d = chrono::__detail;
>- using period = typename _Tp::period;
>- return __d::__fmt_units_suffix<period, _CharT>(std::move(__out));
>- }
>+ return __format::__write(std::move(__out), __us);
> }
>
>- // %Q handled in _M_format
>+ template<typename _FormatContext>
>+ typename _FormatContext::iterator
>+ _M_Q(const _ChronoData<_CharT>& __t, typename _FormatContext::iterator __out,
>+ _FormatContext& __ctx) const
>+ {
>+ // %Q The duration's numeric value.
>+ return std::vformat_to(std::move(__out), _S_empty_spec, __t._M_ereps);
>+ }
>
>- template<typename _Tp, typename _FormatContext>
>+ template<typename _FormatContext>
> typename _FormatContext::iterator
>- _M_R_T(const _Tp& __t, typename _FormatContext::iterator __out,
>+ _M_R_T(const _ChronoData<_CharT>& __t, typename _FormatContext::iterator __out,
> _FormatContext& __ctx, bool __secs) const
> {
> // %R Equivalent to %H:%M
> // %T Equivalent to %H:%M:%S
>- auto __hms = _S_hms(__t);
>- auto __hi = __hms.hours().count();
>+ auto __hi = __t._M_hours.count();
>
>- _CharT __buf[6];
>+ _CharT __buf[6];
> __buf[2] = _S_colon;
> __buf[5] = _S_colon;
> __string_view __sv(__buf, 5 + __secs);
>@@ -1376,86 +1508,96 @@ namespace __format
> else
> _S_fill_two_digits(__buf, __hi);
>
>- _S_fill_two_digits(__buf + 3, __hms.minutes().count());
>+ _S_fill_two_digits(__buf + 3, __t._M_minutes.count());
> __out = __format::__write(std::move(__out), __sv);
> if (__secs)
>- __out = _M_S(__hms, std::move(__out), __ctx);
>+ __out = _M_S(__t, std::move(__out), __ctx);
> return __out;
> }
>
>- template<typename _Tp, typename _FormatContext>
>+ template<typename _FormatContext>
> typename _FormatContext::iterator
>- _M_S(const _Tp& __t, typename _FormatContext::iterator __out,
>+ _M_S(const _ChronoData<_CharT>& __t, typename _FormatContext::iterator __out,
> _FormatContext& __ctx, bool __mod = false) const
> {
> // %S Seconds as a decimal number.
> // %OS The locale's alternative representation.
>- auto __hms = _S_hms(__t);
>- auto __s = __hms.seconds();
>+ auto __s = __t._M_seconds;
>
>- if (__mod) [[unlikely]] // %OS
>- {
>- if (_M_spec._M_localized)
>- if (auto __loc = __ctx.locale(); __loc != locale::classic())
>- {
>- struct tm __tm{};
>- __tm.tm_sec = (int)__s.count();
>- return _M_locale_fmt(std::move(__out), __loc, __tm,
>- 'S', 'O');
>- }
>+ if (__mod && _M_spec._M_localized) [[unlikely]] // %OS
>+ if (auto __loc = __ctx.locale(); __loc != locale::classic())
>+ {
>+ struct tm __tm{};
>+ __tm.tm_sec = (int)__s.count();
>+ return _M_locale_fmt(std::move(__out), __loc, __tm,
>+ 'S', 'O');
>+ }
>
>- // %OS formats don't include subseconds, so just format that:
>- return __format::__write(std::move(__out),
>- _S_two_digits(__s.count()));
>+ __out = __format::__write(std::move(__out),
>+ _S_two_digits(__s.count()));
>+
>+ unsigned __prec = _M_spec._M_prec_kind != _WP_none
>+ ? _M_spec._M_get_precision(__ctx)
>+ : _M_spec._M_prec;
>+
>+ // %OS formats don't include subseconds
>+ if (__prec == 0 || __mod)
>+ return __out;
>+
>+ _CharT __dot = _S_dot;
>+ if (_M_spec._M_localized)
>+ if (auto __loc = __ctx.locale(); __loc != locale::classic())
>+ {
>+ const auto& __np = use_facet<numpunct<_CharT>>(__loc);
>+ __dot = __np.decimal_point();
> }
>+ *__out = __dot;
>+ ++__out;
>
>- if constexpr (__hms.fractional_width == 0)
>- __out = __format::__write(std::move(__out),
>- _S_two_digits(__s.count()));
>- else
>+ if (_M_spec._M_floating_point_rep)
> {
>- locale __loc = _M_locale(__ctx);
>- auto __ss = __hms.subseconds();
>- using rep = typename decltype(__ss)::rep;
>- if constexpr (is_floating_point_v<rep>)
>- {
>- chrono::duration<rep> __fs = __s + __ss;
>- __out = std::format_to(std::move(__out), __loc,
>- _GLIBCXX_WIDEN("{:#0{}.{}Lf}"),
>- __fs.count(),
>- 3 + __hms.fractional_width,
>- __hms.fractional_width);
>- }
>+ _Str_sink<_CharT> __sink;
>+ if (_M_spec._M_localized && _M_spec._M_custom_rep)
>+ std::vformat_to(__sink.out(), _M_locale(__ctx),
>+ _GLIBCXX_WIDEN("{1:0.{2}Lf}"), __t._M_ereps);
> else
>- {
>- const auto& __np
>- = use_facet<numpunct<_CharT>>(__loc);
>- __out = __format::__write(std::move(__out),
>- _S_two_digits(__s.count()));
>- *__out++ = __np.decimal_point();
>- if constexpr (is_integral_v<rep>)
>- __out = std::format_to(std::move(__out),
>- _GLIBCXX_WIDEN("{:0{}}"),
>- __ss.count(),
>- __hms.fractional_width);
>- else
>- {
>- auto __str = std::format(_S_empty_spec, __ss.count());
>- __out = std::format_to(std::move(__out),
>- _GLIBCXX_WIDEN("{:0>{}s}"),
>- __str,
>- __hms.fractional_width);
>- }
>- }
>+ std::vformat_to(__sink.out(),
>+ _GLIBCXX_WIDEN("{1:0.{2}f}"), __t._M_ereps);
>+
>+ auto __sv = __sink.view();
>+ // Skip leading zero and dot
>+ __sv.remove_prefix(2);
>+ return __format::__write(std::move(__out), __sv);
> }
>- return __out;
>+
>+ constexpr unsigned __max_prec = _ChronoData<_CharT>::_S_max_prec;
>+ constexpr typename _ChronoData<_CharT>::_Attoseconds::rep __pow10t[]
>+ {
>+ 1u,
>+ 10u, 100u, 1000u,
>+ 10'000u, 100'000u, 1000'000u,
>+ 10'000'000u, 100'000'000u, 1000'000'000u,
>+ 10'000'000'000u, 100'000'000'000u, 1000'000'000'000u,
>+ 10'000'000'000'000u, 100'000'000'000'000u, 1000'000'000'000'000u,
>+ 10'000'000'000'000'000u, 100'000'000'000'000'000u, 1000'000'000'000'000'000u,
>+ };
>+
>+ auto __subs = __t._M_subseconds.count();
>+ if (__prec < __max_prec)
>+ __subs /= __pow10t[__max_prec - __prec];
>+ else if (__prec > __max_prec)
>+ __prec = __max_prec;
>+
>+ using _FmtStr = _Runtime_format_string<_CharT>;
>+ return std::format_to(__out, _FmtStr(_GLIBCXX_WIDEN("{0:0{1}}")),
>+ __subs, __prec);
> }
>
> // %t handled in _M_format
>
>- template<typename _Tp, typename _FormatContext>
>+ template<typename _FormatContext>
> typename _FormatContext::iterator
>- _M_u_w(const _Tp& __t, typename _FormatContext::iterator __out,
>+ _M_u_w(chrono::weekday __wd, typename _FormatContext::iterator __out,
> _FormatContext& __ctx, _CharT __conv, bool __mod = false) const
> {
> // %u ISO weekday as a decimal number (1-7), where Monday is 1.
>@@ -1463,8 +1605,6 @@ namespace __format
> // %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 && _M_spec._M_localized) [[unlikely]]
> if (auto __loc = __ctx.locale(); __loc != locale::classic())
> {
>@@ -1480,9 +1620,9 @@ namespace __format
> return __format::__write(std::move(__out), _S_str_d1(__buf, __wdi));
> }
>
>- template<typename _Tp, typename _FormatContext>
>+ template<typename _FormatContext>
> typename _FormatContext::iterator
>- _M_U_V_W(const _Tp& __t, typename _FormatContext::iterator __out,
>+ _M_U_V_W(const _ChronoData<_CharT>& __t, typename _FormatContext::iterator __out,
> _FormatContext& __ctx, _CharT __conv, bool __mod = false) const
> {
> // %U Week number of the year as a decimal number, from first Sunday.
>@@ -1492,126 +1632,68 @@ namespace __format
> // %W Week number of the year as a decimal number, from first Monday.
> // %OW Locale's alternative numeric rep.
> using namespace chrono;
>- auto __d = _S_days(__t);
>- using _TDays = decltype(__d); // Either sys_days or local_days.
>
> if (__mod && _M_spec._M_localized) [[unlikely]]
> if (auto __loc = __ctx.locale(); __loc != locale::classic())
> {
>- 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();
>+ __tm.tm_year = (int)__t._M_year - 1900;
>+ __tm.tm_yday = __t._M_day_of_year.count();
>+ __tm.tm_wday = __t._M_weekday.c_encoding();
> return _M_locale_fmt(std::move(__out), __loc, __tm,
> (char)__conv, 'O');
> }
>
>- _TDays __first; // First day of week 1.
>+ auto __d = __t._M_ldays;
>+ local_days __first; // First day of week 1.
> if (__conv == 'V') // W01 begins on Monday before first Thursday.
> {
> // Move to nearest Thursday:
>- __d -= (weekday(__d) - Monday) - days(3);
>+ __d -= (__t._M_weekday - Monday) - days(3);
> // ISO week of __t is number of weeks since January 1 of the
> // same year as that nearest Thursday.
>- __first = _TDays(year_month_day(__d).year()/January/1);
>+ __first = local_days(year_month_day(__d).year()/January/1);
> }
> else
> {
>- year __y;
>- if constexpr (requires { __t.year(); })
>- __y = __t.year();
>- else
>- __y = year_month_day(__d).year();
> const weekday __weekstart = __conv == 'U' ? Sunday : Monday;
>- __first = _TDays(__y/January/__weekstart[1]);
>+ __first = local_days(__t._M_year/January/__weekstart[1]);
> }
> auto __weeks = chrono::floor<weeks>(__d - __first);
> __string_view __sv = _S_two_digits(__weeks.count() + 1);
> return __format::__write(std::move(__out), __sv);
> }
>
>- template<typename _Tp, typename _FormatContext>
>+ template<typename _FormatContext>
> typename _FormatContext::iterator
>- _M_z(const _Tp& __t, typename _FormatContext::iterator __out,
>+ _M_z(chrono::seconds __ts, typename _FormatContext::iterator __out,
> _FormatContext&, bool __mod = false) const
> {
>- using ::std::chrono::__detail::__utc_leap_second;
>- using ::std::chrono::__detail::__local_time_fmt;
>-
>- auto __utc = __mod ? __string_view(_GLIBCXX_WIDEN("+00:00"), 6)
>- : __string_view(_GLIBCXX_WIDEN("+0000"), 5);
>-
>- if constexpr (chrono::__is_time_point_v<_Tp>)
>+ if (__ts == 0s)
> {
>- if constexpr (is_same_v<typename _Tp::clock,
>- chrono::system_clock>)
>- return __format::__write(std::move(__out), __utc);
>- }
>- else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>)
>- {
>- if (__t._M_offset_sec)
>- {
>- auto __sv = __utc;
>- basic_string<_CharT> __s;
>- if (*__t._M_offset_sec != 0s)
>- {
>- chrono:: hh_mm_ss __hms(*__t._M_offset_sec);
>- __s = _S_plus_minus[__hms.is_negative()];
>- __s += _S_two_digits(__hms.hours().count());
>- if (__mod)
>- __s += _S_colon;
>- __s += _S_two_digits(__hms.minutes().count());
>- __sv = __s;
>- }
>- return __format::__write(std::move(__out), __sv);
>- }
>+ __string_view __zero
>+ = __mod ? _GLIBCXX_WIDEN("+00:00") : _GLIBCXX_WIDEN("+0000");
>+ return __format::__write(std::move(__out), __zero);
> }
>- else if constexpr (__is_specialization_of<_Tp, __utc_leap_second>)
>- return __format::__write(std::move(__out), __utc);
>
>- __no_timezone_available();
>+ chrono::hh_mm_ss<chrono::seconds> __hms(__ts);
>+ unsigned __mo = 3 + __mod;
>+
>+ _CharT __buf[6];
>+ __buf[0] = _S_plus_minus[__hms.is_negative()];
>+ __buf[3] = _S_colon;
>+ _S_fill_two_digits(__buf + 1, __hms.hours().count());
>+ _S_fill_two_digits(__buf + __mo, __hms.minutes().count());
>+
>+ __string_view __sv(__buf, __mo + 2);
>+ return __format::__write(std::move(__out), __sv);
> }
>
>- template<typename _Tp, typename _FormatContext>
>+ template<typename _FormatContext>
> typename _FormatContext::iterator
>- _M_Z(const _Tp& __t, typename _FormatContext::iterator __out,
>+ _M_Z(__string_view __abbrev, typename _FormatContext::iterator __out,
> _FormatContext& __ctx) const
>- {
>- using ::std::chrono::__detail::__utc_leap_second;
>- using ::std::chrono::__detail::__local_time_fmt;
>-
>- __string_view __utc(_GLIBCXX_WIDEN("UTC"), 3);
>- if constexpr (chrono::__is_time_point_v<_Tp>)
>- {
>- if constexpr (is_same_v<typename _Tp::clock,
>- chrono::system_clock>)
>- return __format::__write(std::move(__out), __utc);
>- }
>- else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>)
>- {
>- if (__t._M_abbrev)
>- {
>- string_view __sv = *__t._M_abbrev;
>- if constexpr (is_same_v<_CharT, char>)
>- return __format::__write(std::move(__out), __sv);
>- else
>- {
>- // TODO use resize_and_overwrite
>- basic_string<_CharT> __ws(__sv.size(), _CharT());
>- auto& __ct = use_facet<ctype<_CharT>>(_M_locale(__ctx));
>- __ct.widen(__sv.begin(), __sv.end(), __ws.data());
>- __string_view __wsv = __ws;
>- return __format::__write(std::move(__out), __wsv);
>- }
>- }
>- }
>- else if constexpr (__is_specialization_of<_Tp, __utc_leap_second>)
>- return __format::__write(std::move(__out), __utc);
>-
>- __no_timezone_available();
>- }
>+ { return __format::__write(std::move(__out), __abbrev); }
>
> // %% handled in _M_format
>
>@@ -1670,194 +1752,202 @@ namespace __format
> return _S_two_digits(__n);
>
> _S_fill_two_digits(__buf.data(), __n / 10);
>- __buf[2] = _S_digit(__n % 10)[0];
>+ __buf[2] = _S_chars[__n % 10];
> return __string_view(__buf.data(), 3);
> }
>
>- // Accessors for the components of chrono types:
>+ // Use the formatting locale's std::time_put facet to produce
>+ // a locale-specific representation.
>+ template<typename _Iter>
>+ _Iter
>+ _M_locale_fmt(_Iter __out, const locale& __loc, const struct tm& __tm,
>+ char __fmt, char __mod) const
>+ {
>+ basic_ostringstream<_CharT> __os;
>+ __os.imbue(__loc);
>+ const auto& __tp = use_facet<time_put<_CharT>>(__loc);
>+ __tp.put(__os, __os, _S_space, &__tm, __fmt, __mod);
>+ if (__os)
>+ __out = _M_write(std::move(__out), __loc, __os.view());
>+ return __out;
>+ }
>+ };
>
>- // Returns a hh_mm_ss.
>- template<typename _Tp>
>- static decltype(auto)
>- _S_hms(const _Tp& __t)
>+ template<typename _CharT>
>+ struct __formatter_duration : private __formatter_chrono<_CharT>
>+ {
>+ template<typename _Rep, typename _Period>
>+ constexpr static auto
>+ _S_subseconds(const chrono::duration<_Rep, _Period>& __d)
> {
>- using ::std::chrono::__detail::__utc_leap_second;
>- using ::std::chrono::__detail::__local_time_fmt;
>-
>- if constexpr (__is_specialization_of<_Tp, chrono::hh_mm_ss>)
>- return __t;
>- else if constexpr (__is_specialization_of<_Tp, __utc_leap_second>)
>- return __t._M_time;
>- else if constexpr (chrono::__is_duration_v<_Tp>)
>- return chrono::hh_mm_ss<_Tp>(__t);
>- else if constexpr (chrono::__is_time_point_v<_Tp>)
>- return chrono::hh_mm_ss(__t - chrono::floor<chrono::days>(__t));
>- else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>)
>- return _S_hms(__t._M_time);
>+ if constexpr (chrono::treat_as_floating_point_v<_Rep>)
>+ return chrono::duration<_Rep>(__d);
>+ else if constexpr (_Period::den == 1)
>+ return chrono::seconds(0);
> else
>- {
>- __invalid_chrono_spec();
>- return chrono::hh_mm_ss<chrono::seconds>();
>- }
>- }
>+ {
>+ using _Attoseconds = _ChronoData<_CharT>::_Attoseconds;
>+ using _CRep = common_type_t<_Rep, typename _Attoseconds::rep>;
>+ chrono::duration<_CRep, _Period> subs(__d.count());
>+ return chrono::duration_cast<_Attoseconds>(subs);
>+ }
>+ }
>
>- // Returns a sys_days or local_days.
>- template<typename _Tp>
>- static auto
>- _S_days(const _Tp& __t)
>+ public:
>+ template<typename _Duration>
>+ static consteval
>+ _ChronoSpec<_CharT>
>+ _S_spec_for(_ChronoParts __parts)
> {
>- using namespace chrono;
>- using ::std::chrono::__detail::__utc_leap_second;
>- using ::std::chrono::__detail::__local_time_fmt;
>-
>- if constexpr (__is_time_point_v<_Tp>)
>- return chrono::floor<days>(__t);
>- else if constexpr (__is_specialization_of<_Tp, __utc_leap_second>)
>- return __t._M_date;
>- else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>)
>- return chrono::floor<days>(__t._M_time);
>- else if constexpr (is_same_v<_Tp, year_month_day>
>- || is_same_v<_Tp, year_month_day_last>
>- || is_same_v<_Tp, year_month_weekday>
>- || is_same_v<_Tp, year_month_weekday_last>)
>- return sys_days(__t);
>- else
>+ using _Rep = typename _Duration::rep;
>+ using enum _ChronoParts;
>+
>+ _ChronoSpec<_CharT> __res{};
>+ __res._M_time_only = (__parts & _Date) == 0;
>+ __res._M_floating_point_rep = chrono::treat_as_floating_point_v<_Rep>;
>+ __res._M_custom_rep = !is_arithmetic_v<_Rep>;
>+ __res._M_prec = chrono::hh_mm_ss<_Duration>::fractional_width;
>+ if ((__parts & _TimeOfDay) != 0)
>+ __res._M_localized = __res._M_prec > 0 || __res._M_floating_point_rep;
>+
>+ if ((__parts & _TimeOfDay) != 0)
>+ __res._M_needed |= _TimeOfDay;
>+ if ((__parts & _Date) != 0)
>+ __res._M_needed |= _YearMonthDay;
>+ if ((__parts & _ZoneAbbrev) != 0)
>+ __res._M_needed |= _ZoneAbbrev;
>+
>+ switch (__parts)
> {
>- if constexpr (__is_duration_v<_Tp>)
>- __not_valid_for_duration();
>- else
>- __invalid_chrono_spec();
>- return chrono::sys_days();
>+ case _ZonedDateTime:
>+ __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_ftz();
>+ break;
>+ case _DateTime:
>+ __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_ft();
>+ break;
>+ case _Date:
>+ __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_f();
>+ break;
>+ case _Time:
>+ __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_t();
>+ break;
>+ case _None:
>+ break;
> }
>- }
>+ return __res;
>+ };
>
>- // Returns a year_month_day.
>- template<typename _Tp>
>- static chrono::year_month_day
>- _S_date(const _Tp& __t)
>- {
>- if constexpr (is_same_v<_Tp, chrono::year_month_day>)
>- return __t;
>- else
>- return chrono::year_month_day(_S_days(__t));
>- }
>+ using __formatter_chrono<_CharT>::__formatter_chrono;
>+ using __formatter_chrono<_CharT>::_M_locale;
>+ using __formatter_chrono<_CharT>::_M_spec;
>
>- template<typename _Tp>
>- static chrono::day
>- _S_day(const _Tp& __t)
>+ template<typename _Duration, typename _ParseContext>
>+ constexpr typename _ParseContext::iterator
>+ _M_parse(_ParseContext& __pc, _ChronoParts __parts,
>+ const _ChronoSpec<_CharT>& __def = {})
> {
>- using namespace chrono;
>-
>- if constexpr (is_same_v<_Tp, day>)
>- return __t;
>- else if constexpr (requires { __t.day(); })
>- return __t.day();
>- else
>- return _S_date(__t).day();
>+ using _Rep = typename _Duration::rep;
>+ using enum _ChronoParts;
>+
>+ auto __res
>+ = __formatter_chrono<_CharT>::_M_parse(__pc, __parts, __def);
>+ // check for custom floating point durations, if digits of output
>+ // will contain subseconds, then formatters must support specifying
>+ // precision.
>+ if constexpr (!is_floating_point_v<_Rep>)
>+ if constexpr (chrono::treat_as_floating_point_v<_Rep>)
>+ if (_M_spec._M_needs(_Subseconds|_EpochUnits)
>+ || _M_spec._M_prec_kind != _WP_none
>+ || _M_spec._M_prec_value > 0)
>+ {
>+ constexpr const _CharT* __fs = _GLIBCXX_WIDEN("#02.5Lf");
>+ basic_format_parse_context<_CharT> __npc(__fs);
>+ formatter<_Rep, _CharT> __fmtter;
>+ __fmtter.parse(__npc);
>+ }
>+ return __res;
> }
>
>- template<typename _Tp>
>- static chrono::month
>- _S_month(const _Tp& __t)
>+ // Format duration for empty chrono-specs, e.g. "{}" (C++20 [time.format] p6).
>+ template<typename _Rep, typename _Period, typename _FormatContext>
>+ typename _FormatContext::iterator
>+ _M_format_to_ostream(const chrono::duration<_Rep, _Period>& __d,
>+ bool __is_neg,
>+ _FormatContext& __fc) const
> {
>- using namespace chrono;
>+ basic_ostringstream<_CharT> __os;
>+ __os.imbue(this->_M_locale(__fc));
>
>- if constexpr (is_same_v<_Tp, month>)
>- return __t;
>- else if constexpr (requires { __t.month(); })
>- return __t.month();
>- else
>- return _S_date(__t).month();
>+ if (__is_neg) [[unlikely]]
>+ __os << this->_S_plus_minus[1];
>+ __os << __d;
>+
>+ auto __str = std::move(__os).str();
>+ return __format::__write_padded_as_spec(__str, __str.size(),
>+ __fc, _M_spec);
> }
>
>- template<typename _Tp>
>- static chrono::year
>- _S_year(const _Tp& __t)
>+ template<typename _Rep1, typename _Period1,
>+ typename _Rep2, typename _Period2,
>+ typename _FormatContext>
>+ typename _FormatContext::iterator
>+ _M_format_units(_ChronoData<_CharT>& __cd,
>+ const chrono::duration<_Rep1, _Period1>& __ed,
>+ const chrono::duration<_Rep2, _Period2>& __ss,
>+ _FormatContext& __fc) const
> {
>- using namespace chrono;
>+ __format::_Str_sink<_CharT> __suffix_store;
>+ constexpr auto _S_unit_suffix
>+ = chrono::__detail::__units_suffix<_Period1, _CharT>();
>+ if constexpr (!_S_unit_suffix.empty())
>+ __cd._M_unit_suffix = _S_unit_suffix;
>+ else if (_M_spec._M_needs(_ChronoParts::_UnitSuffix))
>+ {
>+ chrono::__detail::
>+ __fmt_units_suffix<_Period1, _CharT>(__suffix_store.out());
>+ __cd._M_unit_suffix = __suffix_store.view();
>+ }
>
>- if constexpr (is_same_v<_Tp, year>)
>- return __t;
>- else if constexpr (requires { __t.year(); })
>- return __t.year();
>- else
>- return _S_date(__t).year();
>- }
>+ const auto __prec = _M_spec._M_prec_kind != _WP_none
>+ ? _M_spec._M_get_precision(__fc)
>+ : _M_spec._M_prec;
>
>- template<typename _Tp>
>- static chrono::weekday
>- _S_weekday(const _Tp& __t)
>- {
>- using namespace ::std::chrono;
>- using ::std::chrono::__detail::__local_time_fmt;
>-
>- if constexpr (is_same_v<_Tp, weekday>)
>- return __t;
>- else if constexpr (requires { __t.weekday(); })
>- return __t.weekday();
>- else if constexpr (is_same_v<_Tp, month_weekday>)
>- return __t.weekday_indexed().weekday();
>- else if constexpr (is_same_v<_Tp, month_weekday_last>)
>- return __t.weekday_last().weekday();
>- else
>- return weekday(_S_days(__t));
>- }
>+ using _ErasedContext = typename _ChronoData<_CharT>::_FormatContext;
>+ // _GLIBCXX_RESOLVE_LIB_DEFECTS
>+ // 4118. How should duration formatters format custom rep?
>+ auto __ereps = +__ed.count();
>+ if (!_M_spec._M_needs(_ChronoParts::_Subseconds))
>+ {
>+ auto __ssreps = 0u;
>+ auto __args_store
>+ = std::make_format_args<_ErasedContext>(__ereps, __ssreps, __prec);
>+ __cd._M_ereps = __args_store;
>+ return this->_M_format(__cd, __fc);
>+ }
>
>- template<typename _Tp>
>- static unsigned
>- _S_weekday_index(const _Tp& __t)
>- {
>- using namespace ::std::chrono;
>+ using _Attoseconds = _ChronoData<_CharT>::_Attoseconds;
>+ auto __nss = _S_subseconds(__ss);
>+ __cd._M_subseconds = chrono::duration_cast<_Attoseconds>(__nss);
>
>- if constexpr (is_same_v<_Tp, weekday_indexed>)
>- return __t.index();
>- else if constexpr (requires { __t.weekday_indexed(); })
>- return __t.weekday_indexed().index();
>- else
>- return ((unsigned)_S_day(__t) + 6) / 7;
>- }
>+ auto __ssreps = __nss.count();
>+ auto __args_store
>+ = std::make_format_args<_ErasedContext>(__ereps, __ssreps, __prec);
>+ __cd._M_ereps = __args_store;
>
>- // Remove subsecond precision from a time_point.
>- template<typename _Tp>
>- static auto
>- _S_floor_seconds(const _Tp& __t)
>- {
>- using chrono::__detail::__local_time_fmt;
>- if constexpr (chrono::__is_time_point_v<_Tp>
>- || chrono::__is_duration_v<_Tp>)
>- {
>- if constexpr (_Tp::period::den != 1)
>- return chrono::floor<chrono::seconds>(__t);
>- else
>- return __t;
>- }
>- else if constexpr (__is_specialization_of<_Tp, chrono::hh_mm_ss>)
>- {
>- if constexpr (_Tp::fractional_width != 0)
>- return chrono::floor<chrono::seconds>(__t.to_duration());
>- else
>- return __t;
>- }
>- else if constexpr (__is_specialization_of<_Tp, __local_time_fmt>)
>- return _S_floor_seconds(__t._M_time);
>- else
>- return __t;
>+ return this->_M_format(__cd, __fc);
> }
>
>- // Use the formatting locale's std::time_put facet to produce
>- // a locale-specific representation.
>- template<typename _Iter>
>- _Iter
>- _M_locale_fmt(_Iter __out, const locale& __loc, const struct tm& __tm,
>- char __fmt, char __mod) const
>+ template<typename _Rep1, typename _Period1, typename _FormatContext>
>+ // pre: __cd._M_lseconds and __cd._M_eseconds are set.
Please put this comment before the template-head
>+ typename _FormatContext::iterator
>+ _M_format_time_point(_ChronoData<_CharT>& __cd,
>+ const chrono::duration<_Rep1, _Period1>& __ed,
>+ _FormatContext& __fc) const
> {
>- basic_ostringstream<_CharT> __os;
>- __os.imbue(__loc);
>- const auto& __tp = use_facet<time_put<_CharT>>(__loc);
>- __tp.put(__os, __os, _S_space, &__tm, __fmt, __mod);
>- if (__os)
>- __out = _M_write(std::move(__out), __loc, __os.view());
>- return __out;
>+ auto __parts = _M_spec._M_needed - _ChronoParts::_TotalSeconds;
>+ if ((__parts & _ChronoParts::_DateTime) != 0)
>+ __cd._M_fill_date_time(__cd._M_lseconds, __parts);
>+ return _M_format_units(__cd, __ed, __ed - __cd._M_eseconds, __fc);
> }
> };
>
>@@ -1877,7 +1967,7 @@ namespace __format
> // n.b. only acceptable chrono-spec for info is one containing
> // only whitespaces and %%, that do not depend on formatted object.
> if (!_M_f._M_spec._M_chrono_specs.empty()) [[unlikely]]
>- return _M_f._M_format(chrono::day(1), __fc);
>+ return _M_f._M_format(_ChronoData<_CharT>{}, __fc);
>
> const size_t __padwidth = _M_f._M_spec._M_get_width(__fc);
> if (__padwidth == 0)
>@@ -1949,12 +2039,8 @@ namespace __format
> constexpr typename basic_format_parse_context<_CharT>::iterator
> parse(basic_format_parse_context<_CharT>& __pc)
> {
>- using namespace __format;
>- auto __it = _M_f._M_parse(__pc, _Duration|_TimeOfDay, __defSpec);
>- if constexpr (!is_floating_point_v<_Rep>)
>- if (_M_f._M_spec._M_prec_kind != __format::_WP_none)
>- __throw_format_error("format error: invalid precision for duration");
>- return __it;
>+ using enum __format::_ChronoParts;
>+ return _M_f.template _M_parse<_Duration>(__pc, _EpochTime, __defSpec);
> }
>
> template<typename _Out>
>@@ -1981,12 +2067,20 @@ namespace __format
> }
>
> private:
>+ using _Duration = chrono::duration<_Rep, _Period>;
>+
> static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
> {
>- __format::_ChronoSpec<_CharT> __res{};
>+ using enum __format::_ChronoParts;
>+ auto __res = __format::__formatter_duration<_CharT>::
>+ template _S_spec_for<_Duration>(_None);
> __res._M_localized = !is_integral_v<_Rep>;
>+ // n.b. for integral format output is the same as ostream outpu
"ostream output"
> if constexpr (is_integral_v<_Rep>)
>- __res._M_chrono_specs = _GLIBCXX_WIDEN("%Q%q");
>+ {
>+ __res._M_needed = _EpochUnits|_UnitSuffix;
>+ __res._M_chrono_specs = _GLIBCXX_WIDEN("%Q%q");
>+ }
> return __res;
> }();
>
The rest of the patch below here looked fine.
More information about the Libstdc++
mailing list