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