Skip to main content

time/formatting/
formattable.rs

1//! A trait that can be used to format an item from its components.
2
3use alloc::string::String;
4use alloc::vec::Vec;
5use core::ops::Deref;
6use std::io;
7
8use deranged::{ru8, ru16};
9use num_conv::prelude::*;
10
11use crate::format_description::format_description_v3::FormatDescriptionV3Inner;
12use crate::format_description::modifier::Padding;
13use crate::format_description::well_known::iso8601::EncodedConfig;
14use crate::format_description::well_known::{Iso8601, Rfc2822, Rfc3339};
15use crate::format_description::{
16    BorrowedFormatItem, Component, FormatDescriptionV3, OwnedFormatItem,
17};
18use crate::formatting::{ComponentProvider, MONTH_NAMES, Output, WEEKDAY_NAMES};
19use crate::internal_macros::try_likely_ok;
20use crate::{PrivateMethod, error, num_fmt};
21
22macro_rules! fmt_component_match {
23    (
24        $self:expr,
25        $output:ident,
26        $value:ident,
27        $state:ident,
28        $($extra:tt)*
29    ) => {
30        match $self {
31            Self::Day(modifier) if V::SUPPLIES_DATE => {
32                $output.fmt_day($value.day($state), *modifier).map_err(Into::into)
33            }
34            Self::MonthShort(modifier) if V::SUPPLIES_DATE => {
35                $output.fmt_month_short($value.month($state), *modifier).map_err(Into::into)
36            }
37            Self::MonthLong(modifier) if V::SUPPLIES_DATE => {
38                $output.fmt_month_long($value.month($state), *modifier).map_err(Into::into)
39            }
40            Self::MonthNumerical(modifier) if V::SUPPLIES_DATE => {
41                $output.fmt_month_numerical($value.month($state), *modifier).map_err(Into::into)
42            }
43            Self::Ordinal(modifier) if V::SUPPLIES_DATE => {
44                $output.fmt_ordinal($value.ordinal($state), *modifier).map_err(Into::into)
45            }
46            Self::WeekdayShort(modifier) if V::SUPPLIES_DATE => {
47                $output.fmt_weekday_short($value.weekday($state), *modifier).map_err(Into::into)
48            }
49            Self::WeekdayLong(modifier) if V::SUPPLIES_DATE => {
50                $output.fmt_weekday_long($value.weekday($state), *modifier).map_err(Into::into)
51            }
52            Self::WeekdaySunday(modifier) if V::SUPPLIES_DATE => {
53                $output.fmt_weekday_sunday($value.weekday($state), *modifier).map_err(Into::into)
54            }
55            Self::WeekdayMonday(modifier) if V::SUPPLIES_DATE => {
56                $output.fmt_weekday_monday($value.weekday($state), *modifier).map_err(Into::into)
57            }
58            Self::WeekNumberIso(modifier) if V::SUPPLIES_DATE => {
59                $output.fmt_week_number_iso(
60                    $value.iso_week_number($state),
61                    *modifier,
62                )
63                .map_err(Into::into)
64            }
65            Self::WeekNumberSunday(modifier) if V::SUPPLIES_DATE => {
66                $output.fmt_week_number_sunday(
67                    $value.sunday_based_week($state),
68                    *modifier,
69                )
70                .map_err(Into::into)
71            }
72            Self::WeekNumberMonday(modifier) if V::SUPPLIES_DATE => {
73                $output.fmt_week_number_monday(
74                    $value.monday_based_week($state),
75                    *modifier,
76                )
77                .map_err(Into::into)
78            }
79            Self::CalendarYearFullExtendedRange(modifier) if V::SUPPLIES_DATE => {
80                $output.fmt_calendar_year_full_extended_range(
81                    $value.calendar_year($state),
82                    *modifier
83                ).map_err(Into::into)
84            }
85            Self::CalendarYearFullStandardRange(modifier) if V::SUPPLIES_DATE => {
86                $output.fmt_calendar_year_full_standard_range(
87                    try_likely_ok!(
88                        $value
89                            .calendar_year($state)
90                            .narrow::<-9_999, 9_999>()
91                            .ok_or_else(|| error::ComponentRange::conditional("year"))
92                    )
93                    .into(),
94                    *modifier,
95                )
96                .map_err(Into::into)
97            }
98            Self::IsoYearFullExtendedRange(modifier) if V::SUPPLIES_DATE => {
99                $output.fmt_iso_year_full_extended_range(
100                    $value.iso_year($state),
101                    *modifier,
102                )
103                .map_err(Into::into)
104            }
105            Self::IsoYearFullStandardRange(modifier) if V::SUPPLIES_DATE => {
106                $output.fmt_iso_year_full_standard_range(
107                    try_likely_ok!(
108                        $value
109                            .iso_year($state)
110                            .narrow::<-9_999, 9_999>()
111                            .ok_or_else(|| error::ComponentRange::conditional("year"))
112                    )
113                    .into(),
114                    *modifier,
115                )
116                .map_err(Into::into)
117            }
118            Self::CalendarYearCenturyExtendedRange(modifier) if V::SUPPLIES_DATE => {
119                let year = $value.calendar_year($state);
120                // Safety: Given the range of `year`, the range of the century is `-9_999..=9_999`.
121                let century = unsafe { ri16::new_unchecked((year.get() / 100).truncate()) };
122                $output.fmt_calendar_year_century_extended_range(
123                    century,
124                    year.is_negative(),
125                    *modifier,
126                )
127                .map_err(Into::into)
128            }
129            Self::CalendarYearCenturyStandardRange(modifier) if V::SUPPLIES_DATE => {
130                let year = $value.calendar_year($state);
131                let is_negative = year.is_negative();
132                // Safety: Given the range of `year`, the range of the century is `-9_999..=9_999`.
133                let year = unsafe {
134                    ri16::<-9_999, 9_999>::new_unchecked((year.get() / 100).truncate())
135                };
136                $output.fmt_calendar_year_century_standard_range(
137                    year.narrow::<-99, 99>()
138                        .ok_or_else(|| error::ComponentRange::conditional("year"))?
139                        .into(),
140                    is_negative,
141                    *modifier,
142                )
143                .map_err(Into::into)
144            }
145            Self::IsoYearCenturyExtendedRange(modifier) if V::SUPPLIES_DATE => {
146                let year = $value.iso_year($state);
147                let is_negative = year.is_negative();
148                // Safety: Given the range of `year`, the range of the century is `-9_999..=9_999`.
149                let century = unsafe { ri16::new_unchecked((year.get() / 100).truncate()) };
150                $output.fmt_iso_year_century_extended_range(
151                    century,
152                    is_negative,
153                    *modifier,
154                )
155                .map_err(Into::into)
156            }
157            Self::IsoYearCenturyStandardRange(modifier) if V::SUPPLIES_DATE => {
158                let year = $value.iso_year($state);
159                let is_negative = year.is_negative();
160                // Safety: Given the range of `year`, the range of the century is `-9_999..=9_999`.
161                let year = unsafe {
162                    ri16::<-9_999, 9_999>::new_unchecked((year.get() / 100).truncate())
163                };
164                $output.fmt_iso_year_century_standard_range(
165                    year.narrow::<-99, 99>()
166                        .ok_or_else(|| error::ComponentRange::conditional("year"))?
167                        .into(),
168                    is_negative,
169                    *modifier,
170                )
171                .map_err(Into::into)
172            }
173            Self::CalendarYearLastTwo(modifier) if V::SUPPLIES_DATE => {
174                // Safety: Modulus of 100 followed by `.unsigned_abs()` guarantees that the $value
175                // is in the range `0..=99`.
176                let last_two = unsafe {
177                    ru8::new_unchecked(
178                        ($value.calendar_year($state).get().unsigned_abs() % 100).truncate(),
179                    )
180                };
181                $output.fmt_calendar_year_last_two(last_two, *modifier)
182                    .map_err(Into::into)
183            }
184            Self::IsoYearLastTwo(modifier) if V::SUPPLIES_DATE => {
185                // Safety: Modulus of 100 followed by `.unsigned_abs()` guarantees that the $value
186                // is in the range `0..=99`.
187                let last_two = unsafe {
188                    ru8::new_unchecked(
189                        ($value.iso_year($state).get().unsigned_abs() % 100).truncate(),
190                    )
191                };
192                $output.fmt_iso_year_last_two(last_two, *modifier).map_err(Into::into)
193            }
194            Self::Hour12(modifier) if V::SUPPLIES_TIME => {
195                $output.fmt_hour_12($value.hour($state), *modifier).map_err(Into::into)
196            }
197            Self::Hour24(modifier) if V::SUPPLIES_TIME => {
198                $output.fmt_hour_24($value.hour($state), *modifier).map_err(Into::into)
199            }
200            Self::Minute(modifier) if V::SUPPLIES_TIME => {
201                $output.fmt_minute($value.minute($state), *modifier).map_err(Into::into)
202            }
203            Self::Period(modifier) if V::SUPPLIES_TIME => {
204                $output.fmt_period($value.period($state), *modifier).map_err(Into::into)
205            }
206            Self::Second(modifier) if V::SUPPLIES_TIME => {
207                $output.fmt_second($value.second($state), *modifier).map_err(Into::into)
208            }
209            Self::Subsecond(modifier) if V::SUPPLIES_TIME => {
210                $output.fmt_subsecond($value.nanosecond($state), *modifier).map_err(Into::into)
211            }
212            Self::OffsetHour(modifier) if V::SUPPLIES_OFFSET => {
213                $output.fmt_offset_hour(
214                    $value.offset_is_negative($state),
215                    $value.offset_hour($state),
216                    *modifier,
217                )
218                .map_err(Into::into)
219            }
220            Self::OffsetMinute(modifier) if V::SUPPLIES_OFFSET => {
221                $output.fmt_offset_minute($value.offset_minute($state), *modifier)
222                    .map_err(Into::into)
223            }
224            Self::OffsetSecond(modifier) if V::SUPPLIES_OFFSET => {
225                $output.fmt_offset_second($value.offset_second($state), *modifier)
226                    .map_err(Into::into)
227            }
228            Self::Ignore(_) => Ok(()),
229            Self::UnixTimestampSecond(modifier) if V::SUPPLIES_TIMESTAMP => {
230                $output.fmt_unix_timestamp_second(
231                    $value.unix_timestamp_seconds($state),
232                    *modifier,
233                )
234                .map_err(Into::into)
235            }
236            Self::UnixTimestampMillisecond(modifier) if V::SUPPLIES_TIMESTAMP => {
237                $output.fmt_unix_timestamp_millisecond(
238                    $value.unix_timestamp_milliseconds($state),
239                    *modifier,
240                )
241                .map_err(Into::into)
242            }
243            Self::UnixTimestampMicrosecond(modifier) if V::SUPPLIES_TIMESTAMP => {
244                $output.fmt_unix_timestamp_microsecond(
245                    $value.unix_timestamp_microseconds($state),
246                    *modifier,
247                )
248                .map_err(Into::into)
249            }
250            Self::UnixTimestampNanosecond(modifier) if V::SUPPLIES_TIMESTAMP => {
251                $output.fmt_unix_timestamp_nanosecond(
252                    $value.unix_timestamp_nanoseconds($state),
253                    *modifier,
254                )
255                .map_err(Into::into)
256            }
257            Self::End(modifier::End { trailing_input: _ }) => Ok(()),
258            $($extra)*
259        }
260    };
261}
262
263/// A type that describes a format.
264///
265/// Implementors of [`Formattable`] are [format descriptions](crate::format_description).
266///
267/// To format a value into a String, use the `format` method on the respective type.
268#[cfg_attr(docsrs, doc(notable_trait))]
269pub trait Formattable: sealed::Sealed {}
270impl Formattable for FormatDescriptionV3<'_> {}
271impl Formattable for BorrowedFormatItem<'_> {}
272impl Formattable for [BorrowedFormatItem<'_>] {}
273impl Formattable for OwnedFormatItem {}
274impl Formattable for [OwnedFormatItem] {}
275impl Formattable for Rfc3339 {}
276impl Formattable for Rfc2822 {}
277impl<const CONFIG: EncodedConfig> Formattable for Iso8601<CONFIG> {}
278impl<T> Formattable for T where T: Deref<Target: Formattable> {}
279
280/// Seal the trait to prevent downstream users from implementing it.
281mod sealed {
282    use super::*;
283    use crate::formatting::ComponentProvider;
284    use crate::formatting::metadata::ComputeMetadata;
285
286    /// Format the item using a format description, the intended output, and the various components.
287    #[expect(
288        private_bounds,
289        private_interfaces,
290        reason = "irrelevant due to being a sealed trait"
291    )]
292    pub trait Sealed: ComputeMetadata {
293        /// Format the item into the provided output.
294        fn format_into<V>(
295            &self,
296            output: &mut Output<impl io::Write + ?Sized>,
297            value: &V,
298            state: &mut V::State,
299            _: PrivateMethod,
300        ) -> Result<(), error::Format>
301        where
302            V: ComponentProvider;
303
304        /// Format the item directly to a `String`.
305        #[inline]
306        fn format<V>(
307            &self,
308            value: &V,
309            state: &mut V::State,
310            _: PrivateMethod,
311        ) -> Result<String, error::Format>
312        where
313            V: ComponentProvider,
314        {
315            let crate::formatting::metadata::Metadata {
316                max_bytes_needed,
317                guaranteed_utf8,
318            } = self.compute_metadata(PrivateMethod);
319
320            let mut output = Output {
321                bytes_written: 0,
322                output: Vec::with_capacity(max_bytes_needed),
323            };
324            try_likely_ok!(self.format_into(&mut output, value, state, PrivateMethod));
325            Ok(if guaranteed_utf8 {
326                // Safety: The output is guaranteed to be UTF-8.
327                unsafe { String::from_utf8_unchecked(output.output) }
328            } else {
329                String::from_utf8_lossy(&output.output).into_owned()
330            })
331        }
332    }
333}
334
335impl sealed::Sealed for FormatDescriptionV3<'_> {
336    #[expect(
337        private_bounds,
338        private_interfaces,
339        reason = "irrelevant due to being a sealed trait"
340    )]
341    #[inline]
342    fn format_into<V>(
343        &self,
344        output: &mut Output<impl io::Write + ?Sized>,
345        value: &V,
346        state: &mut V::State,
347        _: PrivateMethod,
348    ) -> Result<(), error::Format>
349    where
350        V: ComponentProvider,
351    {
352        self.inner.format_into(output, value, state, PrivateMethod)
353    }
354}
355
356impl sealed::Sealed for FormatDescriptionV3Inner<'_> {
357    #[expect(
358        private_bounds,
359        private_interfaces,
360        reason = "irrelevant due to being a sealed trait"
361    )]
362    #[inline]
363    fn format_into<V>(
364        &self,
365        output: &mut Output<impl io::Write + ?Sized>,
366        value: &V,
367        state: &mut V::State,
368        _: PrivateMethod,
369    ) -> Result<(), error::Format>
370    where
371        V: ComponentProvider,
372    {
373        use crate::formatting::*;
374
375        fmt_component_match! { &self, output, value, state,
376            Self::BorrowedLiteral(literal) => output.write(literal).map_err(Into::into),
377            Self::BorrowedCompound(items) => {
378                for item in *items {
379                    try_likely_ok!(item.format_into(
380                        output,
381                        value,
382                        state,
383                        PrivateMethod,
384                    ));
385                }
386                Ok(())
387            }
388            Self::BorrowedOptional {
389                format: should_format,
390                item,
391            } => {
392                if *should_format {
393                    item.format_into(output, value, state, PrivateMethod)
394                } else {
395                    Ok(())
396                }
397            }
398            Self::BorrowedFirst(items) => match items {
399                [] => Ok(()),
400                [item, ..] => {
401                    item.format_into(output, value, state, PrivateMethod)
402                }
403            },
404            Self::OwnedLiteral(literal) => output.write(literal).map_err(Into::into),
405            Self::OwnedCompound(items) => {
406                for item in &**items {
407                    try_likely_ok!(item.format_into(
408                        output,
409                        value,
410                        state,
411                        PrivateMethod,
412                    ));
413                }
414                Ok(())
415            }
416            Self::OwnedOptional {
417                format: should_format,
418                item,
419            } => {
420                if *should_format {
421                    item.format_into(output, value, state, PrivateMethod)
422                } else {
423                    Ok(())
424                }
425            }
426            Self::OwnedFirst(items) => match &items[..] {
427                [] => Ok(()),
428                [item, ..] => {
429                    item.format_into(output, value, state, PrivateMethod)
430                }
431            },
432
433            // This is functionally the same as a wildcard arm, but it will cause an error
434            // if a new component is added. This is to avoid a bug where
435            // a new component, the code compiles, and formatting fails.
436            // Allow unreachable patterns because some branches may be fully matched above.
437            #[allow(unreachable_patterns)]
438            Self::Day(_)
439            | Self::MonthShort(_)
440            | Self::MonthLong(_)
441            | Self::MonthNumerical(_)
442            | Self::Ordinal(_)
443            | Self::WeekdayShort(_)
444            | Self::WeekdayLong(_)
445            | Self::WeekdaySunday(_)
446            | Self::WeekdayMonday(_)
447            | Self::WeekNumberIso(_)
448            | Self::WeekNumberSunday(_)
449            | Self::WeekNumberMonday(_)
450            | Self::CalendarYearFullExtendedRange(_)
451            | Self::CalendarYearFullStandardRange(_)
452            | Self::IsoYearFullExtendedRange(_)
453            | Self::IsoYearFullStandardRange(_)
454            | Self::CalendarYearCenturyExtendedRange(_)
455            | Self::CalendarYearCenturyStandardRange(_)
456            | Self::IsoYearCenturyExtendedRange(_)
457            | Self::IsoYearCenturyStandardRange(_)
458            | Self::CalendarYearLastTwo(_)
459            | Self::IsoYearLastTwo(_)
460            | Self::Hour12(_)
461            | Self::Hour24(_)
462            | Self::Minute(_)
463            | Self::Period(_)
464            | Self::Second(_)
465            | Self::Subsecond(_)
466            | Self::OffsetHour(_)
467            | Self::OffsetMinute(_)
468            | Self::OffsetSecond(_)
469            | Self::Ignore(_)
470            | Self::UnixTimestampSecond(_)
471            | Self::UnixTimestampMillisecond(_)
472            | Self::UnixTimestampMicrosecond(_)
473            | Self::UnixTimestampNanosecond(_)
474            | Self::End(_) => Err(error::Format::InsufficientTypeInformation),
475        }
476    }
477}
478
479impl Component {
480    /// Format the component directly into the provided output.
481    #[inline]
482    #[allow(deprecated)]
483    pub(crate) fn format_into<V>(
484        &self,
485        output: &mut Output<impl io::Write + ?Sized>,
486        value: &V,
487        state: &mut V::State,
488    ) -> Result<(), error::Format>
489    where
490        V: ComponentProvider,
491    {
492        use sealed::Sealed;
493
494        use crate::formatting::*;
495
496        fmt_component_match! { self, output, value, state,
497            // Deprecated component variants: delegate through the existing conversion.
498            Self::Month(_) | Self::Weekday(_) | Self::WeekNumber(_)
499                if V::SUPPLIES_DATE =>
500            {
501                FormatDescriptionV3Inner::from(*self)
502                    .format_into(output, value, state, PrivateMethod)
503            }
504            Self::Hour(_) if V::SUPPLIES_TIME => {
505                FormatDescriptionV3Inner::from(*self)
506                    .format_into(output, value, state, PrivateMethod)
507            }
508            Self::UnixTimestamp(_) if V::SUPPLIES_TIMESTAMP => {
509                FormatDescriptionV3Inner::from(*self)
510                    .format_into(output, value, state, PrivateMethod)
511            }
512            Self::Year(_) if V::SUPPLIES_DATE => {
513                FormatDescriptionV3Inner::from(*self)
514                    .format_into(output, value, state, PrivateMethod)
515            }
516
517            // Unmatched component (e.g. time component on a date-only type).
518            #[allow(unreachable_patterns)]
519            Self::Day(_)
520            | Self::MonthShort(_)
521            | Self::MonthLong(_)
522            | Self::MonthNumerical(_)
523            | Self::Ordinal(_)
524            | Self::WeekdayShort(_)
525            | Self::WeekdayLong(_)
526            | Self::WeekdaySunday(_)
527            | Self::WeekdayMonday(_)
528            | Self::WeekNumberIso(_)
529            | Self::WeekNumberSunday(_)
530            | Self::WeekNumberMonday(_)
531            | Self::CalendarYearFullExtendedRange(_)
532            | Self::CalendarYearFullStandardRange(_)
533            | Self::IsoYearFullExtendedRange(_)
534            | Self::IsoYearFullStandardRange(_)
535            | Self::CalendarYearCenturyExtendedRange(_)
536            | Self::CalendarYearCenturyStandardRange(_)
537            | Self::IsoYearCenturyExtendedRange(_)
538            | Self::IsoYearCenturyStandardRange(_)
539            | Self::CalendarYearLastTwo(_)
540            | Self::IsoYearLastTwo(_)
541            | Self::Hour12(_)
542            | Self::Hour24(_)
543            | Self::Minute(_)
544            | Self::Period(_)
545            | Self::Second(_)
546            | Self::Subsecond(_)
547            | Self::OffsetHour(_)
548            | Self::OffsetMinute(_)
549            | Self::OffsetSecond(_)
550            | Self::Ignore(_)
551            | Self::UnixTimestampSecond(_)
552            | Self::UnixTimestampMillisecond(_)
553            | Self::UnixTimestampMicrosecond(_)
554            | Self::UnixTimestampNanosecond(_)
555            // Deprecated variants not matched by guarded arms above.
556            | Self::Month(_)
557            | Self::Weekday(_)
558            | Self::WeekNumber(_)
559            | Self::Hour(_)
560            | Self::UnixTimestamp(_)
561            | Self::Year(_) => Err(error::Format::InsufficientTypeInformation),
562        }
563    }
564}
565
566impl sealed::Sealed for BorrowedFormatItem<'_> {
567    #[expect(
568        private_bounds,
569        private_interfaces,
570        reason = "irrelevant due to being a sealed trait"
571    )]
572    #[inline]
573    fn format_into<V>(
574        &self,
575        output: &mut Output<impl io::Write + ?Sized>,
576        value: &V,
577        state: &mut V::State,
578        _: PrivateMethod,
579    ) -> Result<(), error::Format>
580    where
581        V: ComponentProvider,
582    {
583        match *self {
584            #[expect(deprecated)]
585            Self::Literal(literal) => try_likely_ok!(output.write_bytes(literal)),
586            Self::StringLiteral(literal) => try_likely_ok!(output.write(literal)),
587            Self::Component(component) => component.format_into(output, value, state)?,
588            Self::Compound(items) => {
589                try_likely_ok!((*items).format_into(output, value, state, PrivateMethod));
590            }
591            Self::Optional(item) => {
592                try_likely_ok!((*item).format_into(output, value, state, PrivateMethod));
593            }
594            Self::First(items) => match items {
595                [] => {}
596                [item, ..] => {
597                    try_likely_ok!((*item).format_into(output, value, state, PrivateMethod));
598                }
599            },
600        }
601        Ok(())
602    }
603}
604
605impl sealed::Sealed for [BorrowedFormatItem<'_>] {
606    #[expect(
607        private_bounds,
608        private_interfaces,
609        reason = "irrelevant due to being a sealed trait"
610    )]
611    #[inline]
612    fn format_into<V>(
613        &self,
614        output: &mut Output<impl io::Write + ?Sized>,
615        value: &V,
616        state: &mut V::State,
617        _: PrivateMethod,
618    ) -> Result<(), error::Format>
619    where
620        V: ComponentProvider,
621    {
622        for item in self.iter() {
623            try_likely_ok!(item.format_into(output, value, state, PrivateMethod));
624        }
625        Ok(())
626    }
627}
628
629impl sealed::Sealed for OwnedFormatItem {
630    #[expect(
631        private_bounds,
632        private_interfaces,
633        reason = "irrelevant due to being a sealed trait"
634    )]
635    #[inline]
636    fn format_into<V>(
637        &self,
638        output: &mut Output<impl io::Write + ?Sized>,
639        value: &V,
640        state: &mut V::State,
641        _: PrivateMethod,
642    ) -> Result<(), error::Format>
643    where
644        V: ComponentProvider,
645    {
646        match self {
647            #[expect(deprecated)]
648            Self::Literal(literal) => output.write_bytes(literal).map_err(Into::into),
649            Self::StringLiteral(literal) => output.write(literal).map_err(Into::into),
650            Self::Component(component) => FormatDescriptionV3Inner::<'_>::from(*component)
651                .format_into(output, value, state, PrivateMethod),
652            Self::Compound(items) => (**items).format_into(output, value, state, PrivateMethod),
653            Self::Optional(item) => (**item).format_into(output, value, state, PrivateMethod),
654            Self::First(items) => match &**items {
655                [] => Ok(()),
656                [item, ..] => (*item).format_into(output, value, state, PrivateMethod),
657            },
658        }
659    }
660}
661
662impl sealed::Sealed for [OwnedFormatItem] {
663    #[expect(
664        private_bounds,
665        private_interfaces,
666        reason = "irrelevant due to being a sealed trait"
667    )]
668    #[inline]
669    fn format_into<V>(
670        &self,
671        output: &mut Output<impl io::Write + ?Sized>,
672        value: &V,
673        state: &mut V::State,
674        _: PrivateMethod,
675    ) -> Result<(), error::Format>
676    where
677        V: ComponentProvider,
678    {
679        for item in self.iter() {
680            try_likely_ok!(item.format_into(output, value, state, PrivateMethod));
681        }
682        Ok(())
683    }
684}
685
686impl<T> sealed::Sealed for T
687where
688    T: Deref<Target: sealed::Sealed>,
689{
690    #[expect(
691        private_bounds,
692        private_interfaces,
693        reason = "irrelevant due to being a sealed trait"
694    )]
695    #[inline]
696    fn format_into<V>(
697        &self,
698        output: &mut Output<impl io::Write + ?Sized>,
699        value: &V,
700        state: &mut V::State,
701        _: PrivateMethod,
702    ) -> Result<(), error::Format>
703    where
704        V: ComponentProvider,
705    {
706        self.deref()
707            .format_into(output, value, state, PrivateMethod)
708    }
709}
710
711#[expect(
712    private_bounds,
713    private_interfaces,
714    reason = "irrelevant due to being a sealed trait"
715)]
716impl sealed::Sealed for Rfc2822 {
717    fn format_into<V>(
718        &self,
719        output: &mut Output<impl io::Write + ?Sized>,
720        value: &V,
721        state: &mut V::State,
722        _: PrivateMethod,
723    ) -> Result<(), error::Format>
724    where
725        V: ComponentProvider,
726    {
727        const {
728            assert!(
729                V::SUPPLIES_DATE && V::SUPPLIES_TIME && V::SUPPLIES_OFFSET,
730                "Rfc2822 requires date, time, and offset components, but not all can be provided \
731                 by this type"
732            );
733        }
734
735        if value.calendar_year(state).get() < 1900
736            // The RFC requires years be exactly four digits.
737            || (cfg!(feature = "large-dates") && value.calendar_year(state).get() >= 10_000)
738        {
739            crate::hint::cold_path();
740            return Err(error::Format::InvalidComponent("year"));
741        }
742        if value.offset_second(state).get() != 0 {
743            crate::hint::cold_path();
744            return Err(error::Format::InvalidComponent("offset_second"));
745        }
746
747        // Safety: All weekday names are at least 3 bytes long.
748        try_likely_ok!(output.write(unsafe {
749            WEEKDAY_NAMES[value
750                .weekday(state)
751                .number_days_from_monday()
752                .widen::<usize>()]
753            .get_unchecked(..3)
754        }));
755        try_likely_ok!(output.write(", "));
756        try_likely_ok!(output.format_two_digits(value.day(state).expand(), Padding::Zero));
757        try_likely_ok!(output.write(" "));
758        // Safety: All month names are at least 3 bytes long.
759        try_likely_ok!(output.write(unsafe {
760            MONTH_NAMES[u8::from(value.month(state)).widen::<usize>() - 1].get_unchecked(..3)
761        }));
762        try_likely_ok!(output.write(" "));
763        // Safety: Years with five or more digits were rejected above. Likewise with negative years.
764        try_likely_ok!(output.format_four_digits_pad_zero(unsafe {
765            ru16::new_unchecked(value.calendar_year(state).get().cast_unsigned().truncate())
766        }));
767        try_likely_ok!(output.write(" "));
768        try_likely_ok!(output.format_two_digits(value.hour(state).expand(), Padding::Zero));
769        try_likely_ok!(output.write(":"));
770        try_likely_ok!(output.format_two_digits(value.minute(state).expand(), Padding::Zero));
771        try_likely_ok!(output.write(":"));
772        try_likely_ok!(output.format_two_digits(value.second(state).expand(), Padding::Zero));
773        try_likely_ok!(output.write(" "));
774        try_likely_ok!(output.write_if_else(value.offset_is_negative(state), "-", "+"));
775        try_likely_ok!(output.format_two_digits(
776            // Safety: `OffsetHours` is guaranteed to be in the range `-25..=25`, so the absolute
777            // value is guaranteed to be in the range `0..=25`.
778            unsafe { ru8::new_unchecked(value.offset_hour(state).get().unsigned_abs()) },
779            Padding::Zero,
780        ));
781        try_likely_ok!(output.format_two_digits(
782            // Safety: `OffsetMinutes` is guaranteed to be in the range `-59..=59`, so the absolute
783            // value is guaranteed to be in the range `0..=59`.
784            unsafe { ru8::new_unchecked(value.offset_minute(state).get().unsigned_abs()) },
785            Padding::Zero,
786        ));
787
788        Ok(())
789    }
790}
791
792#[expect(
793    private_bounds,
794    private_interfaces,
795    reason = "irrelevant due to being a sealed trait"
796)]
797impl sealed::Sealed for Rfc3339 {
798    fn format_into<V>(
799        &self,
800        output: &mut Output<impl io::Write + ?Sized>,
801        value: &V,
802        state: &mut V::State,
803        _: PrivateMethod,
804    ) -> Result<(), error::Format>
805    where
806        V: ComponentProvider,
807    {
808        const {
809            assert!(
810                V::SUPPLIES_DATE && V::SUPPLIES_TIME && V::SUPPLIES_OFFSET,
811                "Rfc3339 requires date, time, and offset components, but not all can be provided \
812                 by this type"
813            );
814        }
815
816        let offset_hour = value.offset_hour(state);
817
818        if !(0..10_000).contains(&value.calendar_year(state).get()) {
819            crate::hint::cold_path();
820            return Err(error::Format::InvalidComponent("year"));
821        }
822        if offset_hour.get().unsigned_abs() > 23 {
823            crate::hint::cold_path();
824            return Err(error::Format::InvalidComponent("offset_hour"));
825        }
826        if value.offset_second(state).get() != 0 {
827            crate::hint::cold_path();
828            return Err(error::Format::InvalidComponent("offset_second"));
829        }
830
831        // Safety: Years outside this range were rejected above.
832        try_likely_ok!(output.format_four_digits_pad_zero(unsafe {
833            ru16::new_unchecked(value.calendar_year(state).get().cast_unsigned().truncate())
834        }));
835        try_likely_ok!(output.write("-"));
836        try_likely_ok!(output.format_two_digits(
837            // Safety: `month` is guaranteed to be in the range `1..=12`.
838            unsafe { ru8::new_unchecked(u8::from(value.month(state))) },
839            Padding::Zero,
840        ));
841        try_likely_ok!(output.write("-"));
842        try_likely_ok!(output.format_two_digits(value.day(state).expand(), Padding::Zero));
843        try_likely_ok!(output.write("T"));
844        try_likely_ok!(output.format_two_digits(value.hour(state).expand(), Padding::Zero));
845        try_likely_ok!(output.write(":"));
846        try_likely_ok!(output.format_two_digits(value.minute(state).expand(), Padding::Zero));
847        try_likely_ok!(output.write(":"));
848        try_likely_ok!(output.format_two_digits(value.second(state).expand(), Padding::Zero));
849
850        let nanos = value.nanosecond(state);
851        if nanos.get() != 0 {
852            try_likely_ok!(output.write("."));
853            try_likely_ok!(output.write(&num_fmt::truncated_subsecond_from_nanos(nanos)));
854        }
855
856        if value.offset_is_utc(state) {
857            try_likely_ok!(output.write("Z"));
858            return Ok(());
859        }
860
861        try_likely_ok!(output.write_if_else(value.offset_is_negative(state), "-", "+"));
862        try_likely_ok!(output.format_two_digits(
863            // Safety: `OffsetHours` is guaranteed to be in the range `-23..=23`, so the absolute
864            // value is guaranteed to be in the range `0..=23`.
865            unsafe { ru8::new_unchecked(offset_hour.get().unsigned_abs()) },
866            Padding::Zero,
867        ));
868        try_likely_ok!(output.write(":"));
869        try_likely_ok!(output.format_two_digits(
870            // Safety: `OffsetMinutes` is guaranteed to be in the range `-59..=59`, so the absolute
871            // value is guaranteed to be in the range `0..=59`.
872            unsafe { ru8::new_unchecked(value.offset_minute(state).get().unsigned_abs()) },
873            Padding::Zero,
874        ));
875
876        Ok(())
877    }
878}
879
880impl<const CONFIG: EncodedConfig> sealed::Sealed for Iso8601<CONFIG> {
881    #[expect(
882        private_bounds,
883        private_interfaces,
884        reason = "irrelevant due to being a sealed trait"
885    )]
886    #[inline]
887    fn format_into<V>(
888        &self,
889        output: &mut Output<impl io::Write + ?Sized>,
890        value: &V,
891        state: &mut V::State,
892        _: PrivateMethod,
893    ) -> Result<(), error::Format>
894    where
895        V: ComponentProvider,
896    {
897        const {
898            assert!(
899                !Self::FORMAT_DATE || V::SUPPLIES_DATE,
900                "this Iso8601 configuration formats date components, but this type cannot provide \
901                 them"
902            );
903            assert!(
904                !Self::FORMAT_TIME || V::SUPPLIES_TIME,
905                "this Iso8601 configuration formats time components, but this type cannot provide \
906                 them"
907            );
908            assert!(
909                !Self::FORMAT_OFFSET || V::SUPPLIES_OFFSET,
910                "this Iso8601 configuration formats offset components, but this type cannot \
911                 provide them"
912            );
913            assert!(
914                Self::FORMAT_DATE || Self::FORMAT_TIME || Self::FORMAT_OFFSET,
915                "this Iso8601 configuration does not format any components"
916            );
917        }
918
919        if Self::FORMAT_DATE {
920            output.format_iso8601_date::<_, CONFIG>(value, state)?;
921        }
922        if Self::FORMAT_TIME {
923            output.format_iso8601_time::<_, CONFIG>(value, state)?;
924        }
925        if Self::FORMAT_OFFSET {
926            output.format_iso8601_offset::<_, CONFIG>(value, state)?;
927        }
928
929        Ok(())
930    }
931}