1mod component_provider;
4pub(crate) mod formattable;
5mod iso8601;
6mod metadata;
7
8use core::mem::MaybeUninit;
9use core::num::NonZero;
10use std::io;
11
12use deranged::{Option_ri32, Option_ru8, ri8, ri16, ri32, ru8, ru16, ru32};
13use num_conv::prelude::*;
14
15use self::component_provider::ComponentProvider;
16pub use self::formattable::Formattable;
17use crate::format_description::{Component, Period, modifier};
18use crate::internal_macros::try_likely_ok;
19use crate::time::{Hours, Minutes, Nanoseconds, Seconds};
20use crate::utc_offset::{Hours as OffsetHours, Minutes as OffsetMinutes, Seconds as OffsetSeconds};
21use crate::{Month, Weekday, error, num_fmt};
22
23type Day = ru8<1, 31>;
24type OptionDay = Option_ru8<1, 31>;
25type Ordinal = ru16<1, 366>;
26type IsoWeekNumber = ru8<1, 53>;
27type OptionIsoWeekNumber = Option_ru8<1, 53>;
28type MondayBasedWeek = ru8<0, 53>;
29type SundayBasedWeek = ru8<0, 53>;
30type Year = ri32<-999_999, 999_999>;
31type StandardYear = ri16<-9_999, 9_999>;
32type OptionYear = Option_ri32<-999_999, 999_999>;
33type ExtendedCentury = ri16<-9_999, 9_999>;
34type StandardCentury = ri8<-99, 99>;
35type LastTwo = ru8<0, 99>;
36type AnyWeekNumber = ru8<0, 53>;
37
38const MONTH_NAMES: [&str; 12] = [
39 "January",
40 "February",
41 "March",
42 "April",
43 "May",
44 "June",
45 "July",
46 "August",
47 "September",
48 "October",
49 "November",
50 "December",
51];
52
53const WEEKDAY_NAMES: [&str; 7] = [
54 "Monday",
55 "Tuesday",
56 "Wednesday",
57 "Thursday",
58 "Friday",
59 "Saturday",
60 "Sunday",
61];
62
63#[inline]
65pub(crate) fn write_bytes(
66 output: &mut (impl io::Write + ?Sized),
67 bytes: &[u8],
68) -> io::Result<usize> {
69 try_likely_ok!(output.write_all(bytes));
70 Ok(bytes.len())
71}
72
73#[inline]
75pub(crate) fn write(output: &mut (impl io::Write + ?Sized), s: &str) -> io::Result<usize> {
76 try_likely_ok!(output.write_all(s.as_bytes()));
77 Ok(s.len())
78}
79
80#[inline]
82pub(crate) fn write_many<const N: usize>(
83 output: &mut (impl io::Write + ?Sized),
84 arr: [&str; N],
85) -> io::Result<usize> {
86 let mut bytes = 0;
87 for s in arr {
88 try_likely_ok!(output.write_all(s.as_bytes()));
89 bytes += s.len();
90 }
91 Ok(bytes)
92}
93
94#[inline]
96pub(crate) fn write_if(
97 output: &mut (impl io::Write + ?Sized),
98 pred: bool,
99 s: &str,
100) -> io::Result<usize> {
101 if pred { write(output, s) } else { Ok(0) }
102}
103
104#[inline]
106pub(crate) fn write_if_else(
107 output: &mut (impl io::Write + ?Sized),
108 pred: bool,
109 true_str: &str,
110 false_str: &str,
111) -> io::Result<usize> {
112 write(output, if pred { true_str } else { false_str })
113}
114
115#[inline]
120fn f64_10_pow_x(x: NonZero<u8>) -> f64 {
121 match x.get() {
122 1 => 10.,
123 2 => 100.,
124 3 => 1_000.,
125 4 => 10_000.,
126 5 => 100_000.,
127 6 => 1_000_000.,
128 7 => 10_000_000.,
129 8 => 100_000_000.,
130 9 => 1_000_000_000.,
131 x => 10_f64.powi(x.cast_signed().extend()),
132 }
133}
134
135#[inline]
140pub(crate) fn format_float(
141 output: &mut (impl io::Write + ?Sized),
142 mut value: f64,
143 digits_before_decimal: u8,
144 digits_after_decimal: Option<NonZero<u8>>,
145) -> io::Result<usize> {
146 match digits_after_decimal {
147 Some(digits_after_decimal) => {
148 if digits_after_decimal.get() < 9 {
161 let trunc_num = f64_10_pow_x(digits_after_decimal);
162 value = f64::trunc(value * trunc_num) / trunc_num;
163 }
164
165 let digits_after_decimal = digits_after_decimal.get().extend();
166 let width = digits_before_decimal.extend::<usize>() + 1 + digits_after_decimal;
167 try_likely_ok!(write!(output, "{value:0>width$.digits_after_decimal$}"));
168 Ok(width)
169 }
170 None => {
171 let value = value.trunc() as u64;
172 let width = digits_before_decimal.extend();
173 try_likely_ok!(write!(output, "{value:0>width$}"));
174 Ok(width)
175 }
176 }
177}
178
179#[inline]
181pub(crate) fn format_single_digit(
182 output: &mut (impl io::Write + ?Sized),
183 value: ru8<0, 9>,
184) -> io::Result<usize> {
185 write(output, num_fmt::single_digit(value))
186}
187
188#[inline]
190pub(crate) fn format_two_digits(
191 output: &mut (impl io::Write + ?Sized),
192 value: ru8<0, 99>,
193 padding: modifier::Padding,
194) -> io::Result<usize> {
195 let s = match padding {
196 modifier::Padding::Space => num_fmt::two_digits_space_padded(value),
197 modifier::Padding::Zero => num_fmt::two_digits_zero_padded(value),
198 modifier::Padding::None => num_fmt::one_to_two_digits_no_padding(value),
199 };
200 write(output, s)
201}
202
203#[inline]
205pub(crate) fn format_three_digits(
206 output: &mut (impl io::Write + ?Sized),
207 value: ru16<0, 999>,
208 padding: modifier::Padding,
209) -> io::Result<usize> {
210 let [first, second_and_third] = match padding {
211 modifier::Padding::Space => num_fmt::three_digits_space_padded(value),
212 modifier::Padding::Zero => num_fmt::three_digits_zero_padded(value),
213 modifier::Padding::None => num_fmt::one_to_three_digits_no_padding(value),
214 };
215 write_many(output, [first, second_and_third])
216}
217
218#[inline]
220pub(crate) fn format_four_digits(
221 output: &mut (impl io::Write + ?Sized),
222 value: ru16<0, 9_999>,
223 padding: modifier::Padding,
224) -> io::Result<usize> {
225 let [first_and_second, third_and_fourth] = match padding {
226 modifier::Padding::Space => num_fmt::four_digits_space_padded(value),
227 modifier::Padding::Zero => num_fmt::four_digits_zero_padded(value),
228 modifier::Padding::None => num_fmt::one_to_four_digits_no_padding(value),
229 };
230 write_many(output, [first_and_second, third_and_fourth])
231}
232
233#[inline]
235pub(crate) fn format_four_digits_pad_zero(
236 output: &mut (impl io::Write + ?Sized),
237 value: ru16<0, 9_999>,
238) -> io::Result<usize> {
239 write_many(output, num_fmt::four_digits_zero_padded(value))
240}
241
242#[inline]
244pub(crate) fn format_five_digits_pad_zero(
245 output: &mut (impl io::Write + ?Sized),
246 value: ru32<0, 99_999>,
247) -> io::Result<usize> {
248 write_many(output, num_fmt::five_digits_zero_padded(value))
249}
250
251#[inline]
253pub(crate) fn format_six_digits_pad_zero(
254 output: &mut (impl io::Write + ?Sized),
255 value: ru32<0, 999_999>,
256) -> io::Result<usize> {
257 write_many(output, num_fmt::six_digits_zero_padded(value))
258}
259
260#[inline]
264pub(crate) fn format_number_pad_none(
265 output: &mut (impl io::Write + ?Sized),
266 value: impl itoa::Integer + Copy,
267) -> Result<usize, io::Error> {
268 write(output, itoa::Buffer::new().format(value))
269}
270
271#[inline]
275pub(crate) fn format_component<V>(
276 output: &mut (impl io::Write + ?Sized),
277 component: Component,
278 value: &V,
279 state: &mut V::State,
280) -> Result<usize, error::Format>
281where
282 V: ComponentProvider,
283{
284 use Component::*;
285 match component {
286 Day(modifier) if V::SUPPLIES_DATE => fmt_day(output, value.day(state), modifier),
287 MonthShort(modifier) if V::SUPPLIES_DATE => {
288 fmt_month_short(output, value.month(state), modifier)
289 }
290 MonthLong(modifier) if V::SUPPLIES_DATE => {
291 fmt_month_long(output, value.month(state), modifier)
292 }
293 MonthNumerical(modifier) if V::SUPPLIES_DATE => {
294 fmt_month_numerical(output, value.month(state), modifier)
295 }
296 Ordinal(modifier) if V::SUPPLIES_DATE => {
297 fmt_ordinal(output, value.ordinal(state), modifier)
298 }
299 WeekdayShort(modifier) if V::SUPPLIES_DATE => {
300 fmt_weekday_short(output, value.weekday(state), modifier)
301 }
302 WeekdayLong(modifier) if V::SUPPLIES_DATE => {
303 fmt_weekday_long(output, value.weekday(state), modifier)
304 }
305 WeekdaySunday(modifier) if V::SUPPLIES_DATE => {
306 fmt_weekday_sunday(output, value.weekday(state), modifier)
307 }
308 WeekdayMonday(modifier) if V::SUPPLIES_DATE => {
309 fmt_weekday_monday(output, value.weekday(state), modifier)
310 }
311 WeekNumberIso(modifier) if V::SUPPLIES_DATE => {
312 fmt_week_number_iso(output, value.iso_week_number(state), modifier)
313 }
314 WeekNumberSunday(modifier) if V::SUPPLIES_DATE => {
315 fmt_week_number_sunday(output, value.sunday_based_week(state), modifier)
316 }
317 WeekNumberMonday(modifier) if V::SUPPLIES_DATE => {
318 fmt_week_number_monday(output, value.monday_based_week(state), modifier)
319 }
320 CalendarYearFullExtendedRange(modifier) if V::SUPPLIES_DATE => {
321 fmt_calendar_year_full_extended_range(output, value.calendar_year(state), modifier)
322 }
323 CalendarYearFullStandardRange(modifier) if V::SUPPLIES_DATE => {
324 fmt_calendar_year_full_standard_range(
325 output,
326 try_likely_ok!(
327 value
328 .calendar_year(state)
329 .narrow::<-9_999, 9_999>()
330 .ok_or_else(|| error::ComponentRange::conditional("year"))
331 )
332 .into(),
333 modifier,
334 )
335 }
336 IsoYearFullExtendedRange(modifier) if V::SUPPLIES_DATE => {
337 fmt_iso_year_full_extended_range(output, value.iso_year(state), modifier)
338 }
339 IsoYearFullStandardRange(modifier) if V::SUPPLIES_DATE => fmt_iso_year_full_standard_range(
340 output,
341 try_likely_ok!(
342 value
343 .iso_year(state)
344 .narrow::<-9_999, 9_999>()
345 .ok_or_else(|| error::ComponentRange::conditional("year"))
346 )
347 .into(),
348 modifier,
349 ),
350 CalendarYearCenturyExtendedRange(modifier) if V::SUPPLIES_DATE => {
351 let year = value.calendar_year(state);
352 let century = unsafe { ri16::new_unchecked((year.get() / 100).truncate()) };
354 fmt_calendar_year_century_extended_range(output, century, year.is_negative(), modifier)
355 }
356 CalendarYearCenturyStandardRange(modifier) if V::SUPPLIES_DATE => {
357 let year = value.calendar_year(state);
358 let is_negative = year.is_negative();
359 let year = unsafe { ri16::<0, 9_999>::new_unchecked((year.get() / 100).truncate()) };
361 fmt_calendar_year_century_standard_range(
362 output,
363 year.narrow::<0, 99>()
364 .ok_or_else(|| error::ComponentRange::conditional("year"))?
365 .into(),
366 is_negative,
367 modifier,
368 )
369 }
370 IsoYearCenturyExtendedRange(modifier) if V::SUPPLIES_DATE => {
371 let year = value.iso_year(state);
372 let century = unsafe { ri16::new_unchecked((year.get() / 100).truncate()) };
374 fmt_iso_year_century_extended_range(output, century, year.is_negative(), modifier)
375 }
376 IsoYearCenturyStandardRange(modifier) if V::SUPPLIES_DATE => {
377 let year = value.iso_year(state);
378 let year = unsafe { ri16::<0, 9_999>::new_unchecked((year.get() / 100).truncate()) };
380 fmt_iso_year_century_standard_range(
381 output,
382 year.narrow::<0, 99>()
383 .ok_or_else(|| error::ComponentRange::conditional("year"))?
384 .into(),
385 year.is_negative(),
386 modifier,
387 )
388 }
389 CalendarYearLastTwo(modifier) if V::SUPPLIES_DATE => {
390 let last_two = unsafe {
393 ru8::new_unchecked(
394 (value.calendar_year(state).get().unsigned_abs() % 100).truncate(),
395 )
396 };
397 fmt_calendar_year_last_two(output, last_two, modifier)
398 }
399 IsoYearLastTwo(modifier) if V::SUPPLIES_DATE => {
400 let last_two = unsafe {
403 ru8::new_unchecked((value.iso_year(state).get().unsigned_abs() % 100).truncate())
404 };
405 fmt_iso_year_last_two(output, last_two, modifier)
406 }
407 Hour12(modifier) if V::SUPPLIES_TIME => fmt_hour_12(output, value.hour(state), modifier),
408 Hour24(modifier) if V::SUPPLIES_TIME => fmt_hour_24(output, value.hour(state), modifier),
409 Minute(modifier) if V::SUPPLIES_TIME => fmt_minute(output, value.minute(state), modifier),
410 Period(modifier) if V::SUPPLIES_TIME => fmt_period(output, value.period(state), modifier),
411 Second(modifier) if V::SUPPLIES_TIME => fmt_second(output, value.second(state), modifier),
412 Subsecond(modifier) if V::SUPPLIES_TIME => {
413 fmt_subsecond(output, value.nanosecond(state), modifier)
414 }
415 OffsetHour(modifier) if V::SUPPLIES_OFFSET => fmt_offset_hour(
416 output,
417 value.offset_is_negative(state),
418 value.offset_hour(state),
419 modifier,
420 ),
421 OffsetMinute(modifier) if V::SUPPLIES_OFFSET => {
422 fmt_offset_minute(output, value.offset_minute(state), modifier)
423 }
424 OffsetSecond(modifier) if V::SUPPLIES_OFFSET => {
425 fmt_offset_second(output, value.offset_second(state), modifier)
426 }
427 Ignore(_) => return Ok(0),
428 UnixTimestampSecond(modifier) if V::SUPPLIES_TIMESTAMP => {
429 fmt_unix_timestamp_second(output, value.unix_timestamp_seconds(state), modifier)
430 }
431 UnixTimestampMillisecond(modifier) if V::SUPPLIES_TIMESTAMP => {
432 fmt_unix_timestamp_millisecond(
433 output,
434 value.unix_timestamp_milliseconds(state),
435 modifier,
436 )
437 }
438 UnixTimestampMicrosecond(modifier) if V::SUPPLIES_TIMESTAMP => {
439 fmt_unix_timestamp_microsecond(
440 output,
441 value.unix_timestamp_microseconds(state),
442 modifier,
443 )
444 }
445 UnixTimestampNanosecond(modifier) if V::SUPPLIES_TIMESTAMP => {
446 fmt_unix_timestamp_nanosecond(output, value.unix_timestamp_nanoseconds(state), modifier)
447 }
448 End(modifier::End { trailing_input: _ }) => return Ok(0),
449
450 #[expect(deprecated)]
452 Month(modifier::Month {
453 padding,
454 repr,
455 case_sensitive,
456 }) if V::SUPPLIES_DATE => {
457 let month = value.month(state);
458 match repr {
459 modifier::MonthRepr::Numerical => {
460 fmt_month_numerical(output, month, modifier::MonthNumerical { padding })
461 }
462 modifier::MonthRepr::Long => {
463 fmt_month_long(output, month, modifier::MonthLong { case_sensitive })
464 }
465 modifier::MonthRepr::Short => {
466 fmt_month_short(output, month, modifier::MonthShort { case_sensitive })
467 }
468 }
469 }
470 #[expect(deprecated)]
471 Weekday(modifier::Weekday {
472 repr,
473 one_indexed,
474 case_sensitive,
475 }) if V::SUPPLIES_DATE => {
476 let weekday = value.weekday(state);
477 match repr {
478 modifier::WeekdayRepr::Short => {
479 fmt_weekday_short(output, weekday, modifier::WeekdayShort { case_sensitive })
480 }
481 modifier::WeekdayRepr::Long => {
482 fmt_weekday_long(output, weekday, modifier::WeekdayLong { case_sensitive })
483 }
484 modifier::WeekdayRepr::Sunday => {
485 fmt_weekday_sunday(output, weekday, modifier::WeekdaySunday { one_indexed })
486 }
487 modifier::WeekdayRepr::Monday => {
488 fmt_weekday_monday(output, weekday, modifier::WeekdayMonday { one_indexed })
489 }
490 }
491 }
492 #[expect(deprecated)]
493 Hour(modifier::Hour {
494 padding,
495 is_12_hour_clock,
496 }) if V::SUPPLIES_TIME => {
497 let hour = value.hour(state);
498 if is_12_hour_clock {
499 fmt_hour_12(output, hour, modifier::Hour12 { padding })
500 } else {
501 fmt_hour_24(output, hour, modifier::Hour24 { padding })
502 }
503 }
504 #[expect(deprecated)]
505 UnixTimestamp(modifier) if V::SUPPLIES_TIMESTAMP => match modifier.precision {
506 modifier::UnixTimestampPrecision::Second => fmt_unix_timestamp_second(
507 output,
508 value.unix_timestamp_seconds(state),
509 modifier::UnixTimestampSecond {
510 sign_is_mandatory: modifier.sign_is_mandatory,
511 },
512 ),
513 modifier::UnixTimestampPrecision::Millisecond => fmt_unix_timestamp_millisecond(
514 output,
515 value.unix_timestamp_milliseconds(state),
516 modifier::UnixTimestampMillisecond {
517 sign_is_mandatory: modifier.sign_is_mandatory,
518 },
519 ),
520 modifier::UnixTimestampPrecision::Microsecond => fmt_unix_timestamp_microsecond(
521 output,
522 value.unix_timestamp_microseconds(state),
523 modifier::UnixTimestampMicrosecond {
524 sign_is_mandatory: modifier.sign_is_mandatory,
525 },
526 ),
527 modifier::UnixTimestampPrecision::Nanosecond => fmt_unix_timestamp_nanosecond(
528 output,
529 value.unix_timestamp_nanoseconds(state),
530 modifier::UnixTimestampNanosecond {
531 sign_is_mandatory: modifier.sign_is_mandatory,
532 },
533 ),
534 },
535 #[expect(deprecated)]
536 WeekNumber(modifier) if V::SUPPLIES_DATE => fmt_week_number(
537 output,
538 match modifier.repr {
539 modifier::WeekNumberRepr::Iso => value.iso_week_number(state).expand(),
540 modifier::WeekNumberRepr::Sunday => value.sunday_based_week(state).expand(),
541 modifier::WeekNumberRepr::Monday => value.monday_based_week(state).expand(),
542 },
543 modifier,
544 ),
545 #[expect(deprecated)]
546 Year(
547 modifier @ modifier::Year {
548 repr: modifier::YearRepr::Full,
549 iso_week_based: false,
550 ..
551 },
552 ) if V::SUPPLIES_DATE => {
553 return fmt_full_year(output, value.calendar_year(state), modifier);
554 }
555 #[expect(deprecated)]
556 Year(
557 modifier @ modifier::Year {
558 repr: modifier::YearRepr::Century,
559 iso_week_based: false,
560 ..
561 },
562 ) if V::SUPPLIES_DATE => return fmt_century(output, value.calendar_year(state), modifier),
563 #[expect(deprecated)]
564 Year(
565 modifier @ modifier::Year {
566 repr: modifier::YearRepr::LastTwo,
567 iso_week_based: false,
568 ..
569 },
570 ) if V::SUPPLIES_DATE => fmt_year_last_two(output, value.calendar_year(state), modifier),
571 #[expect(deprecated)]
572 Year(
573 modifier @ modifier::Year {
574 repr: modifier::YearRepr::Full,
575 iso_week_based: true,
576 ..
577 },
578 ) if V::SUPPLIES_DATE => {
579 return fmt_full_year(output, value.iso_year(state), modifier);
580 }
581 #[expect(deprecated)]
582 Year(
583 modifier @ modifier::Year {
584 repr: modifier::YearRepr::Century,
585 iso_week_based: true,
586 ..
587 },
588 ) if V::SUPPLIES_DATE => return fmt_century(output, value.iso_year(state), modifier),
589 #[expect(deprecated)]
590 Year(
591 modifier @ modifier::Year {
592 repr: modifier::YearRepr::LastTwo,
593 iso_week_based: true,
594 ..
595 },
596 ) if V::SUPPLIES_DATE => fmt_year_last_two(output, value.iso_year(state), modifier),
597
598 #[allow(unreachable_patterns)]
603 #[expect(deprecated)]
604 Day(_)
605 | MonthShort(_)
606 | MonthLong(_)
607 | MonthNumerical(_)
608 | Ordinal(_)
609 | WeekdayShort(_)
610 | WeekdayLong(_)
611 | WeekdaySunday(_)
612 | WeekdayMonday(_)
613 | WeekNumberIso(_)
614 | WeekNumberSunday(_)
615 | WeekNumberMonday(_)
616 | CalendarYearFullExtendedRange(_)
617 | CalendarYearFullStandardRange(_)
618 | IsoYearFullExtendedRange(_)
619 | IsoYearFullStandardRange(_)
620 | CalendarYearCenturyExtendedRange(_)
621 | CalendarYearCenturyStandardRange(_)
622 | IsoYearCenturyExtendedRange(_)
623 | IsoYearCenturyStandardRange(_)
624 | CalendarYearLastTwo(_)
625 | IsoYearLastTwo(_)
626 | Hour12(_)
627 | Hour24(_)
628 | Minute(_)
629 | Period(_)
630 | Second(_)
631 | Subsecond(_)
632 | OffsetHour(_)
633 | OffsetMinute(_)
634 | OffsetSecond(_)
635 | Ignore(_)
636 | UnixTimestampSecond(_)
637 | UnixTimestampMillisecond(_)
638 | UnixTimestampMicrosecond(_)
639 | UnixTimestampNanosecond(_)
640 | End(_)
641 | Month(_)
642 | Weekday(_)
643 | Hour(_)
644 | UnixTimestamp(_)
645 | WeekNumber(_)
646 | Year(_) => return Err(error::Format::InsufficientTypeInformation),
647 }
648 .map_err(Into::into)
649}
650
651#[inline]
653fn fmt_day(
654 output: &mut (impl io::Write + ?Sized),
655 day: Day,
656 modifier::Day { padding }: modifier::Day,
657) -> Result<usize, io::Error> {
658 format_two_digits(output, day.expand(), padding)
659}
660
661#[inline]
663fn fmt_month_short(
664 output: &mut (impl io::Write + ?Sized),
665 month: Month,
666 modifier::MonthShort {
667 case_sensitive: _, }: modifier::MonthShort,
669) -> io::Result<usize> {
670 write(output, unsafe {
672 MONTH_NAMES[u8::from(month).extend::<usize>() - 1].get_unchecked(..3)
673 })
674}
675
676#[inline]
678fn fmt_month_long(
679 output: &mut (impl io::Write + ?Sized),
680 month: Month,
681 modifier::MonthLong {
682 case_sensitive: _, }: modifier::MonthLong,
684) -> io::Result<usize> {
685 write(output, MONTH_NAMES[u8::from(month).extend::<usize>() - 1])
686}
687
688#[inline]
690fn fmt_month_numerical(
691 output: &mut (impl io::Write + ?Sized),
692 month: Month,
693 modifier::MonthNumerical { padding }: modifier::MonthNumerical,
694) -> io::Result<usize> {
695 format_two_digits(
696 output,
697 unsafe { ru8::new_unchecked(u8::from(month)) },
699 padding,
700 )
701}
702
703#[inline]
705fn fmt_ordinal(
706 output: &mut (impl io::Write + ?Sized),
707 ordinal: Ordinal,
708 modifier::Ordinal { padding }: modifier::Ordinal,
709) -> Result<usize, io::Error> {
710 format_three_digits(output, ordinal.expand(), padding)
711}
712
713#[inline]
715fn fmt_weekday_short(
716 output: &mut (impl io::Write + ?Sized),
717 weekday: Weekday,
718 modifier::WeekdayShort {
719 case_sensitive: _, }: modifier::WeekdayShort,
721) -> io::Result<usize> {
722 write(output, unsafe {
724 WEEKDAY_NAMES[weekday.number_days_from_monday().extend::<usize>()].get_unchecked(..3)
725 })
726}
727
728#[inline]
730fn fmt_weekday_long(
731 output: &mut (impl io::Write + ?Sized),
732 weekday: Weekday,
733 modifier::WeekdayLong {
734 case_sensitive: _, }: modifier::WeekdayLong,
736) -> io::Result<usize> {
737 write(
738 output,
739 WEEKDAY_NAMES[weekday.number_days_from_monday().extend::<usize>()],
740 )
741}
742
743#[inline]
746fn fmt_weekday_sunday(
747 output: &mut (impl io::Write + ?Sized),
748 weekday: Weekday,
749 modifier::WeekdaySunday { one_indexed }: modifier::WeekdaySunday,
750) -> io::Result<usize> {
751 format_single_digit(output, unsafe {
753 ru8::new_unchecked(weekday.number_days_from_sunday() + u8::from(one_indexed))
754 })
755}
756
757#[inline]
760fn fmt_weekday_monday(
761 output: &mut (impl io::Write + ?Sized),
762 weekday: Weekday,
763 modifier::WeekdayMonday { one_indexed }: modifier::WeekdayMonday,
764) -> io::Result<usize> {
765 format_single_digit(output, unsafe {
767 ru8::new_unchecked(weekday.number_days_from_monday() + u8::from(one_indexed))
768 })
769}
770
771#[inline]
772fn fmt_week_number_iso(
773 output: &mut (impl io::Write + ?Sized),
774 week_number: IsoWeekNumber,
775 modifier::WeekNumberIso { padding }: modifier::WeekNumberIso,
776) -> io::Result<usize> {
777 format_two_digits(output, week_number.expand(), padding)
778}
779
780#[inline]
781fn fmt_week_number_sunday(
782 output: &mut (impl io::Write + ?Sized),
783 week_number: SundayBasedWeek,
784 modifier::WeekNumberSunday { padding }: modifier::WeekNumberSunday,
785) -> io::Result<usize> {
786 format_two_digits(output, week_number.expand(), padding)
787}
788
789#[inline]
790fn fmt_week_number_monday(
791 output: &mut (impl io::Write + ?Sized),
792 week_number: MondayBasedWeek,
793 modifier::WeekNumberMonday { padding }: modifier::WeekNumberMonday,
794) -> io::Result<usize> {
795 format_two_digits(output, week_number.expand(), padding)
796}
797
798#[inline]
800#[expect(deprecated)]
801#[deprecated(since = "0.3.48", note = "use `fmt_week_number_*` methods instead")]
802fn fmt_week_number(
803 output: &mut (impl io::Write + ?Sized),
804 week_number: AnyWeekNumber,
805 modifier::WeekNumber { padding, repr: _ }: modifier::WeekNumber,
806) -> Result<usize, io::Error> {
807 format_two_digits(output, week_number.expand(), padding)
808}
809
810#[inline]
811fn fmt_calendar_year_full_extended_range(
812 output: &mut (impl io::Write + ?Sized),
813 full_year: Year,
814 modifier::CalendarYearFullExtendedRange {
815 padding,
816 sign_is_mandatory,
817 }: modifier::CalendarYearFullExtendedRange,
818) -> io::Result<usize> {
819 let mut bytes = 0;
820 bytes += try_likely_ok!(fmt_sign(
821 output,
822 full_year.is_negative(),
823 sign_is_mandatory || full_year.get() >= 10_000,
824 ));
825 let value: ru32<0, 999_999> =
828 unsafe { full_year.abs().narrow_unchecked::<0, 999_999>().into() };
829
830 bytes += if let Some(value) = value.narrow::<0, 9_999>() {
831 try_likely_ok!(format_four_digits(output, value.into(), padding))
832 } else if let Some(value) = value.narrow::<0, 99_999>() {
833 try_likely_ok!(format_five_digits_pad_zero(output, value))
834 } else {
835 try_likely_ok!(format_six_digits_pad_zero(output, value))
836 };
837 Ok(bytes)
838}
839
840#[inline]
841fn fmt_calendar_year_full_standard_range(
842 output: &mut (impl io::Write + ?Sized),
843 full_year: StandardYear,
844 modifier::CalendarYearFullStandardRange {
845 padding,
846 sign_is_mandatory,
847 }: modifier::CalendarYearFullStandardRange,
848) -> io::Result<usize> {
849 let mut bytes = 0;
850 bytes += try_likely_ok!(fmt_sign(output, full_year.is_negative(), sign_is_mandatory));
851 bytes += try_likely_ok!(format_four_digits(
853 output,
854 unsafe { full_year.abs().narrow_unchecked::<0, 9_999>().into() },
855 padding
856 ));
857 Ok(bytes)
858}
859
860#[inline]
861fn fmt_iso_year_full_extended_range(
862 output: &mut (impl io::Write + ?Sized),
863 full_year: Year,
864 modifier::IsoYearFullExtendedRange {
865 padding,
866 sign_is_mandatory,
867 }: modifier::IsoYearFullExtendedRange,
868) -> io::Result<usize> {
869 let mut bytes = 0;
870 bytes += try_likely_ok!(fmt_sign(
871 output,
872 full_year.is_negative(),
873 sign_is_mandatory || full_year.get() >= 10_000,
874 ));
875 let value: ru32<0, 999_999> =
877 unsafe { full_year.abs().narrow_unchecked::<0, 999_999>().into() };
878
879 bytes += if let Some(value) = value.narrow::<0, 9_999>() {
880 try_likely_ok!(format_four_digits(output, value.into(), padding))
881 } else if let Some(value) = value.narrow::<0, 99_999>() {
882 try_likely_ok!(format_five_digits_pad_zero(output, value))
883 } else {
884 try_likely_ok!(format_six_digits_pad_zero(output, value))
885 };
886 Ok(bytes)
887}
888
889#[inline]
890fn fmt_iso_year_full_standard_range(
891 output: &mut (impl io::Write + ?Sized),
892 year: StandardYear,
893 modifier::IsoYearFullStandardRange {
894 padding,
895 sign_is_mandatory,
896 }: modifier::IsoYearFullStandardRange,
897) -> io::Result<usize> {
898 let mut bytes = 0;
899 bytes += try_likely_ok!(fmt_sign(output, year.is_negative(), sign_is_mandatory));
900 bytes += try_likely_ok!(format_four_digits(
902 output,
903 unsafe { year.abs().narrow_unchecked::<0, 9_999>().into() },
904 padding
905 ));
906 Ok(bytes)
907}
908
909#[inline]
910fn fmt_calendar_year_century_extended_range(
911 output: &mut (impl io::Write + ?Sized),
912 century: ExtendedCentury,
913 is_negative: bool,
914 modifier::CalendarYearCenturyExtendedRange {
915 padding,
916 sign_is_mandatory,
917 }: modifier::CalendarYearCenturyExtendedRange,
918) -> io::Result<usize> {
919 let mut bytes = 0;
920 bytes += try_likely_ok!(fmt_sign(
921 output,
922 is_negative,
923 sign_is_mandatory || century.get() >= 100,
924 ));
925 let century: ru16<0, 9_999> = unsafe { century.abs().narrow_unchecked::<0, 9_999>().into() };
927
928 bytes += if let Some(century) = century.narrow::<0, 99>() {
929 try_likely_ok!(format_two_digits(output, century.into(), padding))
930 } else if let Some(century) = century.narrow::<0, 999>() {
931 try_likely_ok!(format_three_digits(output, century, padding))
932 } else {
933 try_likely_ok!(format_four_digits(output, century, padding))
934 };
935 Ok(bytes)
936}
937
938#[inline]
939fn fmt_calendar_year_century_standard_range(
940 output: &mut (impl io::Write + ?Sized),
941 century: StandardCentury,
942 is_negative: bool,
943 modifier::CalendarYearCenturyStandardRange {
944 padding,
945 sign_is_mandatory,
946 }: modifier::CalendarYearCenturyStandardRange,
947) -> io::Result<usize> {
948 let mut bytes = 0;
949 bytes += try_likely_ok!(fmt_sign(output, is_negative, sign_is_mandatory));
950 let century = unsafe { century.abs().narrow_unchecked::<0, 99>() };
952 bytes += try_likely_ok!(format_two_digits(output, century.into(), padding));
953 Ok(bytes)
954}
955
956#[inline]
957fn fmt_iso_year_century_extended_range(
958 output: &mut (impl io::Write + ?Sized),
959 century: ExtendedCentury,
960 is_negative: bool,
961 modifier::IsoYearCenturyExtendedRange {
962 padding,
963 sign_is_mandatory,
964 }: modifier::IsoYearCenturyExtendedRange,
965) -> io::Result<usize> {
966 let mut bytes = 0;
967 bytes += try_likely_ok!(fmt_sign(
968 output,
969 is_negative,
970 sign_is_mandatory || century.get() >= 100,
971 ));
972 let century: ru16<0, 9_999> = unsafe { century.abs().narrow_unchecked::<0, 9_999>().into() };
974
975 bytes += if let Some(century) = century.narrow::<0, 99>() {
976 try_likely_ok!(format_two_digits(output, century.into(), padding))
977 } else if let Some(century) = century.narrow::<0, 999>() {
978 try_likely_ok!(format_three_digits(output, century, padding))
979 } else {
980 try_likely_ok!(format_four_digits(output, century, padding))
981 };
982 Ok(bytes)
983}
984
985#[inline]
986fn fmt_iso_year_century_standard_range(
987 output: &mut (impl io::Write + ?Sized),
988 century: StandardCentury,
989 is_negative: bool,
990 modifier::IsoYearCenturyStandardRange {
991 padding,
992 sign_is_mandatory,
993 }: modifier::IsoYearCenturyStandardRange,
994) -> io::Result<usize> {
995 let mut bytes = 0;
996 bytes += try_likely_ok!(fmt_sign(output, is_negative, sign_is_mandatory));
997 let century = unsafe { century.abs().narrow_unchecked::<0, 99>() };
999 bytes += try_likely_ok!(format_two_digits(output, century.into(), padding));
1000 Ok(bytes)
1001}
1002
1003#[inline]
1004fn fmt_calendar_year_last_two(
1005 output: &mut (impl io::Write + ?Sized),
1006 last_two: LastTwo,
1007 modifier::CalendarYearLastTwo { padding }: modifier::CalendarYearLastTwo,
1008) -> io::Result<usize> {
1009 format_two_digits(output, last_two, padding)
1010}
1011
1012#[inline]
1013fn fmt_iso_year_last_two(
1014 output: &mut (impl io::Write + ?Sized),
1015 last_two: LastTwo,
1016 modifier::IsoYearLastTwo { padding }: modifier::IsoYearLastTwo,
1017) -> io::Result<usize> {
1018 format_two_digits(output, last_two, padding)
1019}
1020
1021#[inline]
1023#[expect(deprecated)]
1024#[deprecated]
1025fn fmt_full_year(
1026 output: &mut (impl io::Write + ?Sized),
1027 full_year: Year,
1028 modifier::Year {
1029 padding,
1030 repr: _,
1031 range,
1032 iso_week_based: _,
1033 sign_is_mandatory,
1034 }: modifier::Year,
1035) -> Result<usize, error::Format> {
1036 let mut bytes = 0;
1037 bytes += try_likely_ok!(fmt_sign(
1038 output,
1039 full_year.is_negative(),
1040 sign_is_mandatory || (cfg!(feature = "large-dates") && full_year.get() >= 10_000),
1041 ));
1042 bytes += if cfg!(feature = "large-dates") && range == modifier::YearRange::Extended {
1043 let value: ru32<0, 999_999> =
1046 unsafe { full_year.abs().narrow_unchecked::<0, 999_999>().into() };
1047
1048 if let Some(value) = value.narrow::<0, 9_999>() {
1049 try_likely_ok!(format_four_digits(output, value.into(), padding))
1050 } else if let Some(value) = value.narrow::<0, 99_999>() {
1051 try_likely_ok!(format_five_digits_pad_zero(output, value))
1052 } else {
1053 try_likely_ok!(format_six_digits_pad_zero(output, value))
1054 }
1055 } else if let Some(value) = full_year.abs().narrow::<0, 9_999>() {
1056 try_likely_ok!(format_four_digits(output, value.into(), padding))
1057 } else {
1058 return Err(error::ComponentRange::conditional("year").into());
1059 };
1060 Ok(bytes)
1061}
1062
1063#[inline]
1066#[expect(deprecated)]
1067#[deprecated]
1068fn fmt_century(
1069 output: &mut (impl io::Write + ?Sized),
1070 full_year: Year,
1071 modifier::Year {
1072 padding,
1073 repr: _,
1074 range,
1075 iso_week_based: _,
1076 sign_is_mandatory,
1077 }: modifier::Year,
1078) -> Result<usize, error::Format> {
1079 let mut bytes = 0;
1080 bytes += try_likely_ok!(fmt_sign(
1081 output,
1082 full_year.is_negative(),
1083 sign_is_mandatory || (cfg!(feature = "large-dates") && full_year.get() >= 10_000),
1084 ));
1085 bytes += if cfg!(feature = "large-dates") && range == modifier::YearRange::Extended {
1086 let value: ru16<0, 9_999> =
1089 unsafe { ru16::new_unchecked((full_year.get().unsigned_abs() / 100).truncate()) };
1090
1091 if let Some(value) = value.narrow::<0, 99>() {
1092 try_likely_ok!(format_two_digits(output, value.into(), padding))
1093 } else if let Some(value) = value.narrow::<0, 999>() {
1094 try_likely_ok!(format_three_digits(output, value, padding))
1095 } else {
1096 try_likely_ok!(format_four_digits(output, value, padding))
1097 }
1098 } else {
1099 let value =
1102 unsafe { ru32::<0, 9_999>::new_unchecked(full_year.get().unsigned_abs() / 100) };
1103
1104 if let Some(value) = value.narrow::<0, 99>() {
1105 try_likely_ok!(format_two_digits(output, value.into(), padding))
1106 } else {
1107 return Err(error::ComponentRange::conditional("year").into());
1108 }
1109 };
1110 Ok(bytes)
1111}
1112
1113#[inline]
1114#[expect(deprecated)]
1115#[deprecated]
1116fn fmt_year_last_two(
1117 output: &mut (impl io::Write + ?Sized),
1118 full_year: Year,
1119 modifier::Year {
1120 padding,
1121 repr: _,
1122 range: _,
1123 iso_week_based: _,
1124 sign_is_mandatory: _,
1125 }: modifier::Year,
1126) -> Result<usize, io::Error> {
1127 let mut bytes = 0;
1128 let value = unsafe { ru8::new_unchecked((full_year.get() % 100).unsigned_abs().truncate()) };
1131 bytes += try_likely_ok!(format_two_digits(output, value, padding));
1132 Ok(bytes)
1133}
1134
1135#[inline]
1137fn fmt_hour_12(
1138 output: &mut (impl io::Write + ?Sized),
1139 hour: Hours,
1140 modifier::Hour12 { padding }: modifier::Hour12,
1141) -> io::Result<usize> {
1142 format_two_digits(
1144 output,
1145 unsafe { ru8::new_unchecked((hour.get() + 11) % 12 + 1) },
1146 padding,
1147 )
1148}
1149
1150#[inline]
1152fn fmt_hour_24(
1153 output: &mut (impl io::Write + ?Sized),
1154 hour: Hours,
1155 modifier::Hour24 { padding }: modifier::Hour24,
1156) -> io::Result<usize> {
1157 format_two_digits(output, hour.expand(), padding)
1158}
1159
1160#[inline]
1162fn fmt_minute(
1163 output: &mut (impl io::Write + ?Sized),
1164 minute: Minutes,
1165 modifier::Minute { padding }: modifier::Minute,
1166) -> Result<usize, io::Error> {
1167 format_two_digits(output, minute.expand(), padding)
1168}
1169
1170#[inline]
1172fn fmt_period(
1173 output: &mut (impl io::Write + ?Sized),
1174 period: Period,
1175 modifier::Period {
1176 is_uppercase,
1177 case_sensitive: _, }: modifier::Period,
1179) -> Result<usize, io::Error> {
1180 write(
1181 output,
1182 match (period, is_uppercase) {
1183 (Period::Am, false) => "am",
1184 (Period::Am, true) => "AM",
1185 (Period::Pm, false) => "pm",
1186 (Period::Pm, true) => "PM",
1187 },
1188 )
1189}
1190
1191#[inline]
1193fn fmt_second(
1194 output: &mut (impl io::Write + ?Sized),
1195 second: Seconds,
1196 modifier::Second { padding }: modifier::Second,
1197) -> Result<usize, io::Error> {
1198 format_two_digits(output, second.expand(), padding)
1199}
1200
1201#[inline]
1203fn fmt_subsecond(
1204 output: &mut (impl io::Write + ?Sized),
1205 nanos: Nanoseconds,
1206 modifier::Subsecond { digits }: modifier::Subsecond,
1207) -> Result<usize, io::Error> {
1208 use modifier::SubsecondDigits::*;
1209
1210 #[repr(C, align(8))]
1211 #[derive(Clone, Copy)]
1212 struct Digits {
1213 _padding: MaybeUninit<[u8; 7]>,
1214 digit_1: u8,
1215 digits_2_thru_9: [u8; 8],
1216 }
1217
1218 let [
1219 digit_1,
1220 digits_2_and_3,
1221 digits_4_and_5,
1222 digits_6_and_7,
1223 digits_8_and_9,
1224 ] = num_fmt::subsecond_from_nanos(nanos);
1225
1226 let buf = Digits {
1229 _padding: MaybeUninit::uninit(),
1230 digit_1: digit_1.as_bytes()[0],
1231 digits_2_thru_9: [
1232 digits_2_and_3.as_bytes()[0],
1233 digits_2_and_3.as_bytes()[1],
1234 digits_4_and_5.as_bytes()[0],
1235 digits_4_and_5.as_bytes()[1],
1236 digits_6_and_7.as_bytes()[0],
1237 digits_6_and_7.as_bytes()[1],
1238 digits_8_and_9.as_bytes()[0],
1239 digits_8_and_9.as_bytes()[1],
1240 ],
1241 };
1242
1243 let len = match digits {
1244 One => 1,
1245 Two => 2,
1246 Three => 3,
1247 Four => 4,
1248 Five => 5,
1249 Six => 6,
1250 Seven => 7,
1251 Eight => 8,
1252 Nine => 9,
1253 OneOrMore => {
1254 let bitmask = u64::from_le_bytes(buf.digits_2_thru_9) ^ u64::from_le_bytes([b'0'; 8]);
1258 let digits_to_truncate = bitmask.leading_zeros() / 8;
1259 9 - digits_to_truncate as usize
1260 }
1261 };
1262
1263 let s = unsafe {
1267 num_fmt::StackStr::new(
1268 *(&raw const buf)
1269 .byte_add(core::mem::offset_of!(Digits, digit_1))
1270 .cast::<[MaybeUninit<u8>; 9]>(),
1271 len,
1272 )
1273 };
1274 write(output, &s)
1275}
1276
1277#[inline]
1278fn fmt_sign(
1279 output: &mut (impl io::Write + ?Sized),
1280 is_negative: bool,
1281 sign_is_mandatory: bool,
1282) -> Result<usize, io::Error> {
1283 if is_negative {
1284 write(output, "-")
1285 } else if sign_is_mandatory {
1286 write(output, "+")
1287 } else {
1288 Ok(0)
1289 }
1290}
1291
1292#[inline]
1294fn fmt_offset_hour(
1295 output: &mut (impl io::Write + ?Sized),
1296 is_negative: bool,
1297 hour: OffsetHours,
1298 modifier::OffsetHour {
1299 padding,
1300 sign_is_mandatory,
1301 }: modifier::OffsetHour,
1302) -> Result<usize, io::Error> {
1303 let mut bytes = 0;
1304 bytes += try_likely_ok!(fmt_sign(output, is_negative, sign_is_mandatory));
1305 bytes += try_likely_ok!(format_two_digits(
1307 output,
1308 unsafe { ru8::new_unchecked(hour.get().unsigned_abs()) },
1309 padding,
1310 ));
1311 Ok(bytes)
1312}
1313
1314#[inline]
1316fn fmt_offset_minute(
1317 output: &mut (impl io::Write + ?Sized),
1318 offset_minute: OffsetMinutes,
1319 modifier::OffsetMinute { padding }: modifier::OffsetMinute,
1320) -> Result<usize, io::Error> {
1321 format_two_digits(
1322 output,
1323 unsafe { ru8::new_unchecked(offset_minute.get().unsigned_abs()) },
1326 padding,
1327 )
1328}
1329
1330#[inline]
1332fn fmt_offset_second(
1333 output: &mut (impl io::Write + ?Sized),
1334 offset_second: OffsetSeconds,
1335 modifier::OffsetSecond { padding }: modifier::OffsetSecond,
1336) -> Result<usize, io::Error> {
1337 format_two_digits(
1338 output,
1339 unsafe { ru8::new_unchecked(offset_second.get().unsigned_abs()) },
1342 padding,
1343 )
1344}
1345
1346#[inline]
1348fn fmt_unix_timestamp_second(
1349 output: &mut (impl io::Write + ?Sized),
1350 timestamp: i64,
1351 modifier::UnixTimestampSecond { sign_is_mandatory }: modifier::UnixTimestampSecond,
1352) -> Result<usize, io::Error> {
1353 let mut bytes = 0;
1354 bytes += try_likely_ok!(fmt_sign(output, timestamp < 0, sign_is_mandatory));
1355 bytes += try_likely_ok!(format_number_pad_none(output, timestamp.unsigned_abs()));
1356 Ok(bytes)
1357}
1358
1359#[inline]
1361fn fmt_unix_timestamp_millisecond(
1362 output: &mut (impl io::Write + ?Sized),
1363 timestamp_millis: i64,
1364 modifier::UnixTimestampMillisecond { sign_is_mandatory }: modifier::UnixTimestampMillisecond,
1365) -> Result<usize, io::Error> {
1366 let mut bytes = 0;
1367 bytes += try_likely_ok!(fmt_sign(output, timestamp_millis < 0, sign_is_mandatory));
1368 bytes += try_likely_ok!(format_number_pad_none(
1369 output,
1370 timestamp_millis.unsigned_abs()
1371 ));
1372 Ok(bytes)
1373}
1374
1375#[inline]
1377fn fmt_unix_timestamp_microsecond(
1378 output: &mut (impl io::Write + ?Sized),
1379 timestamp_micros: i128,
1380 modifier::UnixTimestampMicrosecond { sign_is_mandatory }: modifier::UnixTimestampMicrosecond,
1381) -> Result<usize, io::Error> {
1382 let mut bytes = 0;
1383 bytes += try_likely_ok!(fmt_sign(output, timestamp_micros < 0, sign_is_mandatory));
1384 bytes += try_likely_ok!(format_number_pad_none(
1385 output,
1386 timestamp_micros.unsigned_abs()
1387 ));
1388 Ok(bytes)
1389}
1390
1391#[inline]
1393fn fmt_unix_timestamp_nanosecond(
1394 output: &mut (impl io::Write + ?Sized),
1395 timestamp_nanos: i128,
1396 modifier::UnixTimestampNanosecond { sign_is_mandatory }: modifier::UnixTimestampNanosecond,
1397) -> Result<usize, io::Error> {
1398 let mut bytes = 0;
1399 bytes += try_likely_ok!(fmt_sign(output, timestamp_nanos < 0, sign_is_mandatory));
1400 bytes += try_likely_ok!(format_number_pad_none(
1401 output,
1402 timestamp_nanos.unsigned_abs()
1403 ));
1404 Ok(bytes)
1405}