//! Formatting for various types. pub(crate) mod formattable; mod iso8601; use core::num::NonZeroU8; use std::io; use num_conv::prelude::*; pub use self::formattable::Formattable; use crate::convert::*; use crate::ext::DigitCount; use crate::format_description::{modifier, Component}; use crate::{error, Date, OffsetDateTime, Time, UtcOffset}; const MONTH_NAMES: [&[u8]; 12] = [ b"January", b"February", b"March", b"April", b"May", b"June", b"July", b"August", b"September", b"October", b"November", b"December", ]; const WEEKDAY_NAMES: [&[u8]; 7] = [ b"Monday", b"Tuesday", b"Wednesday", b"Thursday", b"Friday", b"Saturday", b"Sunday", ]; /// Write all bytes to the output, returning the number of bytes written. pub(crate) fn write(output: &mut (impl io::Write + ?Sized), bytes: &[u8]) -> io::Result { output.write_all(bytes)?; Ok(bytes.len()) } /// If `pred` is true, write all bytes to the output, returning the number of bytes written. pub(crate) fn write_if( output: &mut (impl io::Write + ?Sized), pred: bool, bytes: &[u8], ) -> io::Result { if pred { write(output, bytes) } else { Ok(0) } } /// If `pred` is true, write `true_bytes` to the output. Otherwise, write `false_bytes`. pub(crate) fn write_if_else( output: &mut (impl io::Write + ?Sized), pred: bool, true_bytes: &[u8], false_bytes: &[u8], ) -> io::Result { write(output, if pred { true_bytes } else { false_bytes }) } /// Write the floating point number to the output, returning the number of bytes written. /// /// This method accepts the number of digits before and after the decimal. The value will be padded /// with zeroes to the left if necessary. pub(crate) fn format_float( output: &mut (impl io::Write + ?Sized), value: f64, digits_before_decimal: u8, digits_after_decimal: Option, ) -> io::Result { match digits_after_decimal { Some(digits_after_decimal) => { // Truncate the decimal points up to the precision let trunc_num = 10_f64.powi(digits_after_decimal.get().cast_signed().extend()); let value = f64::trunc(value * trunc_num) / trunc_num; let digits_after_decimal = digits_after_decimal.get().extend(); let width = digits_before_decimal.extend::() + 1 + digits_after_decimal; write!(output, "{value:0>width$.digits_after_decimal$}")?; Ok(width) } None => { let value = value.trunc() as u64; let width = digits_before_decimal.extend(); write!(output, "{value:0>width$}")?; Ok(width) } } } /// Format a number with the provided padding and width. /// /// The sign must be written by the caller. pub(crate) fn format_number( output: &mut (impl io::Write + ?Sized), value: impl itoa::Integer + DigitCount + Copy, padding: modifier::Padding, ) -> Result { match padding { modifier::Padding::Space => format_number_pad_space::(output, value), modifier::Padding::Zero => format_number_pad_zero::(output, value), modifier::Padding::None => format_number_pad_none(output, value), } } /// Format a number with the provided width and spaces as padding. /// /// The sign must be written by the caller. pub(crate) fn format_number_pad_space( output: &mut (impl io::Write + ?Sized), value: impl itoa::Integer + DigitCount + Copy, ) -> Result { let mut bytes = 0; for _ in 0..(WIDTH.saturating_sub(value.num_digits())) { bytes += write(output, b" ")?; } bytes += write(output, itoa::Buffer::new().format(value).as_bytes())?; Ok(bytes) } /// Format a number with the provided width and zeros as padding. /// /// The sign must be written by the caller. pub(crate) fn format_number_pad_zero( output: &mut (impl io::Write + ?Sized), value: impl itoa::Integer + DigitCount + Copy, ) -> Result { let mut bytes = 0; for _ in 0..(WIDTH.saturating_sub(value.num_digits())) { bytes += write(output, b"0")?; } bytes += write(output, itoa::Buffer::new().format(value).as_bytes())?; Ok(bytes) } /// Format a number with no padding. /// /// If the sign is mandatory, the sign must be written by the caller. pub(crate) fn format_number_pad_none( output: &mut (impl io::Write + ?Sized), value: impl itoa::Integer + Copy, ) -> Result { write(output, itoa::Buffer::new().format(value).as_bytes()) } /// Format the provided component into the designated output. An `Err` will be returned if the /// component requires information that it does not provide or if the value cannot be output to the /// stream. pub(crate) fn format_component( output: &mut (impl io::Write + ?Sized), component: Component, date: Option, time: Option