1use 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::{PrivateMethod, 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 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 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 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 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 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 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#[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
265mod sealed {
267 use super::*;
268 use crate::formatting::ComponentProvider;
269 use crate::formatting::metadata::ComputeMetadata;
270
271 #[expect(
273 private_bounds,
274 private_interfaces,
275 reason = "irrelevant due to being a sealed trait"
276 )]
277 pub trait Sealed: ComputeMetadata {
278 fn format_into<V>(
280 &self,
281 output: &mut (impl io::Write + ?Sized),
282 value: &V,
283 state: &mut V::State,
284 _: PrivateMethod,
285 ) -> Result<usize, error::Format>
286 where
287 V: ComponentProvider;
288
289 #[inline]
291 fn format<V>(
292 &self,
293 value: &V,
294 state: &mut V::State,
295 _: PrivateMethod,
296 ) -> Result<String, error::Format>
297 where
298 V: ComponentProvider,
299 {
300 let crate::formatting::metadata::Metadata {
301 max_bytes_needed,
302 guaranteed_utf8,
303 } = self.compute_metadata(PrivateMethod);
304
305 let mut buf = Vec::with_capacity(max_bytes_needed);
306 try_likely_ok!(self.format_into(&mut buf, value, state, PrivateMethod));
307 Ok(if guaranteed_utf8 {
308 unsafe { String::from_utf8_unchecked(buf) }
310 } else {
311 String::from_utf8_lossy(&buf).into_owned()
312 })
313 }
314 }
315}
316
317impl sealed::Sealed for FormatDescriptionV3<'_> {
318 #[expect(
319 private_bounds,
320 private_interfaces,
321 reason = "irrelevant due to being a sealed trait"
322 )]
323 #[inline]
324 fn format_into<V>(
325 &self,
326 output: &mut (impl io::Write + ?Sized),
327 value: &V,
328 state: &mut V::State,
329 _: PrivateMethod,
330 ) -> Result<usize, error::Format>
331 where
332 V: ComponentProvider,
333 {
334 self.inner.format_into(output, value, state, PrivateMethod)
335 }
336}
337
338impl sealed::Sealed for FormatDescriptionV3Inner<'_> {
339 #[expect(
340 private_bounds,
341 private_interfaces,
342 reason = "irrelevant due to being a sealed trait"
343 )]
344 #[inline]
345 fn format_into<V>(
346 &self,
347 output: &mut (impl io::Write + ?Sized),
348 value: &V,
349 state: &mut V::State,
350 _: PrivateMethod,
351 ) -> Result<usize, error::Format>
352 where
353 V: ComponentProvider,
354 {
355 use crate::formatting::*;
356
357 fmt_component_match! { &self, output, value, state,
358 Self::BorrowedLiteral(literal) => {
359 write_bytes(output, literal.as_bytes()).map_err(Into::into)
360 }
361 Self::BorrowedCompound(items) => {
362 let mut bytes = 0;
363 for item in *items {
364 bytes += try_likely_ok!(item.format_into(output, value, state, PrivateMethod));
365 }
366 Ok(bytes)
367 }
368 Self::BorrowedOptional {
369 format: should_format,
370 item,
371 } => {
372 if *should_format {
373 item.format_into(output, value, state, PrivateMethod)
374 } else {
375 Ok(0)
376 }
377 }
378 Self::BorrowedFirst(items) => match items {
379 [] => Ok(0),
380 [item, ..] => item.format_into(output, value, state, PrivateMethod),
381 },
382 Self::OwnedLiteral(literal) => {
383 write_bytes(output, literal.as_bytes()).map_err(Into::into)
384 }
385 Self::OwnedCompound(items) => {
386 let mut bytes = 0;
387 for item in &**items {
388 bytes += try_likely_ok!(item.format_into(output, value, state, PrivateMethod));
389 }
390 Ok(bytes)
391 }
392 Self::OwnedOptional {
393 format: should_format,
394 item,
395 } => {
396 if *should_format {
397 item.format_into(output, value, state, PrivateMethod)
398 } else {
399 Ok(0)
400 }
401 }
402 Self::OwnedFirst(items) => match &items[..] {
403 [] => Ok(0),
404 [item, ..] => item.format_into(output, value, state, PrivateMethod),
405 },
406
407 #[allow(unreachable_patterns)]
412 Self::Day(_)
413 | Self::MonthShort(_)
414 | Self::MonthLong(_)
415 | Self::MonthNumerical(_)
416 | Self::Ordinal(_)
417 | Self::WeekdayShort(_)
418 | Self::WeekdayLong(_)
419 | Self::WeekdaySunday(_)
420 | Self::WeekdayMonday(_)
421 | Self::WeekNumberIso(_)
422 | Self::WeekNumberSunday(_)
423 | Self::WeekNumberMonday(_)
424 | Self::CalendarYearFullExtendedRange(_)
425 | Self::CalendarYearFullStandardRange(_)
426 | Self::IsoYearFullExtendedRange(_)
427 | Self::IsoYearFullStandardRange(_)
428 | Self::CalendarYearCenturyExtendedRange(_)
429 | Self::CalendarYearCenturyStandardRange(_)
430 | Self::IsoYearCenturyExtendedRange(_)
431 | Self::IsoYearCenturyStandardRange(_)
432 | Self::CalendarYearLastTwo(_)
433 | Self::IsoYearLastTwo(_)
434 | Self::Hour12(_)
435 | Self::Hour24(_)
436 | Self::Minute(_)
437 | Self::Period(_)
438 | Self::Second(_)
439 | Self::Subsecond(_)
440 | Self::OffsetHour(_)
441 | Self::OffsetMinute(_)
442 | Self::OffsetSecond(_)
443 | Self::Ignore(_)
444 | Self::UnixTimestampSecond(_)
445 | Self::UnixTimestampMillisecond(_)
446 | Self::UnixTimestampMicrosecond(_)
447 | Self::UnixTimestampNanosecond(_)
448 | Self::End(_) => Err(error::Format::InsufficientTypeInformation),
449 }
450 }
451}
452
453impl Component {
454 #[inline]
456 #[allow(deprecated)]
457 pub(crate) fn format_into<V>(
458 &self,
459 output: &mut (impl io::Write + ?Sized),
460 value: &V,
461 state: &mut V::State,
462 ) -> Result<usize, error::Format>
463 where
464 V: ComponentProvider,
465 {
466 use sealed::Sealed;
467
468 use crate::formatting::*;
469
470 fmt_component_match! { self, output, value, state,
471 Self::Month(_) | Self::Weekday(_) | Self::WeekNumber(_)
473 if V::SUPPLIES_DATE =>
474 {
475 FormatDescriptionV3Inner::from(*self)
476 .format_into(output, value, state, PrivateMethod)
477 }
478 Self::Hour(_) if V::SUPPLIES_TIME => {
479 FormatDescriptionV3Inner::from(*self)
480 .format_into(output, value, state, PrivateMethod)
481 }
482 Self::UnixTimestamp(_) if V::SUPPLIES_TIMESTAMP => {
483 FormatDescriptionV3Inner::from(*self)
484 .format_into(output, value, state, PrivateMethod)
485 }
486 Self::Year(_) if V::SUPPLIES_DATE => {
487 FormatDescriptionV3Inner::from(*self)
488 .format_into(output, value, state, PrivateMethod)
489 }
490
491 #[allow(unreachable_patterns)]
493 Self::Day(_)
494 | Self::MonthShort(_)
495 | Self::MonthLong(_)
496 | Self::MonthNumerical(_)
497 | Self::Ordinal(_)
498 | Self::WeekdayShort(_)
499 | Self::WeekdayLong(_)
500 | Self::WeekdaySunday(_)
501 | Self::WeekdayMonday(_)
502 | Self::WeekNumberIso(_)
503 | Self::WeekNumberSunday(_)
504 | Self::WeekNumberMonday(_)
505 | Self::CalendarYearFullExtendedRange(_)
506 | Self::CalendarYearFullStandardRange(_)
507 | Self::IsoYearFullExtendedRange(_)
508 | Self::IsoYearFullStandardRange(_)
509 | Self::CalendarYearCenturyExtendedRange(_)
510 | Self::CalendarYearCenturyStandardRange(_)
511 | Self::IsoYearCenturyExtendedRange(_)
512 | Self::IsoYearCenturyStandardRange(_)
513 | Self::CalendarYearLastTwo(_)
514 | Self::IsoYearLastTwo(_)
515 | Self::Hour12(_)
516 | Self::Hour24(_)
517 | Self::Minute(_)
518 | Self::Period(_)
519 | Self::Second(_)
520 | Self::Subsecond(_)
521 | Self::OffsetHour(_)
522 | Self::OffsetMinute(_)
523 | Self::OffsetSecond(_)
524 | Self::Ignore(_)
525 | Self::UnixTimestampSecond(_)
526 | Self::UnixTimestampMillisecond(_)
527 | Self::UnixTimestampMicrosecond(_)
528 | Self::UnixTimestampNanosecond(_)
529 | Self::Month(_)
531 | Self::Weekday(_)
532 | Self::WeekNumber(_)
533 | Self::Hour(_)
534 | Self::UnixTimestamp(_)
535 | Self::Year(_) => Err(error::Format::InsufficientTypeInformation),
536 }
537 }
538}
539
540impl sealed::Sealed for BorrowedFormatItem<'_> {
541 #[expect(
542 private_bounds,
543 private_interfaces,
544 reason = "irrelevant due to being a sealed trait"
545 )]
546 #[inline]
547 fn format_into<V>(
548 &self,
549 output: &mut (impl io::Write + ?Sized),
550 value: &V,
551 state: &mut V::State,
552 _: PrivateMethod,
553 ) -> Result<usize, error::Format>
554 where
555 V: ComponentProvider,
556 {
557 Ok(match *self {
558 #[expect(deprecated)]
559 Self::Literal(literal) => try_likely_ok!(write_bytes(output, literal)),
560 Self::StringLiteral(literal) => try_likely_ok!(write(output, literal)),
561 Self::Component(component) => component.format_into(output, value, state)?,
562 Self::Compound(items) => {
563 try_likely_ok!((*items).format_into(output, value, state, PrivateMethod))
564 }
565 Self::Optional(item) => {
566 try_likely_ok!((*item).format_into(output, value, state, PrivateMethod))
567 }
568 Self::First(items) => match items {
569 [] => 0,
570 [item, ..] => {
571 try_likely_ok!((*item).format_into(output, value, state, PrivateMethod))
572 }
573 },
574 })
575 }
576}
577
578impl sealed::Sealed for [BorrowedFormatItem<'_>] {
579 #[expect(
580 private_bounds,
581 private_interfaces,
582 reason = "irrelevant due to being a sealed trait"
583 )]
584 #[inline]
585 fn format_into<V>(
586 &self,
587 output: &mut (impl io::Write + ?Sized),
588 value: &V,
589 state: &mut V::State,
590 _: PrivateMethod,
591 ) -> Result<usize, error::Format>
592 where
593 V: ComponentProvider,
594 {
595 let mut bytes = 0;
596 for item in self.iter() {
597 bytes += try_likely_ok!(item.format_into(output, value, state, PrivateMethod));
598 }
599 Ok(bytes)
600 }
601}
602
603impl sealed::Sealed for OwnedFormatItem {
604 #[expect(
605 private_bounds,
606 private_interfaces,
607 reason = "irrelevant due to being a sealed trait"
608 )]
609 #[inline]
610 fn format_into<V>(
611 &self,
612 output: &mut (impl io::Write + ?Sized),
613 value: &V,
614 state: &mut V::State,
615 _: PrivateMethod,
616 ) -> Result<usize, error::Format>
617 where
618 V: ComponentProvider,
619 {
620 match self {
621 #[expect(deprecated)]
622 Self::Literal(literal) => Ok(try_likely_ok!(write_bytes(output, literal))),
623 Self::StringLiteral(literal) => Ok(try_likely_ok!(write(output, literal))),
624 Self::Component(component) => FormatDescriptionV3Inner::<'_>::from(*component)
625 .format_into(output, value, state, PrivateMethod),
626 Self::Compound(items) => (**items).format_into(output, value, state, PrivateMethod),
627 Self::Optional(item) => (**item).format_into(output, value, state, PrivateMethod),
628 Self::First(items) => match &**items {
629 [] => Ok(0),
630 [item, ..] => (*item).format_into(output, value, state, PrivateMethod),
631 },
632 }
633 }
634}
635
636impl sealed::Sealed for [OwnedFormatItem] {
637 #[expect(
638 private_bounds,
639 private_interfaces,
640 reason = "irrelevant due to being a sealed trait"
641 )]
642 #[inline]
643 fn format_into<V>(
644 &self,
645 output: &mut (impl io::Write + ?Sized),
646 value: &V,
647 state: &mut V::State,
648 _: PrivateMethod,
649 ) -> Result<usize, error::Format>
650 where
651 V: ComponentProvider,
652 {
653 let mut bytes = 0;
654 for item in self.iter() {
655 bytes += try_likely_ok!(item.format_into(output, value, state, PrivateMethod));
656 }
657 Ok(bytes)
658 }
659}
660
661impl<T> sealed::Sealed for T
662where
663 T: Deref<Target: sealed::Sealed>,
664{
665 #[expect(
666 private_bounds,
667 private_interfaces,
668 reason = "irrelevant due to being a sealed trait"
669 )]
670 #[inline]
671 fn format_into<V>(
672 &self,
673 output: &mut (impl io::Write + ?Sized),
674 value: &V,
675 state: &mut V::State,
676 _: PrivateMethod,
677 ) -> Result<usize, error::Format>
678 where
679 V: ComponentProvider,
680 {
681 self.deref()
682 .format_into(output, value, state, PrivateMethod)
683 }
684}
685
686#[expect(
687 private_bounds,
688 private_interfaces,
689 reason = "irrelevant due to being a sealed trait"
690)]
691impl sealed::Sealed for Rfc2822 {
692 fn format_into<V>(
693 &self,
694 output: &mut (impl io::Write + ?Sized),
695 value: &V,
696 state: &mut V::State,
697 _: PrivateMethod,
698 ) -> Result<usize, error::Format>
699 where
700 V: ComponentProvider,
701 {
702 const {
703 assert!(
704 V::SUPPLIES_DATE && V::SUPPLIES_TIME && V::SUPPLIES_OFFSET,
705 "Rfc2822 requires date, time, and offset components, but not all can be provided \
706 by this type"
707 );
708 }
709
710 let mut bytes = 0;
711
712 if value.calendar_year(state).get() < 1900
713 || (cfg!(feature = "large-dates") && value.calendar_year(state).get() >= 10_000)
715 {
716 crate::hint::cold_path();
717 return Err(error::Format::InvalidComponent("year"));
718 }
719 if value.offset_second(state).get() != 0 {
720 crate::hint::cold_path();
721 return Err(error::Format::InvalidComponent("offset_second"));
722 }
723
724 bytes += try_likely_ok!(write(output, unsafe {
726 WEEKDAY_NAMES[value
727 .weekday(state)
728 .number_days_from_monday()
729 .widen::<usize>()]
730 .get_unchecked(..3)
731 }));
732 bytes += try_likely_ok!(write(output, ", "));
733 bytes += try_likely_ok!(format_two_digits(
734 output,
735 value.day(state).expand(),
736 Padding::Zero
737 ));
738 bytes += try_likely_ok!(write(output, " "));
739 bytes += try_likely_ok!(write(output, unsafe {
741 MONTH_NAMES[u8::from(value.month(state)).widen::<usize>() - 1].get_unchecked(..3)
742 }));
743 bytes += try_likely_ok!(write(output, " "));
744 bytes += try_likely_ok!(format_four_digits_pad_zero(output, unsafe {
746 ru16::new_unchecked(value.calendar_year(state).get().cast_unsigned().truncate())
747 }));
748 bytes += try_likely_ok!(write(output, " "));
749 bytes += try_likely_ok!(format_two_digits(
750 output,
751 value.hour(state).expand(),
752 Padding::Zero
753 ));
754 bytes += try_likely_ok!(write(output, ":"));
755 bytes += try_likely_ok!(format_two_digits(
756 output,
757 value.minute(state).expand(),
758 Padding::Zero
759 ));
760 bytes += try_likely_ok!(write(output, ":"));
761 bytes += try_likely_ok!(format_two_digits(
762 output,
763 value.second(state).expand(),
764 Padding::Zero
765 ));
766 bytes += try_likely_ok!(write(output, " "));
767 bytes += try_likely_ok!(write_if_else(
768 output,
769 value.offset_is_negative(state),
770 "-",
771 "+"
772 ));
773 bytes += try_likely_ok!(format_two_digits(
774 output,
775 unsafe { ru8::new_unchecked(value.offset_hour(state).get().unsigned_abs()) },
778 Padding::Zero,
779 ));
780 bytes += try_likely_ok!(format_two_digits(
781 output,
782 unsafe { ru8::new_unchecked(value.offset_minute(state).get().unsigned_abs()) },
785 Padding::Zero,
786 ));
787
788 Ok(bytes)
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 (impl io::Write + ?Sized),
801 value: &V,
802 state: &mut V::State,
803 _: PrivateMethod,
804 ) -> Result<usize, 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 let mut bytes = 0;
818
819 if !(0..10_000).contains(&value.calendar_year(state).get()) {
820 crate::hint::cold_path();
821 return Err(error::Format::InvalidComponent("year"));
822 }
823 if offset_hour.get().unsigned_abs() > 23 {
824 crate::hint::cold_path();
825 return Err(error::Format::InvalidComponent("offset_hour"));
826 }
827 if value.offset_second(state).get() != 0 {
828 crate::hint::cold_path();
829 return Err(error::Format::InvalidComponent("offset_second"));
830 }
831
832 bytes += try_likely_ok!(format_four_digits_pad_zero(output, unsafe {
834 ru16::new_unchecked(value.calendar_year(state).get().cast_unsigned().truncate())
835 }));
836 bytes += try_likely_ok!(write(output, "-"));
837 bytes += try_likely_ok!(format_two_digits(
838 output,
839 unsafe { ru8::new_unchecked(u8::from(value.month(state))) },
841 Padding::Zero,
842 ));
843 bytes += try_likely_ok!(write(output, "-"));
844 bytes += try_likely_ok!(format_two_digits(
845 output,
846 value.day(state).expand(),
847 Padding::Zero
848 ));
849 bytes += try_likely_ok!(write(output, "T"));
850 bytes += try_likely_ok!(format_two_digits(
851 output,
852 value.hour(state).expand(),
853 Padding::Zero
854 ));
855 bytes += try_likely_ok!(write(output, ":"));
856 bytes += try_likely_ok!(format_two_digits(
857 output,
858 value.minute(state).expand(),
859 Padding::Zero
860 ));
861 bytes += try_likely_ok!(write(output, ":"));
862 bytes += try_likely_ok!(format_two_digits(
863 output,
864 value.second(state).expand(),
865 Padding::Zero
866 ));
867
868 let nanos = value.nanosecond(state);
869 if nanos.get() != 0 {
870 bytes += try_likely_ok!(write(output, "."));
871 try_likely_ok!(write(
872 output,
873 &num_fmt::truncated_subsecond_from_nanos(nanos)
874 ));
875 }
876
877 if value.offset_is_utc(state) {
878 bytes += try_likely_ok!(write(output, "Z"));
879 return Ok(bytes);
880 }
881
882 bytes += try_likely_ok!(write_if_else(
883 output,
884 value.offset_is_negative(state),
885 "-",
886 "+"
887 ));
888 bytes += try_likely_ok!(format_two_digits(
889 output,
890 unsafe { ru8::new_unchecked(offset_hour.get().unsigned_abs()) },
893 Padding::Zero,
894 ));
895 bytes += try_likely_ok!(write(output, ":"));
896 bytes += try_likely_ok!(format_two_digits(
897 output,
898 unsafe { ru8::new_unchecked(value.offset_minute(state).get().unsigned_abs()) },
901 Padding::Zero,
902 ));
903
904 Ok(bytes)
905 }
906}
907
908impl<const CONFIG: EncodedConfig> sealed::Sealed for Iso8601<CONFIG> {
909 #[expect(
910 private_bounds,
911 private_interfaces,
912 reason = "irrelevant due to being a sealed trait"
913 )]
914 #[inline]
915 fn format_into<V>(
916 &self,
917 output: &mut (impl io::Write + ?Sized),
918 value: &V,
919 state: &mut V::State,
920 _: PrivateMethod,
921 ) -> Result<usize, error::Format>
922 where
923 V: ComponentProvider,
924 {
925 let mut bytes = 0;
926
927 const {
928 assert!(
929 !Self::FORMAT_DATE || V::SUPPLIES_DATE,
930 "this Iso8601 configuration formats date components, but this type cannot provide \
931 them"
932 );
933 assert!(
934 !Self::FORMAT_TIME || V::SUPPLIES_TIME,
935 "this Iso8601 configuration formats time components, but this type cannot provide \
936 them"
937 );
938 assert!(
939 !Self::FORMAT_OFFSET || V::SUPPLIES_OFFSET,
940 "this Iso8601 configuration formats offset components, but this type cannot \
941 provide them"
942 );
943 assert!(
944 Self::FORMAT_DATE || Self::FORMAT_TIME || Self::FORMAT_OFFSET,
945 "this Iso8601 configuration does not format any components"
946 );
947 }
948
949 if Self::FORMAT_DATE {
950 bytes += try_likely_ok!(iso8601::format_date::<_, CONFIG>(output, value, state));
951 }
952 if Self::FORMAT_TIME {
953 bytes += try_likely_ok!(iso8601::format_time::<_, CONFIG>(output, value, state));
954 }
955 if Self::FORMAT_OFFSET {
956 bytes += try_likely_ok!(iso8601::format_offset::<_, CONFIG>(output, value, state));
957 }
958
959 Ok(bytes)
960 }
961}