| 1 | //! Formatting for various types. |
| 2 | |
| 3 | pub(crate) mod formattable; |
| 4 | mod iso8601; |
| 5 | |
| 6 | use core::num::NonZeroU8; |
| 7 | use std::io; |
| 8 | |
| 9 | use num_conv::prelude::*; |
| 10 | |
| 11 | pub use self::formattable::Formattable; |
| 12 | use crate::convert::*; |
| 13 | use crate::ext::DigitCount; |
| 14 | use crate::format_description::{modifier, Component}; |
| 15 | use crate::{error, Date, OffsetDateTime, Time, UtcOffset}; |
| 16 | |
| 17 | #[allow (clippy::missing_docs_in_private_items)] |
| 18 | const MONTH_NAMES: [&[u8]; 12] = [ |
| 19 | b"January" , |
| 20 | b"February" , |
| 21 | b"March" , |
| 22 | b"April" , |
| 23 | b"May" , |
| 24 | b"June" , |
| 25 | b"July" , |
| 26 | b"August" , |
| 27 | b"September" , |
| 28 | b"October" , |
| 29 | b"November" , |
| 30 | b"December" , |
| 31 | ]; |
| 32 | |
| 33 | #[allow (clippy::missing_docs_in_private_items)] |
| 34 | const 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 | /// Write all bytes to the output, returning the number of bytes written. |
| 45 | pub(crate) fn write(output: &mut impl io::Write, bytes: &[u8]) -> io::Result<usize> { |
| 46 | output.write_all(buf:bytes)?; |
| 47 | Ok(bytes.len()) |
| 48 | } |
| 49 | |
| 50 | /// If `pred` is true, write all bytes to the output, returning the number of bytes written. |
| 51 | pub(crate) fn write_if(output: &mut impl io::Write, pred: bool, bytes: &[u8]) -> io::Result<usize> { |
| 52 | if pred { |
| 53 | write(output, bytes) |
| 54 | } else { |
| 55 | Ok(0) |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | /// If `pred` is true, write `true_bytes` to the output. Otherwise, write `false_bytes`. |
| 60 | pub(crate) fn write_if_else( |
| 61 | output: &mut impl io::Write, |
| 62 | pred: bool, |
| 63 | true_bytes: &[u8], |
| 64 | false_bytes: &[u8], |
| 65 | ) -> io::Result<usize> { |
| 66 | write(output, bytes:if pred { true_bytes } else { false_bytes }) |
| 67 | } |
| 68 | |
| 69 | /// Write the floating point number to the output, returning the number of bytes written. |
| 70 | /// |
| 71 | /// This method accepts the number of digits before and after the decimal. The value will be padded |
| 72 | /// with zeroes to the left if necessary. |
| 73 | pub(crate) fn format_float( |
| 74 | output: &mut impl io::Write, |
| 75 | value: f64, |
| 76 | digits_before_decimal: u8, |
| 77 | digits_after_decimal: Option<NonZeroU8>, |
| 78 | ) -> io::Result<usize> { |
| 79 | match digits_after_decimal { |
| 80 | Some(digits_after_decimal: NonZero) => { |
| 81 | // Truncate the decimal points up to the precision |
| 82 | let trunc_num: f64 = 10_f64.powi(digits_after_decimal.get().cast_signed().extend()); |
| 83 | let value: f64 = f64::trunc(self:value * trunc_num) / trunc_num; |
| 84 | |
| 85 | let digits_after_decimal: usize = digits_after_decimal.get().extend(); |
| 86 | let width: usize = digits_before_decimal.extend::<usize>() + 1 + digits_after_decimal; |
| 87 | write!(output, " {value:0>width$.digits_after_decimal$}" )?; |
| 88 | Ok(width) |
| 89 | } |
| 90 | None => { |
| 91 | let value: u64 = value.trunc() as u64; |
| 92 | let width: usize = digits_before_decimal.extend(); |
| 93 | write!(output, " {value:0>width$}" )?; |
| 94 | Ok(width) |
| 95 | } |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | /// Format a number with the provided padding and width. |
| 100 | /// |
| 101 | /// The sign must be written by the caller. |
| 102 | pub(crate) fn format_number<const WIDTH: u8>( |
| 103 | output: &mut impl io::Write, |
| 104 | value: impl itoa::Integer + DigitCount + Copy, |
| 105 | padding: modifier::Padding, |
| 106 | ) -> Result<usize, io::Error> { |
| 107 | match padding { |
| 108 | modifier::Padding::Space => format_number_pad_space::<WIDTH>(output, value), |
| 109 | modifier::Padding::Zero => format_number_pad_zero::<WIDTH>(output, value), |
| 110 | modifier::Padding::None => format_number_pad_none(output, value), |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | /// Format a number with the provided width and spaces as padding. |
| 115 | /// |
| 116 | /// The sign must be written by the caller. |
| 117 | pub(crate) fn format_number_pad_space<const WIDTH: u8>( |
| 118 | output: &mut impl io::Write, |
| 119 | value: impl itoa::Integer + DigitCount + Copy, |
| 120 | ) -> Result<usize, io::Error> { |
| 121 | let mut bytes: usize = 0; |
| 122 | for _ in 0..(WIDTH.saturating_sub(value.num_digits())) { |
| 123 | bytes += write(output, bytes:b" " )?; |
| 124 | } |
| 125 | bytes += write(output, itoa::Buffer::new().format(value).as_bytes())?; |
| 126 | Ok(bytes) |
| 127 | } |
| 128 | |
| 129 | /// Format a number with the provided width and zeros as padding. |
| 130 | /// |
| 131 | /// The sign must be written by the caller. |
| 132 | pub(crate) fn format_number_pad_zero<const WIDTH: u8>( |
| 133 | output: &mut impl io::Write, |
| 134 | value: impl itoa::Integer + DigitCount + Copy, |
| 135 | ) -> Result<usize, io::Error> { |
| 136 | let mut bytes: usize = 0; |
| 137 | for _ in 0..(WIDTH.saturating_sub(value.num_digits())) { |
| 138 | bytes += write(output, bytes:b"0" )?; |
| 139 | } |
| 140 | bytes += write(output, itoa::Buffer::new().format(value).as_bytes())?; |
| 141 | Ok(bytes) |
| 142 | } |
| 143 | |
| 144 | /// Format a number with no padding. |
| 145 | /// |
| 146 | /// If the sign is mandatory, the sign must be written by the caller. |
| 147 | pub(crate) fn format_number_pad_none( |
| 148 | output: &mut impl io::Write, |
| 149 | value: impl itoa::Integer + Copy, |
| 150 | ) -> Result<usize, io::Error> { |
| 151 | write(output, itoa::Buffer::new().format(value).as_bytes()) |
| 152 | } |
| 153 | |
| 154 | /// Format the provided component into the designated output. An `Err` will be returned if the |
| 155 | /// component requires information that it does not provide or if the value cannot be output to the |
| 156 | /// stream. |
| 157 | pub(crate) fn format_component( |
| 158 | output: &mut impl io::Write, |
| 159 | component: Component, |
| 160 | date: Option<Date>, |
| 161 | time: Option<Time>, |
| 162 | offset: Option<UtcOffset>, |
| 163 | ) -> Result<usize, error::Format> { |
| 164 | use Component::*; |
| 165 | Ok(match (component, date, time, offset) { |
| 166 | (Day(modifier), Some(date), ..) => fmt_day(output, date, modifier)?, |
| 167 | (Month(modifier), Some(date), ..) => fmt_month(output, date, modifier)?, |
| 168 | (Ordinal(modifier), Some(date), ..) => fmt_ordinal(output, date, modifier)?, |
| 169 | (Weekday(modifier), Some(date), ..) => fmt_weekday(output, date, modifier)?, |
| 170 | (WeekNumber(modifier), Some(date), ..) => fmt_week_number(output, date, modifier)?, |
| 171 | (Year(modifier), Some(date), ..) => fmt_year(output, date, modifier)?, |
| 172 | (Hour(modifier), _, Some(time), _) => fmt_hour(output, time, modifier)?, |
| 173 | (Minute(modifier), _, Some(time), _) => fmt_minute(output, time, modifier)?, |
| 174 | (Period(modifier), _, Some(time), _) => fmt_period(output, time, modifier)?, |
| 175 | (Second(modifier), _, Some(time), _) => fmt_second(output, time, modifier)?, |
| 176 | (Subsecond(modifier), _, Some(time), _) => fmt_subsecond(output, time, modifier)?, |
| 177 | (OffsetHour(modifier), .., Some(offset)) => fmt_offset_hour(output, offset, modifier)?, |
| 178 | (OffsetMinute(modifier), .., Some(offset)) => fmt_offset_minute(output, offset, modifier)?, |
| 179 | (OffsetSecond(modifier), .., Some(offset)) => fmt_offset_second(output, offset, modifier)?, |
| 180 | (Ignore(_), ..) => 0, |
| 181 | (UnixTimestamp(modifier), Some(date), Some(time), Some(offset)) => { |
| 182 | fmt_unix_timestamp(output, date, time, offset, modifier)? |
| 183 | } |
| 184 | (End(modifier::End {}), ..) => 0, |
| 185 | |
| 186 | // This is functionally the same as a wildcard arm, but it will cause an error if a new |
| 187 | // component is added. This is to avoid a bug where a new component, the code compiles, and |
| 188 | // formatting fails. |
| 189 | // Allow unreachable patterns because some branches may be fully matched above. |
| 190 | #[allow (unreachable_patterns)] |
| 191 | ( |
| 192 | Day(_) | Month(_) | Ordinal(_) | Weekday(_) | WeekNumber(_) | Year(_) | Hour(_) |
| 193 | | Minute(_) | Period(_) | Second(_) | Subsecond(_) | OffsetHour(_) | OffsetMinute(_) |
| 194 | | OffsetSecond(_) | Ignore(_) | UnixTimestamp(_) | End(_), |
| 195 | .., |
| 196 | ) => return Err(error::Format::InsufficientTypeInformation), |
| 197 | }) |
| 198 | } |
| 199 | |
| 200 | // region: date formatters |
| 201 | /// Format the day into the designated output. |
| 202 | fn fmt_day( |
| 203 | output: &mut impl io::Write, |
| 204 | date: Date, |
| 205 | modifier::Day { padding: Padding }: modifier::Day, |
| 206 | ) -> Result<usize, io::Error> { |
| 207 | format_number::<2>(output, value:date.day(), padding) |
| 208 | } |
| 209 | |
| 210 | /// Format the month into the designated output. |
| 211 | fn fmt_month( |
| 212 | output: &mut impl io::Write, |
| 213 | date: Date, |
| 214 | modifier::Month { |
| 215 | padding: Padding, |
| 216 | repr: MonthRepr, |
| 217 | case_sensitive: _, // no effect on formatting |
| 218 | }: modifier::Month, |
| 219 | ) -> Result<usize, io::Error> { |
| 220 | match repr { |
| 221 | modifier::MonthRepr::Numerical => { |
| 222 | format_number::<2>(output, value:u8::from(date.month()), padding) |
| 223 | } |
| 224 | modifier::MonthRepr::Long => write( |
| 225 | output, |
| 226 | MONTH_NAMES[u8::from(date.month()).extend::<usize>() - 1], |
| 227 | ), |
| 228 | modifier::MonthRepr::Short => write( |
| 229 | output, |
| 230 | &MONTH_NAMES[u8::from(date.month()).extend::<usize>() - 1][..3], |
| 231 | ), |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | /// Format the ordinal into the designated output. |
| 236 | fn fmt_ordinal( |
| 237 | output: &mut impl io::Write, |
| 238 | date: Date, |
| 239 | modifier::Ordinal { padding: Padding }: modifier::Ordinal, |
| 240 | ) -> Result<usize, io::Error> { |
| 241 | format_number::<3>(output, value:date.ordinal(), padding) |
| 242 | } |
| 243 | |
| 244 | /// Format the weekday into the designated output. |
| 245 | fn fmt_weekday( |
| 246 | output: &mut impl io::Write, |
| 247 | date: Date, |
| 248 | modifier::Weekday { |
| 249 | repr: WeekdayRepr, |
| 250 | one_indexed: bool, |
| 251 | case_sensitive: _, // no effect on formatting |
| 252 | }: modifier::Weekday, |
| 253 | ) -> Result<usize, io::Error> { |
| 254 | match repr { |
| 255 | modifier::WeekdayRepr::Short => write( |
| 256 | output, |
| 257 | &WEEKDAY_NAMES[date.weekday().number_days_from_monday().extend::<usize>()][..3], |
| 258 | ), |
| 259 | modifier::WeekdayRepr::Long => write( |
| 260 | output, |
| 261 | WEEKDAY_NAMES[date.weekday().number_days_from_monday().extend::<usize>()], |
| 262 | ), |
| 263 | modifier::WeekdayRepr::Sunday => format_number::<1>( |
| 264 | output, |
| 265 | value:date.weekday().number_days_from_sunday() + u8::from(one_indexed), |
| 266 | modifier::Padding::None, |
| 267 | ), |
| 268 | modifier::WeekdayRepr::Monday => format_number::<1>( |
| 269 | output, |
| 270 | value:date.weekday().number_days_from_monday() + u8::from(one_indexed), |
| 271 | modifier::Padding::None, |
| 272 | ), |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | /// Format the week number into the designated output. |
| 277 | fn fmt_week_number( |
| 278 | output: &mut impl io::Write, |
| 279 | date: Date, |
| 280 | modifier::WeekNumber { padding: Padding, repr: WeekNumberRepr }: modifier::WeekNumber, |
| 281 | ) -> Result<usize, io::Error> { |
| 282 | format_number::<2>( |
| 283 | output, |
| 284 | value:match repr { |
| 285 | modifier::WeekNumberRepr::Iso => date.iso_week(), |
| 286 | modifier::WeekNumberRepr::Sunday => date.sunday_based_week(), |
| 287 | modifier::WeekNumberRepr::Monday => date.monday_based_week(), |
| 288 | }, |
| 289 | padding, |
| 290 | ) |
| 291 | } |
| 292 | |
| 293 | /// Format the year into the designated output. |
| 294 | fn fmt_year( |
| 295 | output: &mut impl io::Write, |
| 296 | date: Date, |
| 297 | modifier::Year { |
| 298 | padding: Padding, |
| 299 | repr: YearRepr, |
| 300 | iso_week_based: bool, |
| 301 | sign_is_mandatory: bool, |
| 302 | }: modifier::Year, |
| 303 | ) -> Result<usize, io::Error> { |
| 304 | let full_year = if iso_week_based { |
| 305 | date.iso_year_week().0 |
| 306 | } else { |
| 307 | date.year() |
| 308 | }; |
| 309 | let value = match repr { |
| 310 | modifier::YearRepr::Full => full_year, |
| 311 | modifier::YearRepr::Century => full_year / 100, |
| 312 | modifier::YearRepr::LastTwo => (full_year % 100).abs(), |
| 313 | }; |
| 314 | let format_number = match repr { |
| 315 | #[cfg (feature = "large-dates" )] |
| 316 | modifier::YearRepr::Full if value.abs() >= 100_000 => format_number::<6>, |
| 317 | #[cfg (feature = "large-dates" )] |
| 318 | modifier::YearRepr::Full if value.abs() >= 10_000 => format_number::<5>, |
| 319 | modifier::YearRepr::Full => format_number::<4>, |
| 320 | #[cfg (feature = "large-dates" )] |
| 321 | modifier::YearRepr::Century if value.abs() >= 1_000 => format_number::<4>, |
| 322 | #[cfg (feature = "large-dates" )] |
| 323 | modifier::YearRepr::Century if value.abs() >= 100 => format_number::<3>, |
| 324 | modifier::YearRepr::Century | modifier::YearRepr::LastTwo => format_number::<2>, |
| 325 | }; |
| 326 | let mut bytes = 0; |
| 327 | if repr != modifier::YearRepr::LastTwo { |
| 328 | if full_year < 0 { |
| 329 | bytes += write(output, b"-" )?; |
| 330 | } else if sign_is_mandatory || cfg!(feature = "large-dates" ) && full_year >= 10_000 { |
| 331 | bytes += write(output, b"+" )?; |
| 332 | } |
| 333 | } |
| 334 | bytes += format_number(output, value.unsigned_abs(), padding)?; |
| 335 | Ok(bytes) |
| 336 | } |
| 337 | // endregion date formatters |
| 338 | |
| 339 | // region: time formatters |
| 340 | /// Format the hour into the designated output. |
| 341 | fn fmt_hour( |
| 342 | output: &mut impl io::Write, |
| 343 | time: Time, |
| 344 | modifier::Hour { |
| 345 | padding: Padding, |
| 346 | is_12_hour_clock: bool, |
| 347 | }: modifier::Hour, |
| 348 | ) -> Result<usize, io::Error> { |
| 349 | let value: u8 = match (time.hour(), is_12_hour_clock) { |
| 350 | (hour: u8, false) => hour, |
| 351 | (0 | 12, true) => 12, |
| 352 | (hour: u8, true) if hour < 12 => hour, |
| 353 | (hour: u8, true) => hour - 12, |
| 354 | }; |
| 355 | format_number::<2>(output, value, padding) |
| 356 | } |
| 357 | |
| 358 | /// Format the minute into the designated output. |
| 359 | fn fmt_minute( |
| 360 | output: &mut impl io::Write, |
| 361 | time: Time, |
| 362 | modifier::Minute { padding: Padding }: modifier::Minute, |
| 363 | ) -> Result<usize, io::Error> { |
| 364 | format_number::<2>(output, value:time.minute(), padding) |
| 365 | } |
| 366 | |
| 367 | /// Format the period into the designated output. |
| 368 | fn fmt_period( |
| 369 | output: &mut impl io::Write, |
| 370 | time: Time, |
| 371 | modifier::Period { |
| 372 | is_uppercase: bool, |
| 373 | case_sensitive: _, // no effect on formatting |
| 374 | }: modifier::Period, |
| 375 | ) -> Result<usize, io::Error> { |
| 376 | match (time.hour() >= 12, is_uppercase) { |
| 377 | (false, false) => write(output, bytes:b"am" ), |
| 378 | (false, true) => write(output, bytes:b"AM" ), |
| 379 | (true, false) => write(output, bytes:b"pm" ), |
| 380 | (true, true) => write(output, bytes:b"PM" ), |
| 381 | } |
| 382 | } |
| 383 | |
| 384 | /// Format the second into the designated output. |
| 385 | fn fmt_second( |
| 386 | output: &mut impl io::Write, |
| 387 | time: Time, |
| 388 | modifier::Second { padding: Padding }: modifier::Second, |
| 389 | ) -> Result<usize, io::Error> { |
| 390 | format_number::<2>(output, value:time.second(), padding) |
| 391 | } |
| 392 | |
| 393 | /// Format the subsecond into the designated output. |
| 394 | fn fmt_subsecond<W: io::Write>( |
| 395 | output: &mut W, |
| 396 | time: Time, |
| 397 | modifier::Subsecond { digits: SubsecondDigits }: modifier::Subsecond, |
| 398 | ) -> Result<usize, io::Error> { |
| 399 | use modifier::SubsecondDigits::*; |
| 400 | let nanos: u32 = time.nanosecond(); |
| 401 | |
| 402 | if digits == Nine || (digits == OneOrMore && nanos % 10 != 0) { |
| 403 | format_number_pad_zero::<9>(output, value:nanos) |
| 404 | } else if digits == Eight || (digits == OneOrMore && (nanos / 10) % 10 != 0) { |
| 405 | format_number_pad_zero::<8>(output, value:nanos / 10) |
| 406 | } else if digits == Seven || (digits == OneOrMore && (nanos / 100) % 10 != 0) { |
| 407 | format_number_pad_zero::<7>(output, value:nanos / 100) |
| 408 | } else if digits == Six || (digits == OneOrMore && (nanos / 1_000) % 10 != 0) { |
| 409 | format_number_pad_zero::<6>(output, value:nanos / 1_000) |
| 410 | } else if digits == Five || (digits == OneOrMore && (nanos / 10_000) % 10 != 0) { |
| 411 | format_number_pad_zero::<5>(output, value:nanos / 10_000) |
| 412 | } else if digits == Four || (digits == OneOrMore && (nanos / 100_000) % 10 != 0) { |
| 413 | format_number_pad_zero::<4>(output, value:nanos / 100_000) |
| 414 | } else if digits == Three || (digits == OneOrMore && (nanos / 1_000_000) % 10 != 0) { |
| 415 | format_number_pad_zero::<3>(output, value:nanos / 1_000_000) |
| 416 | } else if digits == Two || (digits == OneOrMore && (nanos / 10_000_000) % 10 != 0) { |
| 417 | format_number_pad_zero::<2>(output, value:nanos / 10_000_000) |
| 418 | } else { |
| 419 | format_number_pad_zero::<1>(output, value:nanos / 100_000_000) |
| 420 | } |
| 421 | } |
| 422 | // endregion time formatters |
| 423 | |
| 424 | // region: offset formatters |
| 425 | /// Format the offset hour into the designated output. |
| 426 | fn fmt_offset_hour( |
| 427 | output: &mut impl io::Write, |
| 428 | offset: UtcOffset, |
| 429 | modifier::OffsetHour { |
| 430 | padding: Padding, |
| 431 | sign_is_mandatory: bool, |
| 432 | }: modifier::OffsetHour, |
| 433 | ) -> Result<usize, io::Error> { |
| 434 | let mut bytes: usize = 0; |
| 435 | if offset.is_negative() { |
| 436 | bytes += write(output, bytes:b"-" )?; |
| 437 | } else if sign_is_mandatory { |
| 438 | bytes += write(output, bytes:b"+" )?; |
| 439 | } |
| 440 | bytes += format_number::<2>(output, value:offset.whole_hours().unsigned_abs(), padding)?; |
| 441 | Ok(bytes) |
| 442 | } |
| 443 | |
| 444 | /// Format the offset minute into the designated output. |
| 445 | fn fmt_offset_minute( |
| 446 | output: &mut impl io::Write, |
| 447 | offset: UtcOffset, |
| 448 | modifier::OffsetMinute { padding: Padding }: modifier::OffsetMinute, |
| 449 | ) -> Result<usize, io::Error> { |
| 450 | format_number::<2>(output, value:offset.minutes_past_hour().unsigned_abs(), padding) |
| 451 | } |
| 452 | |
| 453 | /// Format the offset second into the designated output. |
| 454 | fn fmt_offset_second( |
| 455 | output: &mut impl io::Write, |
| 456 | offset: UtcOffset, |
| 457 | modifier::OffsetSecond { padding: Padding }: modifier::OffsetSecond, |
| 458 | ) -> Result<usize, io::Error> { |
| 459 | format_number::<2>(output, value:offset.seconds_past_minute().unsigned_abs(), padding) |
| 460 | } |
| 461 | // endregion offset formatters |
| 462 | |
| 463 | /// Format the Unix timestamp into the designated output. |
| 464 | fn fmt_unix_timestamp( |
| 465 | output: &mut impl io::Write, |
| 466 | date: Date, |
| 467 | time: Time, |
| 468 | offset: UtcOffset, |
| 469 | modifier::UnixTimestamp { |
| 470 | precision: UnixTimestampPrecision, |
| 471 | sign_is_mandatory: bool, |
| 472 | }: modifier::UnixTimestamp, |
| 473 | ) -> Result<usize, io::Error> { |
| 474 | let date_time = OffsetDateTime::new_in_offset(date, time, offset).to_offset(UtcOffset::UTC); |
| 475 | |
| 476 | if date_time < OffsetDateTime::UNIX_EPOCH { |
| 477 | write(output, b"-" )?; |
| 478 | } else if sign_is_mandatory { |
| 479 | write(output, b"+" )?; |
| 480 | } |
| 481 | |
| 482 | match precision { |
| 483 | modifier::UnixTimestampPrecision::Second => { |
| 484 | format_number_pad_none(output, date_time.unix_timestamp().unsigned_abs()) |
| 485 | } |
| 486 | modifier::UnixTimestampPrecision::Millisecond => format_number_pad_none( |
| 487 | output, |
| 488 | (date_time.unix_timestamp_nanos() |
| 489 | / Nanosecond::per(Millisecond).cast_signed().extend::<i128>()) |
| 490 | .unsigned_abs(), |
| 491 | ), |
| 492 | modifier::UnixTimestampPrecision::Microsecond => format_number_pad_none( |
| 493 | output, |
| 494 | (date_time.unix_timestamp_nanos() |
| 495 | / Nanosecond::per(Microsecond).cast_signed().extend::<i128>()) |
| 496 | .unsigned_abs(), |
| 497 | ), |
| 498 | modifier::UnixTimestampPrecision::Nanosecond => { |
| 499 | format_number_pad_none(output, date_time.unix_timestamp_nanos().unsigned_abs()) |
| 500 | } |
| 501 | } |
| 502 | } |
| 503 | |