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::{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 ) -> Result<usize, error::Format>
285 where
286 V: ComponentProvider;
287
288 #[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 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 #[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 #[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 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 #[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 | 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 || (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 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 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 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 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 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 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 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 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 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}