Tomasz Kamiński <tkami...@redhat.com> writes:

> 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
> computed.
>
> In consequence _ChronoParts enum was exteneded with additional values,
> that allows more fine grained indentification:
>  * _TimeOfDay is separated into _HoursMinutesSeconds and _Subseconds,
>  * _TimeZone is separated into _ZoneAbbrev and _ZoneOffset,
>  * _LocalDays, _WeekdayIndex are defiend in included in _Date,
>  * _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.
>
> In addition to fields that can be printed using chron-spec, _ChronoData 
> stores:
>  * 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
>    be used to  compute fractional hours, minutes.
> The both total seconds fielkds we use single _TotalSeconds enumerator in
> _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
>    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,
> that is then used to format duration, hh_mm_ss and time_points specialization.
> 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.
>
>       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.
> ---
> Testing on x86_64-linux. Test passed for std/time/format* with 
> _GLIBCXX_USE_CXX11_ABI=0
> and -D_GLIBCXX_DEBUG.
> OK for trunk once all test passes?
>
>  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, 1162 insertions(+), 830 deletions(-)
>
> diff --git a/libstdc++-v3/include/bits/chrono_io.h 
> b/libstdc++-v3/include/bits/chrono_io.h
> index 4eb00f4932d..c6be859bda6 100644
> --- a/libstdc++-v3/include/bits/chrono_io.h
> +++ b/libstdc++-v3/include/bits/chrono_io.h
> @@ -1,4 +1,4 @@
> -// <chrono> Formatting -*- C++ -*-
> +// <ehrono> Formatting -*- C++ -*-

This looks like an unintentional change.

>  // Copyright The GNU Toolchain Authors.
>  //
> @@ -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,9 +199,78 @@ namespace __format
>    { __throw_format_error("format error: chrono-format-spec not valid for "
>                        "argument type"); }
>  
> +  // 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 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,
> +  };
> +
> +  [[__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>((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
> @@ -214,29 +278,24 @@ namespace __format
>        // 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.
> +      unsigned _M_floating_point_rep : 1;
> +      // Indicate that duration uses user-defined representation.
> +      unsigned _M_custom_rep : 1;
> +      unsigned _M_unused : 4;
>  
> -      basic_string_view<_CharT> _M_chrono_specs;
> -    };
> +      // Chrono parts required by format specs
> +      _ChronoParts _M_needed;
>  
> -  // 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,
> -    _ZonedDateTime = _DateTime | _TimeZone,
> -    _Duration = 128 // special case
> -  };
> -
> -  constexpr _ChronoParts
> -  operator|(_ChronoParts __x, _ChronoParts __y) noexcept
> -  { return static_cast<_ChronoParts>((int)__x | (int)__y); }
> +      basic_string_view<_CharT> _M_chrono_specs;
>  
> -  constexpr _ChronoParts&
> -  operator|=(_ChronoParts& __x, _ChronoParts __y) noexcept
> -  { return __x = __x | __y; }
> +      [[__gnu__::__always_inline__]]
> +      constexpr bool
> +      _M_needs(_ChronoParts __parts) const
> +      { return (_M_needed & __parts) != 0; }
> +    };
>  
>    template<typename _CharT>
>    struct _ChronoFormats
> @@ -340,6 +399,152 @@ namespace __format
>      { return _S_yml().substr(3); }
>    };
>  
> +  template<typename _CharT>
> +    struct _ChronoData
> +    {
> +      static constexpr size_t _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
> +      // 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 +562,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 +593,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.
> +           __spec._M_prec_kind = _WP_value;
> +          if (__finished())
> +            return __first;
>           }
>  
>         __spec._M_localized = false;
> @@ -418,7 +630,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 +641,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 +665,7 @@ namespace __format
>                 __locale_specific = true;
>                 break;
>               case 'c':
> -               __needed = _DateTime;
> +               __needed = _Date|_HoursMinutesSeconds;
>                 __allowed_mods = _Mod_E;
>                 __locale_specific = true;
>                 break;
> @@ -466,27 +680,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;
>                 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 +708,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 +731,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 +740,7 @@ namespace __format
>                 __allowed_mods = _Mod_E;
>                 break;
>               case 'X':
> -               __needed = _TimeOfDay;
> +               __needed = _HoursMinutesSeconds;
>                 __locale_specific = true;
>                 __allowed_mods = _Mod_E;
>                 break;
> @@ -535,11 +753,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 +785,16 @@ namespace __format
>               __locale_specific = true;
>             __mod = _CharT();
>  
> +           // localized format do not include subseconds
> +           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 +820,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.
>        // 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 +843,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 +937,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 +957,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 +975,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 +1004,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 +1013,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 +1057,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 +1071,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 +1101,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 +1131,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 +1145,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 +1156,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 +1186,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 +1234,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 +1265,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 +1275,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 +1302,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 +1336,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 +1368,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 +1391,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 +1430,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 +1452,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 +1469,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 +1509,93 @@ 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()));
> +
> +       const auto __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 size_t __max_prec = _ChronoData<_CharT>::_S_max_prec;
> +       constexpr size_t __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];
> +
> +       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 +1603,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 +1618,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 +1630,69 @@ 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 (__ts == 0s)
> +       {
> +         __string_view __zero
> +           = __mod ? _GLIBCXX_WIDEN("+00:00") : _GLIBCXX_WIDEN("+0000");
> +         return __format::__write(std::move(__out), __zero);
> +       }
>  
> -       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_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);
> -             }
> -         }
> -       else if constexpr (__is_specialization_of<_Tp, __utc_leap_second>)
> -         return __format::__write(std::move(__out), __utc);
> +       bool __is_neg = (__ts < 0s);
> +       __ts = chrono::abs(__ts);
> +       auto __m = chrono::floor<chrono::minutes>(__ts);
> +       auto __s = __ts - __m;
>  
> -       __no_timezone_available();
> +       _CharT __buf[6];
> +       __buf[0] = _S_plus_minus[__is_neg];
> +       __buf[2] = _S_colon;
> +       _S_fill_two_digits(__buf + 1, __m.count());
> +       _S_fill_two_digits(__buf + 2 + __mod, __s.count());
> +       __string_view __sv(__buf, 5 + __mod);
> +       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 +1751,201 @@ 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_rep(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).count();
> +       else if constexpr (_Period::den == 1)
> +         return 0u;
>         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).count();
> +        }
> +       }
>  
> -      // 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;
> +       __cd._M_subseconds = chrono::duration_cast<_Attoseconds>(__ss);
>  
> -       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 = _S_subseconds_rep(__ss);
> +       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.
> +     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 +1965,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 +2037,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 +2065,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
>         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;
>       }();
>  
> @@ -1996,13 +2088,22 @@ namespace __format
>                 bool __is_neg,
>                 basic_format_context<_Out, _CharT>& __fc) const
>       {
> +       using namespace chrono;
> +       using enum __format::_ChronoParts;
>         if constexpr (!is_integral_v<_Rep>)
>           if (_M_f._M_spec._M_chrono_specs.empty())
>             return _M_f._M_format_to_ostream(__d, __is_neg, __fc);
> -       return _M_f._M_format(__d, __fc, __is_neg);
> +
> +       __format::_ChronoData<_CharT> __cd;
> +       __cd._M_is_neg = __is_neg;
> +       auto __ts = chrono::floor<chrono::seconds>(__d);
> +       __cd._M_eseconds = __ts;
> +       if (_M_f._M_spec._M_needs(_HoursMinutesSeconds))
> +         __cd._M_fill_time(__ts);
> +       return _M_f._M_format_units(__cd, __d, __d - __ts, __fc);
>       }
>  
> -      __format::__formatter_chrono<_CharT> _M_f{__defSpec};
> +      __format::__formatter_duration<_CharT> _M_f{__defSpec};
>      };
>  
>    template<__format::__char _CharT>
> @@ -2010,20 +2111,31 @@ namespace __format
>      {
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
> -      { return _M_f._M_parse(__pc, __format::_Day, __defSpec); }
> +      {
> +     using enum __format::_ChronoParts;
> +     return _M_f._M_parse(__pc, _Day|_WeekdayIndex, __defSpec);
> +      }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
>       format(const chrono::day& __t,
>              basic_format_context<_Out, _CharT>& __fc) const
> -     { return _M_f._M_format(__t, __fc); }
> +     {
> +       __format::_ChronoData<_CharT> __cd{};
> +       __cd._M_fill_day(__t, __defSpec._M_needed);
> +       return _M_f._M_format(__cd, __fc);
> +     }
>  
>      private:
>        static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
>       {
> +       using __format::_ChronoFormats;
> +       using enum __format::_ChronoParts;
> +
>         __format::_ChronoSpec<_CharT> __res{};
>         __res._M_debug = true;
> -       __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_d();
> +       __res._M_needed = _Day;
> +       __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_d();
>         return __res;
>       }();
>  
> @@ -2035,22 +2147,33 @@ namespace __format
>      {
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
> -      { return _M_f._M_parse(__pc, __format::_Month, __defSpec); }
> +      {
> +     using enum __format::_ChronoParts;
> +     return _M_f._M_parse(__pc, _Month, __defSpec);
> +      }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
>       format(const chrono::month& __t,
>              basic_format_context<_Out, _CharT>& __fc) const
> -     { return _M_f._M_format(__t, __fc); }
> +     {
> +       __format::_ChronoData<_CharT> __cd{};
> +       __cd._M_month = __t;
> +       return _M_f._M_format(__cd, __fc);
> +     }
>  
>      private:
>        static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
>       {
> +       using __format::_ChronoFormats;
> +       using enum __format::_ChronoParts;
> +
>         __format::_ChronoSpec<_CharT> __res{};
>         __res._M_debug = true;
>         __res._M_localized = true;
>         __res._M_locale_specific = true;
> -       __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_m();
> +       __res._M_needed = _Month;
> +       __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_m();
>         return __res;
>       }();
>  
> @@ -2062,20 +2185,31 @@ namespace __format
>      {
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
> -      { return _M_f._M_parse(__pc, __format::_Year, __defSpec); }
> +      {
> +     using enum __format::_ChronoParts;
> +     return _M_f._M_parse(__pc, _Year, __defSpec);
> +      }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
>       format(const chrono::year& __t,
>              basic_format_context<_Out, _CharT>& __fc) const
> -     { return _M_f._M_format(__t, __fc); }
> +     {
> +       __format::_ChronoData<_CharT> __cd{};
> +       __cd._M_year = __t;
> +       return _M_f._M_format(__cd, __fc);
> +     }
>  
>      private:
>        static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
>       {
> +       using __format::_ChronoFormats;
> +       using enum __format::_ChronoParts;
> +
>         __format::_ChronoSpec<_CharT> __res{};
>         __res._M_debug = true;
> -       __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_y();
> +       __res._M_needed = _Year;
> +       __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_y();
>         return __res;
>       }();
>  
> @@ -2087,22 +2221,33 @@ namespace __format
>      {
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
> -      { return _M_f._M_parse(__pc, __format::_Weekday, __defSpec); }
> +      {
> +     using enum __format::_ChronoParts;
> +     return _M_f._M_parse(__pc, _Weekday, __defSpec);
> +      }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
>       format(const chrono::weekday& __t,
>              basic_format_context<_Out, _CharT>& __fc) const
> -     { return _M_f._M_format(__t, __fc); }
> +     {
> +       __format::_ChronoData<_CharT> __cd{};
> +       __cd._M_weekday = __t;
> +       return _M_f._M_format(__cd, __fc);
> +     }
>  
>      private:
>        static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
>       {
> +       using __format::_ChronoFormats;
> +       using enum __format::_ChronoParts;
> +
>         __format::_ChronoSpec<_CharT> __res{};
>         __res._M_debug = true;
>         __res._M_localized = true;
>         __res._M_locale_specific = true;
> -       __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_w();
> +       __res._M_needed = _Weekday;
> +       __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_w();
>         return __res;
>       }();
>  
> @@ -2114,22 +2259,33 @@ namespace __format
>      {
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
> -      { return _M_f._M_parse(__pc, __format::_Weekday, __defSpec); }
> +      {
> +     using enum __format::_ChronoParts;
> +     return _M_f._M_parse(__pc, _IndexedWeekday, __defSpec);
> +      }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
>       format(const chrono::weekday_indexed& __t,
>              basic_format_context<_Out, _CharT>& __fc) const
> -     { return _M_f._M_format(__t, __fc); }
> +     {
> +       __format::_ChronoData<_CharT> __cd{};
> +       __cd._M_fill_weekday(__t, __defSpec._M_needed);
> +       return _M_f._M_format(__cd, __fc);
> +     }
>  
>      private:
>        static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
>       {
> +       using __format::_ChronoFormats;
> +       using enum __format::_ChronoParts;
> +
>         __format::_ChronoSpec<_CharT> __res{};
>         __res._M_debug = true;
>         __res._M_localized = true;
>         __res._M_locale_specific = true;
> -       __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_wi();
> +       __res._M_needed = _IndexedWeekday;
> +       __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_wi();
>         return __res;
>       }();
>  
> @@ -2141,22 +2297,33 @@ namespace __format
>      {
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
> -      { return _M_f._M_parse(__pc, __format::_Weekday, __defSpec); }
> +      {
> +     using enum __format::_ChronoParts;
> +     return _M_f._M_parse(__pc, _Weekday, __defSpec);
> +      }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
>       format(const chrono::weekday_last& __t,
>              basic_format_context<_Out, _CharT>& __fc) const
> -     { return _M_f._M_format(__t, __fc); }
> +     {
> +       __format::_ChronoData<_CharT> __cd{};
> +       __cd._M_weekday = __t.weekday();
> +       return _M_f._M_format(__cd, __fc);
> +     }
>  
>      private:
>        static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
>       {
> +       using __format::_ChronoFormats;
> +       using enum __format::_ChronoParts;
> +
>         __format::_ChronoSpec<_CharT> __res{};
>         __res._M_debug = true;
>         __res._M_localized = true;
>         __res._M_locale_specific = true;
> -       __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_wl();
> +       __res._M_needed = _Weekday;
> +       __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_wl();
>         return __res;
>       }();
>  
> @@ -2169,24 +2336,33 @@ namespace __format
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
>        {
> -     return _M_f._M_parse(__pc, __format::_Month|__format::_Day,
> -                          __defSpec);
> +     using enum __format::_ChronoParts;
> +     return _M_f._M_parse(__pc, _Month|_Day|_WeekdayIndex, __defSpec);
>        }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
>       format(const chrono::month_day& __t,
>              basic_format_context<_Out, _CharT>& __fc) const
> -     { return _M_f._M_format(__t, __fc); }
> +     {
> +       __format::_ChronoData<_CharT> __cd{};
> +       __cd._M_month = __t.month();
> +       __cd._M_fill_day(__t.day(), __defSpec._M_needed);
> +       return _M_f._M_format(__cd, __fc);
> +     }
>  
>      private:
>        static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
>       {
> +       using __format::_ChronoFormats;
> +       using enum __format::_ChronoParts;
> +
>         __format::_ChronoSpec<_CharT> __res{};
>         __res._M_debug = true;
>         __res._M_localized = true;
>         __res._M_locale_specific = true;
> -       __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_md();
> +       __res._M_needed = _Month|_Day;
> +       __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_md();
>         return __res;
>       }();
>  
> @@ -2199,23 +2375,32 @@ namespace __format
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
>        {
> -     return _M_f._M_parse(__pc, __format::_Month, __defSpec);
> +     using enum __format::_ChronoParts;
> +     return _M_f._M_parse(__pc, _Month, __defSpec);
>        }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
>       format(const chrono::month_day_last& __t,
>              basic_format_context<_Out, _CharT>& __fc) const
> -     { return _M_f._M_format(__t, __fc); }
> +     {
> +       __format::_ChronoData<_CharT> __cd{};
> +       __cd._M_month = __t.month();
> +       return _M_f._M_format(__cd, __fc);
> +     }
>  
>      private:
>         static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
>       {
> +       using __format::_ChronoFormats;
> +       using enum __format::_ChronoParts;
> +
>         __format::_ChronoSpec<_CharT> __res{};
>         __res._M_debug = true;
>         __res._M_localized = true;
>         __res._M_locale_specific = true;
> -       __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ml();
> +       __res._M_needed = _Month;
> +       __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_ml();
>         return __res;
>       }();
>  
> @@ -2228,24 +2413,33 @@ namespace __format
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
>        {
> -     return _M_f._M_parse(__pc, __format::_Month|__format::_Weekday,
> -                          __defSpec);
> +     using enum __format::_ChronoParts;
> +     return _M_f._M_parse(__pc, _Month|_IndexedWeekday, __defSpec);
>        }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
>       format(const chrono::month_weekday& __t,
>              basic_format_context<_Out, _CharT>& __fc) const
> -     { return _M_f._M_format(__t, __fc); }
> +     {
> +       __format::_ChronoData<_CharT> __cd{};
> +       __cd._M_month = __t.month();
> +       __cd._M_fill_weekday(__t.weekday_indexed(), __defSpec._M_needed);
> +       return _M_f._M_format(__cd, __fc);
> +     }
>  
>      private:
>        static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
>       {
> +       using __format::_ChronoFormats;
> +       using enum __format::_ChronoParts;
> +
>         __format::_ChronoSpec<_CharT> __res{};
>         __res._M_debug = true;
>         __res._M_localized = true;
>         __res._M_locale_specific = true;
> -       __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_mwi();
> +       __res._M_needed = _Month|_IndexedWeekday;
> +       __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_mwi();
>         return __res;
>       }();
>  
> @@ -2258,24 +2452,33 @@ namespace __format
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
>        {
> -     return _M_f._M_parse(__pc, __format::_Month|__format::_Weekday,
> -                          __defSpec);
> +     using enum __format::_ChronoParts;
> +     return _M_f._M_parse(__pc, _Month|_Weekday, __defSpec);
>        }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
>       format(const chrono::month_weekday_last& __t,
>              basic_format_context<_Out, _CharT>& __fc) const
> -     { return _M_f._M_format(__t, __fc); }
> +     {
> +       __format::_ChronoData<_CharT> __cd{};
> +       __cd._M_month = __t.month();
> +       __cd._M_weekday = __t.weekday_last().weekday();
> +       return _M_f._M_format(__cd, __fc);
> +     }
>  
>      private:
>         static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
>       {
> +       using __format::_ChronoFormats;
> +       using enum __format::_ChronoParts;
> +
>         __format::_ChronoSpec<_CharT> __res{};
>         __res._M_debug = true;
>         __res._M_localized = true;
>         __res._M_locale_specific = true;
> -       __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_mwl();
> +       __res._M_needed = _Month|_Weekday;
> +       __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_mwl();
>         return __res;
>       }();
>  
> @@ -2288,24 +2491,32 @@ namespace __format
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
>        {
> -     return _M_f._M_parse(__pc, __format::_Year|__format::_Month,
> -                          __defSpec);
> +     using enum __format::_ChronoParts;
> +     return _M_f._M_parse(__pc, _Year|_Month, __defSpec);
>        }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
>       format(const chrono::year_month& __t,
>              basic_format_context<_Out, _CharT>& __fc) const
> -     { return _M_f._M_format(__t, __fc); }
> +     {
> +       __format::_ChronoData<_CharT> __cd{};
> +       __cd._M_fill_year_month(__t, __defSpec._M_needed);
> +       return _M_f._M_format(__cd, __fc);
> +     }
>  
>      private:
>         static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
>       {
> +       using __format::_ChronoFormats;
> +       using enum __format::_ChronoParts;
> +
>         __format::_ChronoSpec<_CharT> __res{};
>         __res._M_debug = true;
>         __res._M_localized = true;
>         __res._M_locale_specific = true;
> -       __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ym();
> +       __res._M_needed = _Year|_Month;
> +       __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_ym();
>         return __res;
>       }();
>  
> @@ -2317,20 +2528,38 @@ namespace __format
>      {
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
> -      { return _M_f._M_parse(__pc, __format::_Date, __defSpec); }
> +      {
> +     using enum __format::_ChronoParts;
> +     return _M_f._M_parse(__pc, _Date, __defSpec);
> +      }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
>       format(const chrono::year_month_day& __t,
>              basic_format_context<_Out, _CharT>& __fc) const
> -     { return _M_f._M_format(__t, __fc); }
> +     {
> +       __format::_ChronoData<_CharT> __cd{};
> +       auto __parts = _M_f._M_spec._M_needed;
> +       __parts = __cd._M_fill_year_month(__t, __parts);
> +       __parts = __cd._M_fill_day(__t.day(), __parts);
> +       if (__parts == 0)
> +         return _M_f._M_format(__cd, __fc);
> +
> +       chrono::local_days __ld(__t);
> +       __cd._M_fill_ldays(__ld, __parts);
> +       return _M_f._M_format(__cd, __fc);
> +     }
>  
>      private:
>        static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
>       {
> +       using __format::_ChronoFormats;
> +       using enum __format::_ChronoParts;
> +
>         __format::_ChronoSpec<_CharT> __res{};
>         __res._M_debug = true;
> -       __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_f();
> +       __res._M_needed = _YearMonthDay;
> +       __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_f();
>         return __res;
>       }();
>  
> @@ -2342,22 +2571,44 @@ namespace __format
>      {
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
> -      { return _M_f._M_parse(__pc, __format::_Date, __defSpec); }
> +      {
> +     using enum __format::_ChronoParts;
> +     return _M_f._M_parse(__pc, _Date, __defSpec);
> +      }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
>       format(const chrono::year_month_day_last& __t,
>              basic_format_context<_Out, _CharT>& __fc) const
> -     { return _M_f._M_format(__t, __fc); }
> +     {
> +       __format::_ChronoData<_CharT> __cd{};
> +       auto __parts = _M_f._M_spec._M_needed;
> +       __parts = __cd._M_fill_year_month(__t, __parts);
> +       if (__parts == 0)
> +         return _M_f._M_format(__cd, __fc);
> +
> +       chrono::local_days __ld(__t);
> +       __parts = __cd._M_fill_ldays(__ld, __parts);
> +       if (__parts == 0)
> +         return _M_f._M_format(__cd, __fc);
> +
> +       chrono::year_month_day __ymd(__ld);
> +       __cd._M_fill_day(__ymd.day(), __parts);
> +       return _M_f._M_format(__cd, __fc);
> +     }
>  
>      private:
>       static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
>       {
> +       using __format::_ChronoFormats;
> +       using enum __format::_ChronoParts;
> +
>         __format::_ChronoSpec<_CharT> __res{};
>         __res._M_debug = true;
>         __res._M_localized = true;
>         __res._M_locale_specific = true;
> -       __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_yml();
> +       __res._M_needed = _Year|_Month;
> +       __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_yml();
>         return __res;
>       }();
>  
> @@ -2369,22 +2620,46 @@ namespace __format
>      {
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
> -      { return _M_f._M_parse(__pc, __format::_Date, __defSpec); }
> +      {
> +     using enum __format::_ChronoParts;
> +     return _M_f._M_parse(__pc, _Date, __defSpec);
> +      }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
>       format(const chrono::year_month_weekday& __t,
>              basic_format_context<_Out, _CharT>& __fc) const
> -     { return _M_f._M_format(__t, __fc); }
> +     {
> +       __format::_ChronoData<_CharT> __cd{};
> +       auto __parts = _M_f._M_spec._M_needed;
> +       __parts = __cd._M_fill_year_month(__t, __parts);
> +       __parts = __cd._M_fill_weekday(__t.weekday_indexed(), __parts);
> +       if (__parts == 0)
> +         return _M_f._M_format(__cd, __fc);
> +
> +       chrono::local_days __ld(__t);
> +       __parts = __cd._M_fill_ldays(__ld, __parts);
> +       if (__parts == 0)
> +         return _M_f._M_format(__cd, __fc);
> +
> +       chrono::year_month_day __ymd(__ld);
> +       // n.b. weekday index is supplied in input, do not override it
> +       __cd._M_day = __ymd.day();
> +       return _M_f._M_format(__cd, __fc);
> +     }
>  
>      private:
>       static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
>       {
> +       using __format::_ChronoFormats;
> +       using enum __format::_ChronoParts;
> +
>         __format::_ChronoSpec<_CharT> __res{};
>         __res._M_debug = true;
>         __res._M_localized = true;
>         __res._M_locale_specific = true;
> -       __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ymwi();
> +       __res._M_needed = _Year|_Month|_IndexedWeekday;
> +       __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_ymwi();
>         return __res;
>       }();
>  
> @@ -2396,22 +2671,46 @@ namespace __format
>      {
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
> -      { return _M_f._M_parse(__pc, __format::_Date, __defSpec); }
> +      {
> +     using enum __format::_ChronoParts;
> +     return _M_f._M_parse(__pc, _Date, __defSpec);
> +      }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
>       format(const chrono::year_month_weekday_last& __t,
>              basic_format_context<_Out, _CharT>& __fc) const
> -             { return _M_f._M_format(__t, __fc); }
> +     {
> +       __format::_ChronoData<_CharT> __cd{};
> +       auto __parts = _M_f._M_spec._M_needed;
> +       __parts = __cd._M_fill_year_month(__t, __parts);
> +       __cd._M_weekday = __t.weekday_last().weekday();
> +       __parts -= __format::_ChronoParts::_Weekday;
> +       if (__parts == 0)
> +         return _M_f._M_format(__cd, __fc);
> +
> +       chrono::local_days __ld(__t);
> +       __parts = __cd._M_fill_ldays(__ld, __parts);
> +       if (__parts == 0)
> +         return _M_f._M_format(__cd, __fc);
> +
> +       chrono::year_month_day __ymd(__ld);
> +       __cd._M_fill_day(__ymd.day(), __parts);
> +       return _M_f._M_format(__cd, __fc);
> +     }
>  
>      private:
>       static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
>       {
> +       using __format::_ChronoFormats;
> +       using enum __format::_ChronoParts;
> +
>         __format::_ChronoSpec<_CharT> __res{};
>         __res._M_debug = true;
>         __res._M_localized = true;
>         __res._M_locale_specific = true;
> -       __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ymwl();
> +       __res._M_needed = _Year|_Month|_Weekday;
> +       __res._M_chrono_specs = _ChronoFormats<_CharT>::_S_ymwl();
>         return __res;
>       }();
>  
> @@ -2423,24 +2722,43 @@ namespace __format
>      {
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
> -      { return _M_f._M_parse(__pc, __format::_TimeOfDay, __defSpec); }
> +      {
> +     using enum __format::_ChronoParts;
> +     return _M_f.template _M_parse<_Precision>(__pc, _Time, __defSpec);
> +      }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
>       format(const chrono::hh_mm_ss<chrono::duration<_Rep, _Period>>& __t,
>              basic_format_context<_Out, _CharT>& __fc) const
> -     { return _M_f._M_format(__t, __fc); }
> +     {
> +       using enum __format::_ChronoParts;
> +
> +       __format::_ChronoData<_CharT> __cd;
> +       __cd._M_is_neg = __t.is_negative();
> +       __cd._M_hours = __t.hours();
> +       __cd._M_minutes = __t.minutes();
> +       __cd._M_seconds = __t.seconds();
> +
> +       _Precision __d(0);
> +       // n.b. computing total duration or total seconds may overflow,
> +       // do not compute them if not requested.
> +       if (_M_f._M_spec._M_needs(_EpochUnits))
> +         __d = __t.to_duration();
> +       if (_M_f._M_spec._M_needs(_TotalSeconds))
> +         __cd._M_eseconds
> +           = __cd._M_hours + __cd._M_minutes + __cd._M_seconds;
> +       return _M_f._M_format_units(__cd, __d, __t.subseconds(), __fc);
> +     }
>  
>      private:
> -      static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
> -     {
> -       __format::_ChronoSpec<_CharT> __res{};
> -       __res._M_localized = true;
> -       __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_t();
> -       return __res;
> -     }();
> +      using _Precision
> +     = typename chrono::hh_mm_ss<chrono::duration<_Rep, _Period>>::precision;
> +      static constexpr __format::_ChronoSpec<_CharT> __defSpec =
> +     __format::__formatter_duration<_CharT>::
> +        template _S_spec_for<_Precision>(__format::_ChronoParts::_Time);
>  
> -      __format::__formatter_chrono<_CharT> _M_f{__defSpec};
> +      __format::__formatter_duration<_CharT> _M_f{__defSpec};
>      };
>  
>  #if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
> @@ -2485,7 +2803,9 @@ namespace __format
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
>        {
> -     auto __next = _M_f._M_parse(__pc, __format::_ZonedDateTime, __defSpec);
> +     using enum __format::_ChronoParts;
> +     auto __next
> +       = _M_f.template _M_parse<_Duration>(__pc, _ZonedDateTime, __defSpec);
>       if constexpr (!__stream_insertable)
>         if (_M_f._M_spec._M_chrono_specs.empty())
>           __format::__invalid_chrono_spec(); // chrono-specs can't be empty
> @@ -2496,7 +2816,15 @@ namespace __format
>         typename basic_format_context<_Out, _CharT>::iterator
>         format(const chrono::sys_time<_Duration>& __t,
>             basic_format_context<_Out, _CharT>& __fc) const
> -       { return _M_f._M_format(__t, __fc); }
> +       {
> +      __format::_ChronoData<_CharT> __cd{};
> +      __cd._M_fill_utc_zone();
> +
> +      _Duration __ed = __t.time_since_epoch();
> +      __cd._M_eseconds = chrono::floor<chrono::seconds>(__ed);
> +      __cd._M_lseconds = chrono::local_seconds(__cd._M_eseconds);
> +      return _M_f._M_format_time_point(__cd, __ed, __fc);
> +       }
>  
>      private:
>        static constexpr bool __stream_insertable
> @@ -2505,20 +2833,17 @@ namespace __format
>  
>        static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
>       {
> -       __format::_ChronoSpec<_CharT> __res{};
> +       using enum __format::_ChronoParts;
> +       __format::_ChronoParts __needed = _DateTime;
>         if constexpr (!__stream_insertable)
> -         return __res;
> +         __needed = _None;
>         else if constexpr (is_convertible_v<_Duration, chrono::days>)
> -         __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_f();
> -       else
> -         {
> -           __res._M_localized = true;
> -           __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ft();
> -         }
> -       return __res;
> +         __needed = _Date;
> +       return __format::__formatter_duration<_CharT>::
> +                template _S_spec_for<_Duration>(__needed);
>       }();
>  
> -      __format::__formatter_chrono<_CharT> _M_f{__defSpec};
> +      __format::__formatter_duration<_CharT> _M_f{__defSpec};
>      };
>  
>    template<typename _Duration, __format::__char _CharT>
> @@ -2527,40 +2852,43 @@ namespace __format
>      {
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
> -      { return _M_f._M_parse(__pc, __format::_ZonedDateTime, __defSpec); }
> +      {
> +     using enum __format::_ChronoParts;
> +     return _M_f.template _M_parse<_Duration>(__pc, _ZonedDateTime, 
> __defSpec);
> +      }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
>       format(const chrono::utc_time<_Duration>& __t,
>              basic_format_context<_Out, _CharT>& __fc) const
>       {
> +       using __format::_ChronoParts;
> +       using namespace chrono;
> +       __format::_ChronoData<_CharT> __cd{};
> +       __cd._M_fill_utc_zone();
> +
> +       _Duration __ed = __t.time_since_epoch();
> +       __cd._M_eseconds = chrono::floor<seconds>(__ed);
>         // Adjust by removing leap seconds to get equivalent sys_time.
>         // We can't just use clock_cast because we want to know if the time
>         // falls within a leap second insertion, and format seconds as "60".
> -       using chrono::__detail::__utc_leap_second;
> -       using chrono::seconds;
> -       using chrono::sys_time;
> -       using _CDur = common_type_t<_Duration, seconds>;
>         const auto __li = chrono::get_leap_second_info(__t);
> -       sys_time<_CDur> __s{__t.time_since_epoch() - __li.elapsed};
> -       if (!__li.is_leap_second) [[likely]]
> -         return _M_f._M_format(__s, __fc);
> -       else
> -         return _M_f._M_format(__utc_leap_second(__s), __fc);
> +       __cd._M_lseconds = local_seconds(__cd._M_eseconds - __li.elapsed);
> +       auto __parts = _M_f._M_spec._M_needed - _ChronoParts::_TotalSeconds;
> +       if ((__parts & _ChronoParts::_DateTime) != 0)
> +       {
> +         __cd._M_fill_date_time(__cd._M_lseconds, __parts);
> +         __cd._M_seconds += seconds(__li.is_leap_second);
> +       }
> +       return _M_f._M_format_units(__cd, __ed, __ed - __cd._M_eseconds, 
> __fc);
>       }
>  
>      private:
> -      friend formatter<chrono::__detail::__utc_leap_second<_Duration>, 
> _CharT>;
> +      static constexpr __format::_ChronoSpec<_CharT> __defSpec =
> +     __format::__formatter_duration<_CharT>::
> +       template _S_spec_for<_Duration>(__format::_ChronoParts::_DateTime);
>  
> -      static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
> -     {
> -       __format::_ChronoSpec<_CharT> __res{};
> -       __res._M_localized = true;
> -       __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ft();
> -       return __res;
> -     }();
> -
> -      __format::__formatter_chrono<_CharT> _M_f{__defSpec};
> +      __format::__formatter_duration<_CharT> _M_f{__defSpec};
>      };
>  
>    template<typename _Duration, __format::__char _CharT>
> @@ -2569,37 +2897,34 @@ namespace __format
>      {
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
> -      { return _M_f._M_parse(__pc, __format::_ZonedDateTime, __defSpec); }
> +      {
> +     using enum __format::_ChronoParts;
> +     return _M_f.template _M_parse<_Duration>(__pc, _ZonedDateTime, 
> __defSpec);
> +      }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
>       format(const chrono::tai_time<_Duration>& __t,
>              basic_format_context<_Out, _CharT>& __fc) const
>       {
> -       // Convert to __local_time_fmt with abbrev "TAI" and offset 0s.
> -       // We use __local_time_fmt and not sys_time (as the standard implies)
> -       // because %Z for sys_time would print "UTC" and we want "TAI" here.
> +       using namespace chrono;
> +       __format::_ChronoData<_CharT> __cd{};
> +       __cd._M_fill_zone("TAI", L"TAI");
>  
> +       _Duration __ed = __t.time_since_epoch();
> +       __cd._M_eseconds = chrono::floor<seconds>(__ed);
>         // Offset is 1970y/January/1 - 1958y/January/1
>         constexpr chrono::days __tai_offset = chrono::days(4383);
> -       using _CDur = common_type_t<_Duration, chrono::days>;
> -       chrono::local_time<_CDur> __lt(__t.time_since_epoch() - __tai_offset);
> -       const string __abbrev("TAI", 3);
> -       const chrono::seconds __off = 0s;
> -       const auto __lf = chrono::local_time_format(__lt, &__abbrev, &__off);
> -       return _M_f._M_format(__lf, __fc);
> +       __cd._M_lseconds = local_seconds(__cd._M_eseconds - __tai_offset);
> +       return _M_f._M_format_time_point(__cd, __ed, __fc);
>       }
>  
>      private:
> -      static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
> -     {
> -       __format::_ChronoSpec<_CharT> __res{};
> -       __res._M_localized = true;
> -       __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ft();
> -       return __res;
> -     }();
> +      static constexpr __format::_ChronoSpec<_CharT> __defSpec =
> +     __format::__formatter_duration<_CharT>::
> +       template _S_spec_for<_Duration>(__format::_ChronoParts::_DateTime);
>  
> -      __format::__formatter_chrono<_CharT> _M_f{__defSpec};
> +      __format::__formatter_duration<_CharT> _M_f{__defSpec};
>      };
>  
>    template<typename _Duration, __format::__char _CharT>
> @@ -2608,37 +2933,34 @@ namespace __format
>      {
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
> -      { return _M_f._M_parse(__pc, __format::_ZonedDateTime, __defSpec); }
> +      {
> +     using enum __format::_ChronoParts;
> +     return _M_f.template _M_parse<_Duration>(__pc, _ZonedDateTime, 
> __defSpec);
> +      }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
>       format(const chrono::gps_time<_Duration>& __t,
>              basic_format_context<_Out, _CharT>& __fc) const
>       {
> -       // Convert to __local_time_fmt with abbrev "GPS" and offset 0s.
> -       // We use __local_time_fmt and not sys_time (as the standard implies)
> -       // because %Z for sys_time would print "UTC" and we want "GPS" here.
> +       using namespace chrono;
> +       __format::_ChronoData<_CharT> __cd{};
> +       __cd._M_fill_zone("GPS", L"GPS");
>  
> +       _Duration __ed = __t.time_since_epoch();
> +       __cd._M_eseconds = chrono::floor<seconds>(__ed);
>         // Offset is 1980y/January/Sunday[1] - 1970y/January/1
>         constexpr chrono::days __gps_offset = chrono::days(3657);
> -       using _CDur = common_type_t<_Duration, chrono::days>;
> -       chrono::local_time<_CDur> __lt(__t.time_since_epoch() + __gps_offset);
> -       const string __abbrev("GPS", 3);
> -       const chrono::seconds __off = 0s;
> -       const auto __lf = chrono::local_time_format(__lt, &__abbrev, &__off);
> -       return _M_f._M_format(__lf, __fc);
> +       __cd._M_lseconds = local_seconds(__cd._M_eseconds + __gps_offset);
> +       return _M_f._M_format_time_point(__cd, __ed, __fc);
>       }
>  
>      private:
> -      static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
> -     {
> -       __format::_ChronoSpec<_CharT> __res{};
> -       __res._M_localized = true;
> -       __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ft();
> -       return __res;
> -     }();
> +      static constexpr __format::_ChronoSpec<_CharT> __defSpec =
> +     __format::__formatter_duration<_CharT>::
> +       template _S_spec_for<_Duration>(__format::_ChronoParts::_DateTime);
>  
> -      __format::__formatter_chrono<_CharT> _M_f{__defSpec};
> +      __format::__formatter_duration<_CharT> _M_f{__defSpec};
>      };
>  
>    template<typename _Duration, __format::__char _CharT>
> @@ -2646,7 +2968,10 @@ namespace __format
>      {
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
> -      { return _M_f._M_parse(__pc, __format::_ZonedDateTime, __defSpec); }
> +      {
> +     using enum __format::_ChronoParts;
> +     return _M_f.template _M_parse<_Duration>(__pc, _ZonedDateTime, 
> __defSpec);
> +      }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
> @@ -2654,19 +2979,23 @@ namespace __format
>              basic_format_context<_Out, _CharT>& __fc) const
>       {
>         using namespace chrono;
> -       return _M_f._M_format(chrono::clock_cast<system_clock>(__t), __fc);
> +       __format::_ChronoData<_CharT> __cd{};
> +       __cd._M_fill_utc_zone();
> +
> +       _Duration __ed = __t.time_since_epoch();
> +       __cd._M_eseconds = chrono::floor<seconds>(__ed);
> +       auto __st = chrono::clock_cast<system_clock>(__t);
> +       __cd._M_lseconds
> +         = local_seconds(chrono::floor<seconds>(__st.time_since_epoch()));
> +       return _M_f._M_format_time_point(__cd, __ed, __fc);
>       }
>  
>      private:
> -       static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
> -     {
> -       __format::_ChronoSpec<_CharT> __res{};
> -       __res._M_localized = true;
> -       __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ft();
> -       return __res;
> -     }();
> +      static constexpr __format::_ChronoSpec<_CharT> __defSpec =
> +     __format::__formatter_duration<_CharT>::
> +       template _S_spec_for<_Duration>(__format::_ChronoParts::_DateTime);
>  
> -      __format::__formatter_chrono<_CharT> _M_f{__defSpec};
> +      __format::__formatter_duration<_CharT> _M_f{__defSpec};
>       };
>  
>    template<typename _Duration, __format::__char _CharT>
> @@ -2674,29 +3003,35 @@ namespace __format
>      {
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
> -      {  return _M_f._M_parse(__pc, __format::_DateTime, __defSpec); }
> +      {
> +     using enum __format::_ChronoParts;
> +     return _M_f.template _M_parse<_Duration>(__pc, _DateTime, __defSpec);
> +      }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
> -     format(const chrono::local_time<_Duration>& __t,
> +     format(const chrono::local_time<_Duration>& __lt,
>              basic_format_context<_Out, _CharT>& __fc) const
> -     { return _M_f._M_format(__t, __fc); }
> +     {
> +       __format::_ChronoData<_CharT> __cd{};
> +       _Duration __ed = __lt.time_since_epoch();
> +       __cd._M_lseconds = chrono::floor<chrono::seconds>(__lt);
> +       __cd._M_eseconds = __cd._M_lseconds.time_since_epoch();
> +       return _M_f._M_format_time_point(__cd, __ed, __fc);
> +     }
>  
>      private:
>        static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
>       {
> -       __format::_ChronoSpec<_CharT> __res{};
> +       using enum __format::_ChronoParts;
> +       __format::_ChronoParts __needed = _DateTime;
>         if constexpr (is_convertible_v<_Duration, chrono::days>)
> -         __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_f();
> -       else
> -         {
> -           __res._M_localized = true;
> -           __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ft();
> -         }
> -       return __res;
> +         __needed = _Date;
> +       return __format::__formatter_duration<_CharT>::
> +                template _S_spec_for<_Duration>(__needed);
>       }();
>  
> -      __format::__formatter_chrono<_CharT> _M_f{__defSpec};
> +      __format::__formatter_duration<_CharT> _M_f{__defSpec};
>      };
>  
>    template<typename _Duration, __format::__char _CharT>
> @@ -2704,24 +3039,59 @@ namespace __format
>      {
>        constexpr typename basic_format_parse_context<_CharT>::iterator
>        parse(basic_format_parse_context<_CharT>& __pc)
> -      { return _M_f._M_parse(__pc, __format::_ZonedDateTime, __defSpec); }
> +      {
> +     using enum __format::_ChronoParts;
> +     return _M_f.template _M_parse<_Duration>(__pc, _ZonedDateTime, 
> __defSpec);
> +      }
>  
>        template<typename _Out>
>       typename basic_format_context<_Out, _CharT>::iterator
> -     format(const chrono::__detail::__local_time_fmt<_Duration>& __t,
> +     format(const chrono::__detail::__local_time_fmt<_Duration>& __zt,
>              basic_format_context<_Out, _CharT>& __fc) const
> -     { return _M_f._M_format(__t, __fc); }
> +     {
> +       using enum __format::_ChronoParts;
> +       __format::_ChronoData<_CharT> __cd{};
> +
> +       if (_M_f._M_spec._M_needs(_ZoneOffset))
> +         {
> +           if (!__zt._M_offset_sec)
> +             std::__throw_format_error("format error: no timezone available 
> for %z");
> +           __cd._M_zone_offset = *__zt._M_offset_sec;
> +         }
> +
> +       basic_string<_CharT> __zone_store;
> +       if (_M_f._M_spec._M_needs(_ZoneAbbrev))
> +         {
> +           if (!__zt._M_abbrev)
> +             std::__throw_format_error("format error: no timezone available 
> for %Z");
> +
> +           __cd._M_zone_cstr = __zt._M_abbrev->data();
> +           if constexpr (is_same_v<_CharT, char>)
> +             __cd._M_zone_abbrev = *__zt._M_abbrev;
> +           else
> +             {
> +               // TODO: use resize_for_override
> +               __zone_store.resize(__zt._M_abbrev->size());
> +               auto& __ct = use_facet<ctype<_CharT>>(_M_f._M_locale(__fc));
> +               __ct.widen(__zt._M_abbrev->data(),
> +                          __zt._M_abbrev->data() + __zt._M_abbrev->size(),
> +                          __zone_store.data());
> +               __cd._M_zone_abbrev = __zone_store;
> +             }
> +         }
> +
> +       _Duration __ed = __zt._M_time.time_since_epoch();
> +       __cd._M_lseconds = chrono::floor<chrono::seconds>(__zt._M_time);
> +       __cd._M_eseconds = __cd._M_lseconds.time_since_epoch();
> +       return _M_f._M_format_time_point(__cd, __ed, __fc);
> +     }
>  
>      private:
> -       static constexpr __format::_ChronoSpec<_CharT> __defSpec = []
> -     {
> -       __format::_ChronoSpec<_CharT> __res{};
> -       __res._M_localized = true;
> -       __res._M_chrono_specs = __format::_ChronoFormats<_CharT>::_S_ftz();
> -       return __res;
> -     }();
> +      static constexpr __format::_ChronoSpec<_CharT> __defSpec =
> +     __format::__formatter_duration<_CharT>::
> +       template 
> _S_spec_for<_Duration>(__format::_ChronoParts::_ZonedDateTime);
>  
> -      __format::__formatter_chrono<_CharT> _M_f{__defSpec};
> +      __format::__formatter_duration<_CharT> _M_f{__defSpec};
>      };
>  
>  #if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
> @@ -2745,18 +3115,6 @@ namespace __format
>      };
>  #endif
>  
> -  // Partial specialization needed for %c formatting of __utc_leap_second.
> -  template<typename _Duration, __format::__char _CharT>
> -    struct formatter<chrono::__detail::__utc_leap_second<_Duration>, _CharT>
> -    : formatter<chrono::utc_time<_Duration>, _CharT>
> -    {
> -      template<typename _Out>
> -     typename basic_format_context<_Out, _CharT>::iterator
> -     format(const chrono::__detail::__utc_leap_second<_Duration>& __t,
> -            basic_format_context<_Out, _CharT>& __fc) const
> -     { return this->_M_f._M_format(__t, __fc); }
> -    };
> -
>  namespace chrono
>  {
>  /// @addtogroup chrono
> @@ -3514,8 +3872,7 @@ namespace __detail
>        if (!__offset)
>       __offset = &__off;
>        using __format::_ChronoParts;
> -      auto __need = _ChronoParts::_Year | _ChronoParts::_Month
> -                 | _ChronoParts::_Day | _ChronoParts::_TimeOfDay;
> +      auto __need = _ChronoParts::_YearMonthDay | _ChronoParts::_TimeOfDay;
>        __detail::_Parser_t<_Duration> __p(__need);
>        if (__p(__is, __fmt, __abbrev, __offset))
>       {
> @@ -3573,8 +3930,7 @@ namespace __detail
>               minutes* __offset = nullptr)
>      {
>        using __format::_ChronoParts;
> -      auto __need = _ChronoParts::_Year | _ChronoParts::_Month
> -                 | _ChronoParts::_Day | _ChronoParts::_TimeOfDay;
> +      auto __need = _ChronoParts::_YearMonthDay | _ChronoParts::_TimeOfDay;
>        __detail::_Parser_t<_Duration> __p(__need);
>        if (__p(__is, __fmt, __abbrev, __offset))
>       {
> @@ -3827,8 +4183,8 @@ namespace __detail
>         minutes __tz_offset = __bad_min;
>         basic_string<_CharT, _Traits> __tz_abbr;
>  
> -       if ((_M_need & _ChronoParts::_TimeOfDay)
> -             && (_M_need & _ChronoParts::_Year))
> +       if ((_M_need & _ChronoParts::_TimeOfDay) != 0
> +             && (_M_need & _ChronoParts::_Year) != 0)
>           {
>             // For time_points assume "00:00:00" is implicitly present,
>             // so we don't fail to parse if it's not (PR libstdc++/114240).
> @@ -4101,7 +4457,7 @@ namespace __detail
>                       }
>                     else
>                       {
> -                       if (_M_need & _ChronoParts::_TimeOfDay)
> +                       if ((_M_need & _ChronoParts::_TimeOfDay) != 0)
>                           __err |= ios_base::failbit;
>                         break;
>                       }
> @@ -4169,7 +4525,7 @@ namespace __detail
>                       __min = minutes(__val);
>                     else
>                       {
> -                       if (_M_need & _ChronoParts::_TimeOfDay)
> +                       if ((_M_need & _ChronoParts::_TimeOfDay) != 0)
>                           __err |= ios_base::failbit;
>                         break;
>                       }
> @@ -4259,7 +4615,7 @@ namespace __detail
>                     auto __val = __read_unsigned(2);
>                     if (__val == -1 || __val > 23) [[unlikely]]
>                       {
> -                       if (_M_need & _ChronoParts::_TimeOfDay)
> +                       if ((_M_need & _ChronoParts::_TimeOfDay) != 0)
>                           __err |= ios_base::failbit;
>                         break;
>                       }
> @@ -4270,7 +4626,7 @@ namespace __detail
>                     __val = __read_unsigned(2);
>                     if (__val == -1 || __val > 60) [[unlikely]]
>                       {
> -                       if (_M_need & _ChronoParts::_TimeOfDay)
> +                       if ((_M_need & _ChronoParts::_TimeOfDay) != 0)
>                           __err |= ios_base::failbit;
>                         break;
>                       }
> @@ -4305,7 +4661,7 @@ namespace __detail
>                       __s = seconds(__val);
>                     else
>                       {
> -                       if (_M_need & _ChronoParts::_TimeOfDay)
> +                       if ((_M_need & _ChronoParts::_TimeOfDay) != 0)
>                           __err |= ios_base::failbit;
>                         break;
>                       }
> @@ -4779,23 +5135,23 @@ namespace __detail
>  
>             // Whether the caller wants _M_wd.
>             // The _Weekday bit is only set for chrono::weekday.
> -           const bool __need_wday = _M_need & _ChronoParts::_Weekday;
> +           const bool __need_wday = (_M_need & _ChronoParts::_Weekday) != 0;
>  
>             // Whether the caller wants _M_sys_days and _M_time.
>             // Only true for durations and time_points.
> -           const bool __need_time = _M_need & _ChronoParts::_TimeOfDay;
> +           const bool __need_time = (_M_need & _ChronoParts::_TimeOfDay) != 
> 0;
>  
>             if (__need_wday && __wday != __bad_wday)
>               _M_wd = __wday; // Caller only wants a weekday and we have one.
> -           else if (_M_need & _ChronoParts::_Date) // subsumes __need_wday
> +           else if ((_M_need & _ChronoParts::_Date) != 0) // subsumes 
> __need_wday
>               {
>                 // Whether the caller wants _M_ymd.
>                 // True for chrono::year etc., false for time_points.
>                 const bool __need_ymd = !__need_wday && !__need_time;
>  
> -               if ((_M_need & _ChronoParts::_Year && __y == __bad_y)
> -                  || (_M_need & _ChronoParts::_Month && __m == __bad_mon)
> -                  || (_M_need & _ChronoParts::_Day && __d == __bad_day))
> +               if (((_M_need & _ChronoParts::_Year) != 0 && __y == __bad_y)
> +                  || ((_M_need & _ChronoParts::_Month) != 0 && __m == 
> __bad_mon)
> +                  || ((_M_need & _ChronoParts::_Day) != 0 && __d == 
> __bad_day))
>                   {
>                     // Missing at least one of y/m/d so calculate sys_days
>                     // from the other data we have available.
> @@ -4872,7 +5228,7 @@ namespace __detail
>                     // but check that their values are in range.
>                     // Make unwanted fields valid so that _M_ymd.ok() is true.
>  
> -                   if (_M_need & _ChronoParts::_Year)
> +                   if ((_M_need & _ChronoParts::_Year) != 0)
>                       {
>                         if (!__y.ok()) [[unlikely]]
>                           __err |= ios_base::failbit;
> @@ -4880,7 +5236,7 @@ namespace __detail
>                     else if (__y == __bad_y)
>                       __y = 1972y; // Leap year so that Feb 29 is valid.
>  
> -                   if (_M_need & _ChronoParts::_Month)
> +                   if ((_M_need & _ChronoParts::_Month) != 0)
>                       {
>                         if (!__m.ok()) [[unlikely]]
>                           __err |= ios_base::failbit;
> @@ -4888,7 +5244,7 @@ namespace __detail
>                     else if (__m == __bad_mon)
>                       __m = January;
>  
> -                   if (_M_need & _ChronoParts::_Day)
> +                   if ((_M_need & _ChronoParts::_Day) != 0)
>                       {
>                         if (__d < day(1) || __d > (__y/__m/last).day())
>                           __err |= ios_base::failbit;
> diff --git a/libstdc++-v3/include/std/chrono b/libstdc++-v3/include/std/chrono
> index 8eb9fd9baac..cb8213e2f9f 100644
> --- a/libstdc++-v3/include/std/chrono
> +++ b/libstdc++-v3/include/std/chrono
> @@ -2305,8 +2305,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         __r *= 10;
>       return __r;
>        }
> -
> -      template<typename _Duration> struct __utc_leap_second;
>      }
>      /// @endcond
>  
> @@ -2481,30 +2479,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       __byte_duration<ratio<1>>   _M_s{};
>       bool                        _M_is_neg{};
>       __subseconds<precision>     _M_ss{};
> -
> -     template<typename> friend struct __detail::__utc_leap_second;
>        };
>  
> -    /// @cond undocumented
> -    namespace __detail
> -    {
> -      // Represents a time that is within a leap second insertion.
> -      template<typename _Duration>
> -     struct __utc_leap_second
> -     {
> -       explicit
> -       __utc_leap_second(const sys_time<_Duration>& __s)
> -       : _M_date(chrono::floor<days>(__s)), _M_time(__s - _M_date)
> -       {
> -         ++_M_time._M_s;
> -       }
> -
> -       sys_days _M_date;
> -       hh_mm_ss<common_type_t<_Duration, days>> _M_time;
> -     };
> -    }
> -    /// @endcond
> -
>      // 12/24 HOURS FUNCTIONS
>  
>      constexpr bool
> diff --git a/libstdc++-v3/testsuite/std/time/format/empty_spec.cc 
> b/libstdc++-v3/testsuite/std/time/format/empty_spec.cc
> index 5f7b8688b8a..923df3d813c 100644
> --- a/libstdc++-v3/testsuite/std/time/format/empty_spec.cc
> +++ b/libstdc++-v3/testsuite/std/time/format/empty_spec.cc
> @@ -355,30 +355,30 @@ test_hh_mm_ss_cust()
>    // +plus returns long, so formatted as long
>    const duration<Rep<long>, std::milli> asLong(dt.count());
>    verify( hms<milliseconds>(asLong),
> -       WIDEN("22:24:54.123[via format]") );
> +       WIDEN("22:24:54.123") );
>    verify( hms<deciseconds>(asLong),
> -       WIDEN("22:24:54.1[via format]") );
> +       WIDEN("22:24:54.1") );
>    verify( hms<seconds>(asLong),
>         WIDEN("22:24:54") );
>    verify( hms<milliseconds>(-asLong),
> -       WIDEN("-22:24:54.123[via format]") );
> +       WIDEN("-22:24:54.123") );
>    verify( hms<deciseconds>(-asLong),
> -       WIDEN("-22:24:54.1[via format]") );
> +       WIDEN("-22:24:54.1") );
>    verify( hms<seconds>(-asLong),
>         WIDEN("-22:24:54") );
>  
>    // +asRep returns Rep<>, so formatted as Rep<>
>    const duration<Rep<>, std::milli> asRep(dt.count());
>    verify( hms<milliseconds>(asRep),
> -       WIDEN("22:24:54.123[via format]") );
> +       WIDEN("22:24:54.123") );
>    verify( hms<deciseconds>(asRep),
> -       WIDEN("22:24:54.1[via format]") );
> +       WIDEN("22:24:54.1") );
>    verify( hms<seconds>(asLong),
>         WIDEN("22:24:54") );
>    verify( hms<milliseconds>(-asLong),
> -       WIDEN("-22:24:54.123[via format]") );
> +       WIDEN("-22:24:54.123") );
>    verify( hms<deciseconds>(-asLong),
> -       WIDEN("-22:24:54.1[via format]") );
> +       WIDEN("-22:24:54.1") );
>    verify( hms<seconds>(-asLong),
>         WIDEN("-22:24:54") );
>  }

-- 
Michael Welsh Duggan
(m...@md5i.com)

Reply via email to