1mod component_provider;
4pub(crate) mod formattable;
5mod iso8601;
6
7use core::num::NonZero;
8use std::io;
9
10use num_conv::prelude::*;
11
12use self::component_provider::ComponentProvider;
13pub use self::formattable::Formattable;
14use crate::ext::DigitCount;
15use crate::format_description::{Component, Period, modifier};
16use crate::internal_macros::try_likely_ok;
17use crate::{Month, Weekday, error};
18
19const MONTH_NAMES: [&[u8]; 12] = [
20 b"January",
21 b"February",
22 b"March",
23 b"April",
24 b"May",
25 b"June",
26 b"July",
27 b"August",
28 b"September",
29 b"October",
30 b"November",
31 b"December",
32];
33
34const WEEKDAY_NAMES: [&[u8]; 7] = [
35 b"Monday",
36 b"Tuesday",
37 b"Wednesday",
38 b"Thursday",
39 b"Friday",
40 b"Saturday",
41 b"Sunday",
42];
43
44#[inline]
46pub(crate) fn write(output: &mut (impl io::Write + ?Sized), bytes: &[u8]) -> io::Result<usize> {
47 output.write_all(bytes)?;
48 Ok(bytes.len())
49}
50
51#[inline]
53pub(crate) fn write_if(
54 output: &mut (impl io::Write + ?Sized),
55 pred: bool,
56 bytes: &[u8],
57) -> io::Result<usize> {
58 if pred { write(output, bytes) } else { Ok(0) }
59}
60
61#[inline]
63pub(crate) fn write_if_else(
64 output: &mut (impl io::Write + ?Sized),
65 pred: bool,
66 true_bytes: &[u8],
67 false_bytes: &[u8],
68) -> io::Result<usize> {
69 write(output, if pred { true_bytes } else { false_bytes })
70}
71
72#[inline]
77fn f64_10_pow_x(x: NonZero<u8>) -> f64 {
78 match x.get() {
79 1 => 10.,
80 2 => 100.,
81 3 => 1_000.,
82 4 => 10_000.,
83 5 => 100_000.,
84 6 => 1_000_000.,
85 7 => 10_000_000.,
86 8 => 100_000_000.,
87 9 => 1_000_000_000.,
88 x => 10_f64.powi(x.cast_signed().extend()),
89 }
90}
91
92#[inline]
97pub(crate) fn format_float(
98 output: &mut (impl io::Write + ?Sized),
99 mut value: f64,
100 digits_before_decimal: u8,
101 digits_after_decimal: Option<NonZero<u8>>,
102) -> io::Result<usize> {
103 match digits_after_decimal {
104 Some(digits_after_decimal) => {
105 if digits_after_decimal.get() < 9 {
118 let trunc_num = f64_10_pow_x(digits_after_decimal);
119 value = f64::trunc(value * trunc_num) / trunc_num;
120 }
121
122 let digits_after_decimal = digits_after_decimal.get().extend();
123 let width = digits_before_decimal.extend::<usize>() + 1 + digits_after_decimal;
124 write!(output, "{value:0>width$.digits_after_decimal$}")?;
125 Ok(width)
126 }
127 None => {
128 let value = value.trunc() as u64;
129 let width = digits_before_decimal.extend();
130 write!(output, "{value:0>width$}")?;
131 Ok(width)
132 }
133 }
134}
135
136#[inline]
140pub(crate) fn format_number<const WIDTH: u8>(
141 output: &mut (impl io::Write + ?Sized),
142 value: impl itoa::Integer + DigitCount + Copy,
143 padding: modifier::Padding,
144) -> Result<usize, io::Error> {
145 match padding {
146 modifier::Padding::Space => format_number_pad_space::<WIDTH>(output, value),
147 modifier::Padding::Zero => format_number_pad_zero::<WIDTH>(output, value),
148 modifier::Padding::None => format_number_pad_none(output, value),
149 }
150}
151
152#[inline]
156pub(crate) fn format_number_pad_space<const WIDTH: u8>(
157 output: &mut (impl io::Write + ?Sized),
158 value: impl itoa::Integer + DigitCount + Copy,
159) -> Result<usize, io::Error> {
160 let mut bytes = 0;
161 for _ in 0..(WIDTH.saturating_sub(value.num_digits())) {
162 bytes += write(output, b" ")?;
163 }
164 bytes += write(output, itoa::Buffer::new().format(value).as_bytes())?;
165 Ok(bytes)
166}
167
168#[inline]
172pub(crate) fn format_number_pad_zero<const WIDTH: u8>(
173 output: &mut (impl io::Write + ?Sized),
174 value: impl itoa::Integer + DigitCount + Copy,
175) -> Result<usize, io::Error> {
176 let mut bytes = 0;
177 for _ in 0..(WIDTH.saturating_sub(value.num_digits())) {
178 bytes += write(output, b"0")?;
179 }
180 bytes += write(output, itoa::Buffer::new().format(value).as_bytes())?;
181 Ok(bytes)
182}
183
184#[inline]
188pub(crate) fn format_number_pad_none(
189 output: &mut (impl io::Write + ?Sized),
190 value: impl itoa::Integer + Copy,
191) -> Result<usize, io::Error> {
192 write(output, itoa::Buffer::new().format(value).as_bytes())
193}
194
195#[inline]
199pub(crate) fn format_component<V>(
200 output: &mut (impl io::Write + ?Sized),
201 component: Component,
202 value: &V,
203 state: &mut V::State,
204) -> Result<usize, error::Format>
205where
206 V: ComponentProvider,
207{
208 use Component::*;
209 Ok(match component {
210 Day(modifier) if V::SUPPLIES_DATE => {
211 try_likely_ok!(fmt_day(output, value.day(state), modifier))
212 }
213 Month(modifier) if V::SUPPLIES_DATE => {
214 try_likely_ok!(fmt_month(output, value.month(state), modifier))
215 }
216 Ordinal(modifier) if V::SUPPLIES_DATE => {
217 try_likely_ok!(fmt_ordinal(output, value.ordinal(state), modifier))
218 }
219 Weekday(modifier) if V::SUPPLIES_DATE => {
220 try_likely_ok!(fmt_weekday(output, value.weekday(state), modifier))
221 }
222 WeekNumber(modifier) if V::SUPPLIES_DATE => try_likely_ok!(fmt_week_number(
223 output,
224 match modifier.repr {
225 modifier::WeekNumberRepr::Iso => value.iso_week_number(state),
226 modifier::WeekNumberRepr::Sunday => value.sunday_based_week(state),
227 modifier::WeekNumberRepr::Monday => value.monday_based_week(state),
228 },
229 modifier,
230 )),
231 Year(modifier) if V::SUPPLIES_DATE => try_likely_ok!(fmt_year(
232 output,
233 if modifier.iso_week_based {
234 value.iso_year(state)
235 } else {
236 value.calendar_year(state)
237 },
238 modifier,
239 )),
240 Hour(modifier) if V::SUPPLIES_TIME => {
241 try_likely_ok!(fmt_hour(output, value.hour(state), modifier))
242 }
243 Minute(modifier) if V::SUPPLIES_TIME => {
244 try_likely_ok!(fmt_minute(output, value.minute(state), modifier))
245 }
246 Period(modifier) if V::SUPPLIES_TIME => {
247 try_likely_ok!(fmt_period(output, value.period(state), modifier))
248 }
249 Second(modifier) if V::SUPPLIES_TIME => {
250 try_likely_ok!(fmt_second(output, value.second(state), modifier))
251 }
252 Subsecond(modifier) if V::SUPPLIES_TIME => {
253 try_likely_ok!(fmt_subsecond(output, value.nanosecond(state), modifier))
254 }
255 OffsetHour(modifier) if V::SUPPLIES_OFFSET => try_likely_ok!(fmt_offset_hour(
256 output,
257 value.offset_is_negative(state),
258 value.offset_hour(state),
259 modifier,
260 )),
261 OffsetMinute(modifier) if V::SUPPLIES_OFFSET => try_likely_ok!(fmt_offset_minute(
262 output,
263 value.offset_minute(state),
264 modifier
265 )),
266 OffsetSecond(modifier) if V::SUPPLIES_OFFSET => try_likely_ok!(fmt_offset_second(
267 output,
268 value.offset_second(state),
269 modifier
270 )),
271 Ignore(_) => 0,
272 UnixTimestamp(modifier) if V::SUPPLIES_TIMESTAMP => match modifier.precision {
273 modifier::UnixTimestampPrecision::Second => try_likely_ok!(fmt_unix_timestamp_seconds(
274 output,
275 value.unix_timestamp_seconds(state),
276 modifier,
277 )),
278 modifier::UnixTimestampPrecision::Millisecond => {
279 try_likely_ok!(fmt_unix_timestamp_milliseconds(
280 output,
281 value.unix_timestamp_milliseconds(state),
282 modifier,
283 ))
284 }
285 modifier::UnixTimestampPrecision::Microsecond => {
286 try_likely_ok!(fmt_unix_timestamp_microseconds(
287 output,
288 value.unix_timestamp_microseconds(state),
289 modifier,
290 ))
291 }
292 modifier::UnixTimestampPrecision::Nanosecond => {
293 try_likely_ok!(fmt_unix_timestamp_nanoseconds(
294 output,
295 value.unix_timestamp_nanoseconds(state),
296 modifier,
297 ))
298 }
299 },
300 End(modifier::End { trailing_input: _ }) => 0,
301
302 #[allow(unreachable_patterns)]
307 Day(_) | Month(_) | Ordinal(_) | Weekday(_) | WeekNumber(_) | Year(_) | Hour(_)
308 | Minute(_) | Period(_) | Second(_) | Subsecond(_) | OffsetHour(_) | OffsetMinute(_)
309 | OffsetSecond(_) | Ignore(_) | UnixTimestamp(_) | End(_) => {
310 return Err(error::Format::InsufficientTypeInformation);
311 }
312 })
313}
314
315#[inline]
317fn fmt_day(
318 output: &mut (impl io::Write + ?Sized),
319 day: u8,
320 modifier::Day { padding }: modifier::Day,
321) -> Result<usize, io::Error> {
322 format_number::<2>(output, day, padding)
323}
324
325#[inline]
327fn fmt_month(
328 output: &mut (impl io::Write + ?Sized),
329 month: Month,
330 modifier::Month {
331 padding,
332 repr,
333 case_sensitive: _, }: modifier::Month,
335) -> Result<usize, io::Error> {
336 match repr {
337 modifier::MonthRepr::Numerical => format_number::<2>(output, u8::from(month), padding),
338 modifier::MonthRepr::Long => {
339 write(output, MONTH_NAMES[u8::from(month).extend::<usize>() - 1])
340 }
341 modifier::MonthRepr::Short => write(output, unsafe {
343 MONTH_NAMES[u8::from(month).extend::<usize>() - 1].get_unchecked(..3)
344 }),
345 }
346}
347
348#[inline]
350fn fmt_ordinal(
351 output: &mut (impl io::Write + ?Sized),
352 ordinal: u16,
353 modifier::Ordinal { padding }: modifier::Ordinal,
354) -> Result<usize, io::Error> {
355 format_number::<3>(output, ordinal, padding)
356}
357
358#[inline]
360fn fmt_weekday(
361 output: &mut (impl io::Write + ?Sized),
362 weekday: Weekday,
363 modifier::Weekday {
364 repr,
365 one_indexed,
366 case_sensitive: _, }: modifier::Weekday,
368) -> Result<usize, io::Error> {
369 match repr {
370 modifier::WeekdayRepr::Short => write(output, unsafe {
372 WEEKDAY_NAMES[weekday.number_days_from_monday().extend::<usize>()].get_unchecked(..3)
373 }),
374 modifier::WeekdayRepr::Long => write(
375 output,
376 WEEKDAY_NAMES[weekday.number_days_from_monday().extend::<usize>()],
377 ),
378 modifier::WeekdayRepr::Sunday => format_number::<1>(
379 output,
380 weekday.number_days_from_sunday() + u8::from(one_indexed),
381 modifier::Padding::None,
382 ),
383 modifier::WeekdayRepr::Monday => format_number::<1>(
384 output,
385 weekday.number_days_from_monday() + u8::from(one_indexed),
386 modifier::Padding::None,
387 ),
388 }
389}
390
391#[inline]
393fn fmt_week_number(
394 output: &mut (impl io::Write + ?Sized),
395 week_number: u8,
396 modifier::WeekNumber { padding, repr: _ }: modifier::WeekNumber,
397) -> Result<usize, io::Error> {
398 format_number::<2>(output, week_number, padding)
399}
400
401fn fmt_year(
403 output: &mut (impl io::Write + ?Sized),
404 full_year: i32,
405 modifier::Year {
406 padding,
407 repr,
408 range,
409 iso_week_based: _,
410 sign_is_mandatory,
411 }: modifier::Year,
412) -> Result<usize, error::Format> {
413 let value = match repr {
414 modifier::YearRepr::Full => full_year,
415 modifier::YearRepr::Century => full_year / 100,
416 modifier::YearRepr::LastTwo => (full_year % 100).abs(),
417 };
418 let format_number = if cfg!(feature = "large-dates") && range == modifier::YearRange::Extended {
419 match repr {
420 modifier::YearRepr::Full if value.abs() >= 100_000 => format_number::<6>,
421 modifier::YearRepr::Full if value.abs() >= 10_000 => format_number::<5>,
422 modifier::YearRepr::Full => format_number::<4>,
423 modifier::YearRepr::Century if value.abs() >= 1_000 => format_number::<4>,
424 modifier::YearRepr::Century if value.abs() >= 100 => format_number::<3>,
425 modifier::YearRepr::Century => format_number::<2>,
426 modifier::YearRepr::LastTwo => format_number::<2>,
427 }
428 } else {
429 match repr {
430 modifier::YearRepr::Full | modifier::YearRepr::Century if full_year.abs() >= 10_000 => {
431 return Err(error::ComponentRange::conditional("year").into());
432 }
433 _ => {}
434 }
435 match repr {
436 modifier::YearRepr::Full => format_number::<4>,
437 modifier::YearRepr::Century => format_number::<2>,
438 modifier::YearRepr::LastTwo => format_number::<2>,
439 }
440 };
441 let mut bytes = 0;
442 if repr != modifier::YearRepr::LastTwo {
443 if full_year < 0 {
444 bytes += write(output, b"-")?;
445 } else if sign_is_mandatory || cfg!(feature = "large-dates") && full_year >= 10_000 {
446 bytes += write(output, b"+")?;
447 }
448 }
449 bytes += format_number(output, value.unsigned_abs(), padding)?;
450 Ok(bytes)
451}
452
453#[inline]
455fn fmt_hour(
456 output: &mut (impl io::Write + ?Sized),
457 hour: u8,
458 modifier::Hour {
459 padding,
460 is_12_hour_clock,
461 }: modifier::Hour,
462) -> Result<usize, io::Error> {
463 let value = match (hour, is_12_hour_clock) {
464 (hour, false) => hour,
465 (0 | 12, true) => 12,
466 (hour, true) if hour < 12 => hour,
467 (hour, true) => hour - 12,
468 };
469 format_number::<2>(output, value, padding)
470}
471
472#[inline]
474fn fmt_minute(
475 output: &mut (impl io::Write + ?Sized),
476 minute: u8,
477 modifier::Minute { padding }: modifier::Minute,
478) -> Result<usize, io::Error> {
479 format_number::<2>(output, minute, padding)
480}
481
482#[inline]
484fn fmt_period(
485 output: &mut (impl io::Write + ?Sized),
486 period: Period,
487 modifier::Period {
488 is_uppercase,
489 case_sensitive: _, }: modifier::Period,
491) -> Result<usize, io::Error> {
492 write(
493 output,
494 match (period, is_uppercase) {
495 (Period::Am, false) => b"am",
496 (Period::Am, true) => b"AM",
497 (Period::Pm, false) => b"pm",
498 (Period::Pm, true) => b"PM",
499 },
500 )
501}
502
503#[inline]
505fn fmt_second(
506 output: &mut (impl io::Write + ?Sized),
507 second: u8,
508 modifier::Second { padding }: modifier::Second,
509) -> Result<usize, io::Error> {
510 format_number::<2>(output, second, padding)
511}
512
513#[inline]
515fn fmt_subsecond(
516 output: &mut (impl io::Write + ?Sized),
517 nanos: u32,
518 modifier::Subsecond { digits }: modifier::Subsecond,
519) -> Result<usize, io::Error> {
520 use modifier::SubsecondDigits::*;
521 if digits == Nine || (digits == OneOrMore && !nanos.is_multiple_of(10)) {
522 format_number_pad_zero::<9>(output, nanos)
523 } else if digits == Eight || (digits == OneOrMore && !(nanos / 10).is_multiple_of(10)) {
524 format_number_pad_zero::<8>(output, nanos / 10)
525 } else if digits == Seven || (digits == OneOrMore && !(nanos / 100).is_multiple_of(10)) {
526 format_number_pad_zero::<7>(output, nanos / 100)
527 } else if digits == Six || (digits == OneOrMore && !(nanos / 1_000).is_multiple_of(10)) {
528 format_number_pad_zero::<6>(output, nanos / 1_000)
529 } else if digits == Five || (digits == OneOrMore && !(nanos / 10_000).is_multiple_of(10)) {
530 format_number_pad_zero::<5>(output, nanos / 10_000)
531 } else if digits == Four || (digits == OneOrMore && !(nanos / 100_000).is_multiple_of(10)) {
532 format_number_pad_zero::<4>(output, nanos / 100_000)
533 } else if digits == Three || (digits == OneOrMore && !(nanos / 1_000_000).is_multiple_of(10)) {
534 format_number_pad_zero::<3>(output, nanos / 1_000_000)
535 } else if digits == Two || (digits == OneOrMore && !(nanos / 10_000_000).is_multiple_of(10)) {
536 format_number_pad_zero::<2>(output, nanos / 10_000_000)
537 } else {
538 format_number_pad_zero::<1>(output, nanos / 100_000_000)
539 }
540}
541
542#[inline]
543fn fmt_offset_sign(
544 output: &mut (impl io::Write + ?Sized),
545 is_negative: bool,
546 sign_is_mandatory: bool,
547) -> Result<usize, io::Error> {
548 if is_negative {
549 write(output, b"-")
550 } else if sign_is_mandatory {
551 write(output, b"+")
552 } else {
553 Ok(0)
554 }
555}
556
557#[inline]
559fn fmt_offset_hour(
560 output: &mut (impl io::Write + ?Sized),
561 is_negative: bool,
562 hour: i8,
563 modifier::OffsetHour {
564 padding,
565 sign_is_mandatory,
566 }: modifier::OffsetHour,
567) -> Result<usize, io::Error> {
568 let mut bytes = 0;
569 bytes += fmt_offset_sign(output, is_negative, sign_is_mandatory)?;
570 bytes += format_number::<2>(output, hour.unsigned_abs(), padding)?;
571 Ok(bytes)
572}
573
574#[inline]
576fn fmt_offset_minute(
577 output: &mut (impl io::Write + ?Sized),
578 offset_minute: i8,
579 modifier::OffsetMinute { padding }: modifier::OffsetMinute,
580) -> Result<usize, io::Error> {
581 format_number::<2>(output, offset_minute.unsigned_abs(), padding)
582}
583
584#[inline]
586fn fmt_offset_second(
587 output: &mut (impl io::Write + ?Sized),
588 offset_second: i8,
589 modifier::OffsetSecond { padding }: modifier::OffsetSecond,
590) -> Result<usize, io::Error> {
591 format_number::<2>(output, offset_second.unsigned_abs(), padding)
592}
593
594#[inline]
596fn fmt_unix_timestamp_seconds(
597 output: &mut (impl io::Write + ?Sized),
598 timestamp: i64,
599 modifier::UnixTimestamp {
600 precision,
601 sign_is_mandatory,
602 }: modifier::UnixTimestamp,
603) -> Result<usize, io::Error> {
604 debug_assert_eq!(precision, modifier::UnixTimestampPrecision::Second);
605
606 let mut bytes = 0;
607 bytes += fmt_offset_sign(output, timestamp < 0, sign_is_mandatory)?;
608 bytes += format_number_pad_none(output, timestamp.unsigned_abs())?;
609 Ok(bytes)
610}
611
612#[inline]
614fn fmt_unix_timestamp_milliseconds(
615 output: &mut (impl io::Write + ?Sized),
616 timestamp_millis: i64,
617 modifier::UnixTimestamp {
618 precision,
619 sign_is_mandatory,
620 }: modifier::UnixTimestamp,
621) -> Result<usize, io::Error> {
622 debug_assert_eq!(precision, modifier::UnixTimestampPrecision::Millisecond);
623
624 let mut bytes = 0;
625 bytes += fmt_offset_sign(output, timestamp_millis < 0, sign_is_mandatory)?;
626 bytes += format_number_pad_none(output, timestamp_millis.unsigned_abs())?;
627 Ok(bytes)
628}
629
630#[inline]
632fn fmt_unix_timestamp_microseconds(
633 output: &mut (impl io::Write + ?Sized),
634 timestamp_micros: i128,
635 modifier::UnixTimestamp {
636 precision,
637 sign_is_mandatory,
638 }: modifier::UnixTimestamp,
639) -> Result<usize, io::Error> {
640 debug_assert_eq!(precision, modifier::UnixTimestampPrecision::Microsecond);
641
642 let mut bytes = 0;
643 bytes += fmt_offset_sign(output, timestamp_micros < 0, sign_is_mandatory)?;
644 bytes += format_number_pad_none(output, timestamp_micros.unsigned_abs())?;
645 Ok(bytes)
646}
647
648#[inline]
650fn fmt_unix_timestamp_nanoseconds(
651 output: &mut (impl io::Write + ?Sized),
652 timestamp_nanos: i128,
653 modifier::UnixTimestamp {
654 precision,
655 sign_is_mandatory,
656 }: modifier::UnixTimestamp,
657) -> Result<usize, io::Error> {
658 debug_assert_eq!(precision, modifier::UnixTimestampPrecision::Nanosecond);
659
660 let mut bytes = 0;
661 bytes += fmt_offset_sign(output, timestamp_nanos < 0, sign_is_mandatory)?;
662 bytes += format_number_pad_none(output, timestamp_nanos.unsigned_abs())?;
663 Ok(bytes)
664}