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::{Period, format_description_v3, 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>;
36
37const MONTH_NAMES: [&str; 12] = [
38 "January",
39 "February",
40 "March",
41 "April",
42 "May",
43 "June",
44 "July",
45 "August",
46 "September",
47 "October",
48 "November",
49 "December",
50];
51
52const WEEKDAY_NAMES: [&str; 7] = [
53 "Monday",
54 "Tuesday",
55 "Wednesday",
56 "Thursday",
57 "Friday",
58 "Saturday",
59 "Sunday",
60];
61
62#[inline]
64pub(crate) fn write_bytes(
65 output: &mut (impl io::Write + ?Sized),
66 bytes: &[u8],
67) -> io::Result<usize> {
68 try_likely_ok!(output.write_all(bytes));
69 Ok(bytes.len())
70}
71
72#[inline]
74pub(crate) fn write(output: &mut (impl io::Write + ?Sized), s: &str) -> io::Result<usize> {
75 try_likely_ok!(output.write_all(s.as_bytes()));
76 Ok(s.len())
77}
78
79#[inline]
81pub(crate) fn write_many<const N: usize>(
82 output: &mut (impl io::Write + ?Sized),
83 arr: [&str; N],
84) -> io::Result<usize> {
85 let mut bytes = 0;
86 for s in arr {
87 try_likely_ok!(output.write_all(s.as_bytes()));
88 bytes += s.len();
89 }
90 Ok(bytes)
91}
92
93#[inline]
95pub(crate) fn write_if(
96 output: &mut (impl io::Write + ?Sized),
97 pred: bool,
98 s: &str,
99) -> io::Result<usize> {
100 if pred { write(output, s) } else { Ok(0) }
101}
102
103#[inline]
105pub(crate) fn write_if_else(
106 output: &mut (impl io::Write + ?Sized),
107 pred: bool,
108 true_str: &str,
109 false_str: &str,
110) -> io::Result<usize> {
111 write(output, if pred { true_str } else { false_str })
112}
113
114#[inline]
119fn f64_10_pow_x(x: NonZero<u8>) -> f64 {
120 match x.get() {
121 1 => 10.,
122 2 => 100.,
123 3 => 1_000.,
124 4 => 10_000.,
125 5 => 100_000.,
126 6 => 1_000_000.,
127 7 => 10_000_000.,
128 8 => 100_000_000.,
129 9 => 1_000_000_000.,
130 x => 10_f64.powi(x.cast_signed().extend()),
131 }
132}
133
134#[inline]
139pub(crate) fn format_float(
140 output: &mut (impl io::Write + ?Sized),
141 mut value: f64,
142 digits_before_decimal: u8,
143 digits_after_decimal: Option<NonZero<u8>>,
144) -> io::Result<usize> {
145 match digits_after_decimal {
146 Some(digits_after_decimal) => {
147 if digits_after_decimal.get() < 9 {
160 let trunc_num = f64_10_pow_x(digits_after_decimal);
161 value = f64::trunc(value * trunc_num) / trunc_num;
162 }
163
164 let digits_after_decimal = digits_after_decimal.get().extend();
165 let width = digits_before_decimal.extend::<usize>() + 1 + digits_after_decimal;
166 try_likely_ok!(write!(output, "{value:0>width$.digits_after_decimal$}"));
167 Ok(width)
168 }
169 None => {
170 let value = value.trunc() as u64;
171 let width = digits_before_decimal.extend();
172 try_likely_ok!(write!(output, "{value:0>width$}"));
173 Ok(width)
174 }
175 }
176}
177
178#[inline]
180pub(crate) fn format_single_digit(
181 output: &mut (impl io::Write + ?Sized),
182 value: ru8<0, 9>,
183) -> io::Result<usize> {
184 write(output, num_fmt::single_digit(value))
185}
186
187#[inline]
189pub(crate) fn format_two_digits(
190 output: &mut (impl io::Write + ?Sized),
191 value: ru8<0, 99>,
192 padding: modifier::Padding,
193) -> io::Result<usize> {
194 let s = match padding {
195 modifier::Padding::Space => num_fmt::two_digits_space_padded(value),
196 modifier::Padding::Zero => num_fmt::two_digits_zero_padded(value),
197 modifier::Padding::None => num_fmt::one_to_two_digits_no_padding(value),
198 };
199 write(output, s)
200}
201
202#[inline]
204pub(crate) fn format_three_digits(
205 output: &mut (impl io::Write + ?Sized),
206 value: ru16<0, 999>,
207 padding: modifier::Padding,
208) -> io::Result<usize> {
209 let [first, second_and_third] = match padding {
210 modifier::Padding::Space => num_fmt::three_digits_space_padded(value),
211 modifier::Padding::Zero => num_fmt::three_digits_zero_padded(value),
212 modifier::Padding::None => num_fmt::one_to_three_digits_no_padding(value),
213 };
214 write_many(output, [first, second_and_third])
215}
216
217#[inline]
219pub(crate) fn format_four_digits(
220 output: &mut (impl io::Write + ?Sized),
221 value: ru16<0, 9_999>,
222 padding: modifier::Padding,
223) -> io::Result<usize> {
224 let [first_and_second, third_and_fourth] = match padding {
225 modifier::Padding::Space => num_fmt::four_digits_space_padded(value),
226 modifier::Padding::Zero => num_fmt::four_digits_zero_padded(value),
227 modifier::Padding::None => num_fmt::one_to_four_digits_no_padding(value),
228 };
229 write_many(output, [first_and_second, third_and_fourth])
230}
231
232#[inline]
234pub(crate) fn format_four_digits_pad_zero(
235 output: &mut (impl io::Write + ?Sized),
236 value: ru16<0, 9_999>,
237) -> io::Result<usize> {
238 write_many(output, num_fmt::four_digits_zero_padded(value))
239}
240
241#[inline]
243pub(crate) fn format_five_digits_pad_zero(
244 output: &mut (impl io::Write + ?Sized),
245 value: ru32<0, 99_999>,
246) -> io::Result<usize> {
247 write_many(output, num_fmt::five_digits_zero_padded(value))
248}
249
250#[inline]
252pub(crate) fn format_six_digits_pad_zero(
253 output: &mut (impl io::Write + ?Sized),
254 value: ru32<0, 999_999>,
255) -> io::Result<usize> {
256 write_many(output, num_fmt::six_digits_zero_padded(value))
257}
258
259#[inline]
263pub(crate) fn format_u64_pad_none(
264 output: &mut (impl io::Write + ?Sized),
265 value: u64,
266) -> io::Result<usize> {
267 write(output, &num_fmt::u64_pad_none(value))
268}
269
270#[inline]
274pub(crate) fn format_u128_pad_none(
275 output: &mut (impl io::Write + ?Sized),
276 value: u128,
277) -> io::Result<usize> {
278 write(output, &num_fmt::u128_pad_none(value))
279}
280
281#[inline]
285fn fmt_component_v3<V>(
286 output: &mut (impl io::Write + ?Sized),
287 value: &V,
288 state: &mut <V as ComponentProvider>::State,
289 component: &format_description_v3::Component,
290) -> Result<usize, error::Format>
291where
292 V: ComponentProvider,
293{
294 use format_description_v3::Component::*;
295
296 use crate::formatting::*;
297
298 match component {
299 Day(modifier) if V::SUPPLIES_DATE => fmt_day(output, value.day(state), *modifier),
300 MonthShort(modifier) if V::SUPPLIES_DATE => {
301 fmt_month_short(output, value.month(state), *modifier)
302 }
303 MonthLong(modifier) if V::SUPPLIES_DATE => {
304 fmt_month_long(output, value.month(state), *modifier)
305 }
306 MonthNumerical(modifier) if V::SUPPLIES_DATE => {
307 fmt_month_numerical(output, value.month(state), *modifier)
308 }
309 Ordinal(modifier) if V::SUPPLIES_DATE => {
310 fmt_ordinal(output, value.ordinal(state), *modifier)
311 }
312 WeekdayShort(modifier) if V::SUPPLIES_DATE => {
313 fmt_weekday_short(output, value.weekday(state), *modifier)
314 }
315 WeekdayLong(modifier) if V::SUPPLIES_DATE => {
316 fmt_weekday_long(output, value.weekday(state), *modifier)
317 }
318 WeekdaySunday(modifier) if V::SUPPLIES_DATE => {
319 fmt_weekday_sunday(output, value.weekday(state), *modifier)
320 }
321 WeekdayMonday(modifier) if V::SUPPLIES_DATE => {
322 fmt_weekday_monday(output, value.weekday(state), *modifier)
323 }
324 WeekNumberIso(modifier) if V::SUPPLIES_DATE => {
325 fmt_week_number_iso(output, value.iso_week_number(state), *modifier)
326 }
327 WeekNumberSunday(modifier) if V::SUPPLIES_DATE => {
328 fmt_week_number_sunday(output, value.sunday_based_week(state), *modifier)
329 }
330 WeekNumberMonday(modifier) if V::SUPPLIES_DATE => {
331 fmt_week_number_monday(output, value.monday_based_week(state), *modifier)
332 }
333 CalendarYearFullExtendedRange(modifier) if V::SUPPLIES_DATE => {
334 fmt_calendar_year_full_extended_range(output, value.calendar_year(state), *modifier)
335 }
336 CalendarYearFullStandardRange(modifier) if V::SUPPLIES_DATE => {
337 fmt_calendar_year_full_standard_range(
338 output,
339 try_likely_ok!(
340 value
341 .calendar_year(state)
342 .narrow::<-9_999, 9_999>()
343 .ok_or_else(|| error::ComponentRange::conditional("year"))
344 )
345 .into(),
346 *modifier,
347 )
348 }
349 IsoYearFullExtendedRange(modifier) if V::SUPPLIES_DATE => {
350 fmt_iso_year_full_extended_range(output, value.iso_year(state), *modifier)
351 }
352 IsoYearFullStandardRange(modifier) if V::SUPPLIES_DATE => fmt_iso_year_full_standard_range(
353 output,
354 try_likely_ok!(
355 value
356 .iso_year(state)
357 .narrow::<-9_999, 9_999>()
358 .ok_or_else(|| error::ComponentRange::conditional("year"))
359 )
360 .into(),
361 *modifier,
362 ),
363 CalendarYearCenturyExtendedRange(modifier) if V::SUPPLIES_DATE => {
364 let year = value.calendar_year(state);
365 let century = unsafe { ri16::new_unchecked((year.get() / 100).truncate()) };
368 fmt_calendar_year_century_extended_range(output, century, year.is_negative(), *modifier)
369 }
370 CalendarYearCenturyStandardRange(modifier) if V::SUPPLIES_DATE => {
371 let year = value.calendar_year(state);
372 let is_negative = year.is_negative();
373 let year = unsafe { ri16::<0, 9_999>::new_unchecked((year.get() / 100).truncate()) };
376 fmt_calendar_year_century_standard_range(
377 output,
378 year.narrow::<0, 99>()
379 .ok_or_else(|| error::ComponentRange::conditional("year"))?
380 .into(),
381 is_negative,
382 *modifier,
383 )
384 }
385 IsoYearCenturyExtendedRange(modifier) if V::SUPPLIES_DATE => {
386 let year = value.iso_year(state);
387 let century = unsafe { ri16::new_unchecked((year.get() / 100).truncate()) };
390 fmt_iso_year_century_extended_range(output, century, year.is_negative(), *modifier)
391 }
392 IsoYearCenturyStandardRange(modifier) if V::SUPPLIES_DATE => {
393 let year = value.iso_year(state);
394 let year = unsafe { ri16::<0, 9_999>::new_unchecked((year.get() / 100).truncate()) };
397 fmt_iso_year_century_standard_range(
398 output,
399 year.narrow::<0, 99>()
400 .ok_or_else(|| error::ComponentRange::conditional("year"))?
401 .into(),
402 year.is_negative(),
403 *modifier,
404 )
405 }
406 CalendarYearLastTwo(modifier) if V::SUPPLIES_DATE => {
407 let last_two = unsafe {
410 ru8::new_unchecked(
411 (value.calendar_year(state).get().unsigned_abs() % 100).truncate(),
412 )
413 };
414 fmt_calendar_year_last_two(output, last_two, *modifier)
415 }
416 IsoYearLastTwo(modifier) if V::SUPPLIES_DATE => {
417 let last_two = unsafe {
420 ru8::new_unchecked((value.iso_year(state).get().unsigned_abs() % 100).truncate())
421 };
422 fmt_iso_year_last_two(output, last_two, *modifier)
423 }
424 Hour12(modifier) if V::SUPPLIES_TIME => fmt_hour_12(output, value.hour(state), *modifier),
425 Hour24(modifier) if V::SUPPLIES_TIME => fmt_hour_24(output, value.hour(state), *modifier),
426 Minute(modifier) if V::SUPPLIES_TIME => fmt_minute(output, value.minute(state), *modifier),
427 Period(modifier) if V::SUPPLIES_TIME => fmt_period(output, value.period(state), *modifier),
428 Second(modifier) if V::SUPPLIES_TIME => fmt_second(output, value.second(state), *modifier),
429 Subsecond(modifier) if V::SUPPLIES_TIME => {
430 fmt_subsecond(output, value.nanosecond(state), *modifier)
431 }
432 OffsetHour(modifier) if V::SUPPLIES_OFFSET => fmt_offset_hour(
433 output,
434 value.offset_is_negative(state),
435 value.offset_hour(state),
436 *modifier,
437 ),
438 OffsetMinute(modifier) if V::SUPPLIES_OFFSET => {
439 fmt_offset_minute(output, value.offset_minute(state), *modifier)
440 }
441 OffsetSecond(modifier) if V::SUPPLIES_OFFSET => {
442 fmt_offset_second(output, value.offset_second(state), *modifier)
443 }
444 Ignore(_) => return Ok(0),
445 UnixTimestampSecond(modifier) if V::SUPPLIES_TIMESTAMP => {
446 fmt_unix_timestamp_second(output, value.unix_timestamp_seconds(state), *modifier)
447 }
448 UnixTimestampMillisecond(modifier) if V::SUPPLIES_TIMESTAMP => {
449 fmt_unix_timestamp_millisecond(
450 output,
451 value.unix_timestamp_milliseconds(state),
452 *modifier,
453 )
454 }
455 UnixTimestampMicrosecond(modifier) if V::SUPPLIES_TIMESTAMP => {
456 fmt_unix_timestamp_microsecond(
457 output,
458 value.unix_timestamp_microseconds(state),
459 *modifier,
460 )
461 }
462 UnixTimestampNanosecond(modifier) if V::SUPPLIES_TIMESTAMP => {
463 fmt_unix_timestamp_nanosecond(
464 output,
465 value.unix_timestamp_nanoseconds(state),
466 *modifier,
467 )
468 }
469 End(modifier::End { trailing_input: _ }) => return Ok(0),
470
471 #[allow(unreachable_patterns)]
476 Day(_)
477 | MonthShort(_)
478 | MonthLong(_)
479 | MonthNumerical(_)
480 | Ordinal(_)
481 | WeekdayShort(_)
482 | WeekdayLong(_)
483 | WeekdaySunday(_)
484 | WeekdayMonday(_)
485 | WeekNumberIso(_)
486 | WeekNumberSunday(_)
487 | WeekNumberMonday(_)
488 | CalendarYearFullExtendedRange(_)
489 | CalendarYearFullStandardRange(_)
490 | IsoYearFullExtendedRange(_)
491 | IsoYearFullStandardRange(_)
492 | CalendarYearCenturyExtendedRange(_)
493 | CalendarYearCenturyStandardRange(_)
494 | IsoYearCenturyExtendedRange(_)
495 | IsoYearCenturyStandardRange(_)
496 | CalendarYearLastTwo(_)
497 | IsoYearLastTwo(_)
498 | Hour12(_)
499 | Hour24(_)
500 | Minute(_)
501 | Period(_)
502 | Second(_)
503 | Subsecond(_)
504 | OffsetHour(_)
505 | OffsetMinute(_)
506 | OffsetSecond(_)
507 | Ignore(_)
508 | UnixTimestampSecond(_)
509 | UnixTimestampMillisecond(_)
510 | UnixTimestampMicrosecond(_)
511 | UnixTimestampNanosecond(_)
512 | End(_) => return Err(error::Format::InsufficientTypeInformation),
513 }
514 .map_err(Into::into)
515}
516
517#[inline]
519fn fmt_day(
520 output: &mut (impl io::Write + ?Sized),
521 day: Day,
522 modifier::Day { padding }: modifier::Day,
523) -> Result<usize, io::Error> {
524 format_two_digits(output, day.expand(), padding)
525}
526
527#[inline]
529fn fmt_month_short(
530 output: &mut (impl io::Write + ?Sized),
531 month: Month,
532 modifier::MonthShort {
533 case_sensitive: _, }: modifier::MonthShort,
535) -> io::Result<usize> {
536 write(output, unsafe {
538 MONTH_NAMES[u8::from(month).extend::<usize>() - 1].get_unchecked(..3)
539 })
540}
541
542#[inline]
544fn fmt_month_long(
545 output: &mut (impl io::Write + ?Sized),
546 month: Month,
547 modifier::MonthLong {
548 case_sensitive: _, }: modifier::MonthLong,
550) -> io::Result<usize> {
551 write(output, MONTH_NAMES[u8::from(month).extend::<usize>() - 1])
552}
553
554#[inline]
556fn fmt_month_numerical(
557 output: &mut (impl io::Write + ?Sized),
558 month: Month,
559 modifier::MonthNumerical { padding }: modifier::MonthNumerical,
560) -> io::Result<usize> {
561 format_two_digits(
562 output,
563 unsafe { ru8::new_unchecked(u8::from(month)) },
565 padding,
566 )
567}
568
569#[inline]
571fn fmt_ordinal(
572 output: &mut (impl io::Write + ?Sized),
573 ordinal: Ordinal,
574 modifier::Ordinal { padding }: modifier::Ordinal,
575) -> Result<usize, io::Error> {
576 format_three_digits(output, ordinal.expand(), padding)
577}
578
579#[inline]
581fn fmt_weekday_short(
582 output: &mut (impl io::Write + ?Sized),
583 weekday: Weekday,
584 modifier::WeekdayShort {
585 case_sensitive: _, }: modifier::WeekdayShort,
587) -> io::Result<usize> {
588 write(output, unsafe {
590 WEEKDAY_NAMES[weekday.number_days_from_monday().extend::<usize>()].get_unchecked(..3)
591 })
592}
593
594#[inline]
596fn fmt_weekday_long(
597 output: &mut (impl io::Write + ?Sized),
598 weekday: Weekday,
599 modifier::WeekdayLong {
600 case_sensitive: _, }: modifier::WeekdayLong,
602) -> io::Result<usize> {
603 write(
604 output,
605 WEEKDAY_NAMES[weekday.number_days_from_monday().extend::<usize>()],
606 )
607}
608
609#[inline]
612fn fmt_weekday_sunday(
613 output: &mut (impl io::Write + ?Sized),
614 weekday: Weekday,
615 modifier::WeekdaySunday { one_indexed }: modifier::WeekdaySunday,
616) -> io::Result<usize> {
617 format_single_digit(output, unsafe {
619 ru8::new_unchecked(weekday.number_days_from_sunday() + u8::from(one_indexed))
620 })
621}
622
623#[inline]
626fn fmt_weekday_monday(
627 output: &mut (impl io::Write + ?Sized),
628 weekday: Weekday,
629 modifier::WeekdayMonday { one_indexed }: modifier::WeekdayMonday,
630) -> io::Result<usize> {
631 format_single_digit(output, unsafe {
633 ru8::new_unchecked(weekday.number_days_from_monday() + u8::from(one_indexed))
634 })
635}
636
637#[inline]
638fn fmt_week_number_iso(
639 output: &mut (impl io::Write + ?Sized),
640 week_number: IsoWeekNumber,
641 modifier::WeekNumberIso { padding }: modifier::WeekNumberIso,
642) -> io::Result<usize> {
643 format_two_digits(output, week_number.expand(), padding)
644}
645
646#[inline]
647fn fmt_week_number_sunday(
648 output: &mut (impl io::Write + ?Sized),
649 week_number: SundayBasedWeek,
650 modifier::WeekNumberSunday { padding }: modifier::WeekNumberSunday,
651) -> io::Result<usize> {
652 format_two_digits(output, week_number.expand(), padding)
653}
654
655#[inline]
656fn fmt_week_number_monday(
657 output: &mut (impl io::Write + ?Sized),
658 week_number: MondayBasedWeek,
659 modifier::WeekNumberMonday { padding }: modifier::WeekNumberMonday,
660) -> io::Result<usize> {
661 format_two_digits(output, week_number.expand(), padding)
662}
663
664#[inline]
665fn fmt_calendar_year_full_extended_range(
666 output: &mut (impl io::Write + ?Sized),
667 full_year: Year,
668 modifier::CalendarYearFullExtendedRange {
669 padding,
670 sign_is_mandatory,
671 }: modifier::CalendarYearFullExtendedRange,
672) -> io::Result<usize> {
673 let mut bytes = 0;
674 bytes += try_likely_ok!(fmt_sign(
675 output,
676 full_year.is_negative(),
677 sign_is_mandatory || full_year.get() >= 10_000,
678 ));
679 let value: ru32<0, 999_999> =
682 unsafe { full_year.abs().narrow_unchecked::<0, 999_999>().into() };
683
684 bytes += if let Some(value) = value.narrow::<0, 9_999>() {
685 try_likely_ok!(format_four_digits(output, value.into(), padding))
686 } else if let Some(value) = value.narrow::<0, 99_999>() {
687 try_likely_ok!(format_five_digits_pad_zero(output, value))
688 } else {
689 try_likely_ok!(format_six_digits_pad_zero(output, value))
690 };
691 Ok(bytes)
692}
693
694#[inline]
695fn fmt_calendar_year_full_standard_range(
696 output: &mut (impl io::Write + ?Sized),
697 full_year: StandardYear,
698 modifier::CalendarYearFullStandardRange {
699 padding,
700 sign_is_mandatory,
701 }: modifier::CalendarYearFullStandardRange,
702) -> io::Result<usize> {
703 let mut bytes = 0;
704 bytes += try_likely_ok!(fmt_sign(output, full_year.is_negative(), sign_is_mandatory));
705 bytes += try_likely_ok!(format_four_digits(
707 output,
708 unsafe { full_year.abs().narrow_unchecked::<0, 9_999>().into() },
709 padding
710 ));
711 Ok(bytes)
712}
713
714#[inline]
715fn fmt_iso_year_full_extended_range(
716 output: &mut (impl io::Write + ?Sized),
717 full_year: Year,
718 modifier::IsoYearFullExtendedRange {
719 padding,
720 sign_is_mandatory,
721 }: modifier::IsoYearFullExtendedRange,
722) -> io::Result<usize> {
723 let mut bytes = 0;
724 bytes += try_likely_ok!(fmt_sign(
725 output,
726 full_year.is_negative(),
727 sign_is_mandatory || full_year.get() >= 10_000,
728 ));
729 let value: ru32<0, 999_999> =
731 unsafe { full_year.abs().narrow_unchecked::<0, 999_999>().into() };
732
733 bytes += if let Some(value) = value.narrow::<0, 9_999>() {
734 try_likely_ok!(format_four_digits(output, value.into(), padding))
735 } else if let Some(value) = value.narrow::<0, 99_999>() {
736 try_likely_ok!(format_five_digits_pad_zero(output, value))
737 } else {
738 try_likely_ok!(format_six_digits_pad_zero(output, value))
739 };
740 Ok(bytes)
741}
742
743#[inline]
744fn fmt_iso_year_full_standard_range(
745 output: &mut (impl io::Write + ?Sized),
746 year: StandardYear,
747 modifier::IsoYearFullStandardRange {
748 padding,
749 sign_is_mandatory,
750 }: modifier::IsoYearFullStandardRange,
751) -> io::Result<usize> {
752 let mut bytes = 0;
753 bytes += try_likely_ok!(fmt_sign(output, year.is_negative(), sign_is_mandatory));
754 bytes += try_likely_ok!(format_four_digits(
756 output,
757 unsafe { year.abs().narrow_unchecked::<0, 9_999>().into() },
758 padding
759 ));
760 Ok(bytes)
761}
762
763#[inline]
764fn fmt_calendar_year_century_extended_range(
765 output: &mut (impl io::Write + ?Sized),
766 century: ExtendedCentury,
767 is_negative: bool,
768 modifier::CalendarYearCenturyExtendedRange {
769 padding,
770 sign_is_mandatory,
771 }: modifier::CalendarYearCenturyExtendedRange,
772) -> io::Result<usize> {
773 let mut bytes = 0;
774 bytes += try_likely_ok!(fmt_sign(
775 output,
776 is_negative,
777 sign_is_mandatory || century.get() >= 100,
778 ));
779 let century: ru16<0, 9_999> = unsafe { century.abs().narrow_unchecked::<0, 9_999>().into() };
781
782 bytes += if let Some(century) = century.narrow::<0, 99>() {
783 try_likely_ok!(format_two_digits(output, century.into(), padding))
784 } else if let Some(century) = century.narrow::<0, 999>() {
785 try_likely_ok!(format_three_digits(output, century, padding))
786 } else {
787 try_likely_ok!(format_four_digits(output, century, padding))
788 };
789 Ok(bytes)
790}
791
792#[inline]
793fn fmt_calendar_year_century_standard_range(
794 output: &mut (impl io::Write + ?Sized),
795 century: StandardCentury,
796 is_negative: bool,
797 modifier::CalendarYearCenturyStandardRange {
798 padding,
799 sign_is_mandatory,
800 }: modifier::CalendarYearCenturyStandardRange,
801) -> io::Result<usize> {
802 let mut bytes = 0;
803 bytes += try_likely_ok!(fmt_sign(output, is_negative, sign_is_mandatory));
804 let century = unsafe { century.abs().narrow_unchecked::<0, 99>() };
806 bytes += try_likely_ok!(format_two_digits(output, century.into(), padding));
807 Ok(bytes)
808}
809
810#[inline]
811fn fmt_iso_year_century_extended_range(
812 output: &mut (impl io::Write + ?Sized),
813 century: ExtendedCentury,
814 is_negative: bool,
815 modifier::IsoYearCenturyExtendedRange {
816 padding,
817 sign_is_mandatory,
818 }: modifier::IsoYearCenturyExtendedRange,
819) -> io::Result<usize> {
820 let mut bytes = 0;
821 bytes += try_likely_ok!(fmt_sign(
822 output,
823 is_negative,
824 sign_is_mandatory || century.get() >= 100,
825 ));
826 let century: ru16<0, 9_999> = unsafe { century.abs().narrow_unchecked::<0, 9_999>().into() };
828
829 bytes += if let Some(century) = century.narrow::<0, 99>() {
830 try_likely_ok!(format_two_digits(output, century.into(), padding))
831 } else if let Some(century) = century.narrow::<0, 999>() {
832 try_likely_ok!(format_three_digits(output, century, padding))
833 } else {
834 try_likely_ok!(format_four_digits(output, century, padding))
835 };
836 Ok(bytes)
837}
838
839#[inline]
840fn fmt_iso_year_century_standard_range(
841 output: &mut (impl io::Write + ?Sized),
842 century: StandardCentury,
843 is_negative: bool,
844 modifier::IsoYearCenturyStandardRange {
845 padding,
846 sign_is_mandatory,
847 }: modifier::IsoYearCenturyStandardRange,
848) -> io::Result<usize> {
849 let mut bytes = 0;
850 bytes += try_likely_ok!(fmt_sign(output, is_negative, sign_is_mandatory));
851 let century = unsafe { century.abs().narrow_unchecked::<0, 99>() };
853 bytes += try_likely_ok!(format_two_digits(output, century.into(), padding));
854 Ok(bytes)
855}
856
857#[inline]
858fn fmt_calendar_year_last_two(
859 output: &mut (impl io::Write + ?Sized),
860 last_two: LastTwo,
861 modifier::CalendarYearLastTwo { padding }: modifier::CalendarYearLastTwo,
862) -> io::Result<usize> {
863 format_two_digits(output, last_two, padding)
864}
865
866#[inline]
867fn fmt_iso_year_last_two(
868 output: &mut (impl io::Write + ?Sized),
869 last_two: LastTwo,
870 modifier::IsoYearLastTwo { padding }: modifier::IsoYearLastTwo,
871) -> io::Result<usize> {
872 format_two_digits(output, last_two, padding)
873}
874
875#[inline]
877fn fmt_hour_12(
878 output: &mut (impl io::Write + ?Sized),
879 hour: Hours,
880 modifier::Hour12 { padding }: modifier::Hour12,
881) -> io::Result<usize> {
882 format_two_digits(
884 output,
885 unsafe { ru8::new_unchecked((hour.get() + 11) % 12 + 1) },
886 padding,
887 )
888}
889
890#[inline]
892fn fmt_hour_24(
893 output: &mut (impl io::Write + ?Sized),
894 hour: Hours,
895 modifier::Hour24 { padding }: modifier::Hour24,
896) -> io::Result<usize> {
897 format_two_digits(output, hour.expand(), padding)
898}
899
900#[inline]
902fn fmt_minute(
903 output: &mut (impl io::Write + ?Sized),
904 minute: Minutes,
905 modifier::Minute { padding }: modifier::Minute,
906) -> Result<usize, io::Error> {
907 format_two_digits(output, minute.expand(), padding)
908}
909
910#[inline]
912fn fmt_period(
913 output: &mut (impl io::Write + ?Sized),
914 period: Period,
915 modifier::Period {
916 is_uppercase,
917 case_sensitive: _, }: modifier::Period,
919) -> Result<usize, io::Error> {
920 write(
921 output,
922 match (period, is_uppercase) {
923 (Period::Am, false) => "am",
924 (Period::Am, true) => "AM",
925 (Period::Pm, false) => "pm",
926 (Period::Pm, true) => "PM",
927 },
928 )
929}
930
931#[inline]
933fn fmt_second(
934 output: &mut (impl io::Write + ?Sized),
935 second: Seconds,
936 modifier::Second { padding }: modifier::Second,
937) -> Result<usize, io::Error> {
938 format_two_digits(output, second.expand(), padding)
939}
940
941#[inline]
943fn fmt_subsecond(
944 output: &mut (impl io::Write + ?Sized),
945 nanos: Nanoseconds,
946 modifier::Subsecond { digits }: modifier::Subsecond,
947) -> Result<usize, io::Error> {
948 use modifier::SubsecondDigits::*;
949
950 #[repr(C, align(8))]
951 #[derive(Clone, Copy)]
952 struct Digits {
953 _padding: MaybeUninit<[u8; 7]>,
954 digit_1: u8,
955 digits_2_thru_9: [u8; 8],
956 }
957
958 let [
959 digit_1,
960 digits_2_and_3,
961 digits_4_and_5,
962 digits_6_and_7,
963 digits_8_and_9,
964 ] = num_fmt::subsecond_from_nanos(nanos);
965
966 let buf = Digits {
969 _padding: MaybeUninit::uninit(),
970 digit_1: digit_1.as_bytes()[0],
971 digits_2_thru_9: [
972 digits_2_and_3.as_bytes()[0],
973 digits_2_and_3.as_bytes()[1],
974 digits_4_and_5.as_bytes()[0],
975 digits_4_and_5.as_bytes()[1],
976 digits_6_and_7.as_bytes()[0],
977 digits_6_and_7.as_bytes()[1],
978 digits_8_and_9.as_bytes()[0],
979 digits_8_and_9.as_bytes()[1],
980 ],
981 };
982
983 let len = match digits {
984 One => 1,
985 Two => 2,
986 Three => 3,
987 Four => 4,
988 Five => 5,
989 Six => 6,
990 Seven => 7,
991 Eight => 8,
992 Nine => 9,
993 OneOrMore => {
994 let bitmask = u64::from_le_bytes(buf.digits_2_thru_9) ^ u64::from_le_bytes([b'0'; 8]);
998 let digits_to_truncate = bitmask.leading_zeros() / 8;
999 9 - digits_to_truncate as usize
1000 }
1001 };
1002
1003 let s = unsafe {
1007 num_fmt::StackStr::new(
1008 *(&raw const buf)
1009 .byte_add(core::mem::offset_of!(Digits, digit_1))
1010 .cast::<[MaybeUninit<u8>; 9]>(),
1011 len,
1012 )
1013 };
1014 write(output, &s)
1015}
1016
1017#[inline]
1018fn fmt_sign(
1019 output: &mut (impl io::Write + ?Sized),
1020 is_negative: bool,
1021 sign_is_mandatory: bool,
1022) -> Result<usize, io::Error> {
1023 if is_negative {
1024 write(output, "-")
1025 } else if sign_is_mandatory {
1026 write(output, "+")
1027 } else {
1028 Ok(0)
1029 }
1030}
1031
1032#[inline]
1034fn fmt_offset_hour(
1035 output: &mut (impl io::Write + ?Sized),
1036 is_negative: bool,
1037 hour: OffsetHours,
1038 modifier::OffsetHour {
1039 padding,
1040 sign_is_mandatory,
1041 }: modifier::OffsetHour,
1042) -> Result<usize, io::Error> {
1043 let mut bytes = 0;
1044 bytes += try_likely_ok!(fmt_sign(output, is_negative, sign_is_mandatory));
1045 bytes += try_likely_ok!(format_two_digits(
1047 output,
1048 unsafe { ru8::new_unchecked(hour.get().unsigned_abs()) },
1049 padding,
1050 ));
1051 Ok(bytes)
1052}
1053
1054#[inline]
1056fn fmt_offset_minute(
1057 output: &mut (impl io::Write + ?Sized),
1058 offset_minute: OffsetMinutes,
1059 modifier::OffsetMinute { padding }: modifier::OffsetMinute,
1060) -> Result<usize, io::Error> {
1061 format_two_digits(
1062 output,
1063 unsafe { ru8::new_unchecked(offset_minute.get().unsigned_abs()) },
1066 padding,
1067 )
1068}
1069
1070#[inline]
1072fn fmt_offset_second(
1073 output: &mut (impl io::Write + ?Sized),
1074 offset_second: OffsetSeconds,
1075 modifier::OffsetSecond { padding }: modifier::OffsetSecond,
1076) -> Result<usize, io::Error> {
1077 format_two_digits(
1078 output,
1079 unsafe { ru8::new_unchecked(offset_second.get().unsigned_abs()) },
1082 padding,
1083 )
1084}
1085
1086#[inline]
1088fn fmt_unix_timestamp_second(
1089 output: &mut (impl io::Write + ?Sized),
1090 timestamp: i64,
1091 modifier::UnixTimestampSecond { sign_is_mandatory }: modifier::UnixTimestampSecond,
1092) -> Result<usize, io::Error> {
1093 let mut bytes = 0;
1094 bytes += try_likely_ok!(fmt_sign(output, timestamp < 0, sign_is_mandatory));
1095 bytes += try_likely_ok!(format_u64_pad_none(output, timestamp.unsigned_abs()));
1096 Ok(bytes)
1097}
1098
1099#[inline]
1101fn fmt_unix_timestamp_millisecond(
1102 output: &mut (impl io::Write + ?Sized),
1103 timestamp_millis: i64,
1104 modifier::UnixTimestampMillisecond { sign_is_mandatory }: modifier::UnixTimestampMillisecond,
1105) -> Result<usize, io::Error> {
1106 let mut bytes = 0;
1107 bytes += try_likely_ok!(fmt_sign(output, timestamp_millis < 0, sign_is_mandatory));
1108 bytes += try_likely_ok!(format_u64_pad_none(output, timestamp_millis.unsigned_abs()));
1109 Ok(bytes)
1110}
1111
1112#[inline]
1114fn fmt_unix_timestamp_microsecond(
1115 output: &mut (impl io::Write + ?Sized),
1116 timestamp_micros: i128,
1117 modifier::UnixTimestampMicrosecond { sign_is_mandatory }: modifier::UnixTimestampMicrosecond,
1118) -> Result<usize, io::Error> {
1119 let mut bytes = 0;
1120 bytes += try_likely_ok!(fmt_sign(output, timestamp_micros < 0, sign_is_mandatory));
1121 bytes += try_likely_ok!(format_u128_pad_none(
1122 output,
1123 timestamp_micros.unsigned_abs()
1124 ));
1125 Ok(bytes)
1126}
1127
1128#[inline]
1130fn fmt_unix_timestamp_nanosecond(
1131 output: &mut (impl io::Write + ?Sized),
1132 timestamp_nanos: i128,
1133 modifier::UnixTimestampNanosecond { sign_is_mandatory }: modifier::UnixTimestampNanosecond,
1134) -> Result<usize, io::Error> {
1135 let mut bytes = 0;
1136 bytes += try_likely_ok!(fmt_sign(output, timestamp_nanos < 0, sign_is_mandatory));
1137 bytes += try_likely_ok!(format_u128_pad_none(output, timestamp_nanos.unsigned_abs()));
1138 Ok(bytes)
1139}