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.



Reply via email to