From 14e2272a306f507280bd268e786b6117de28cdae Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Mon, 17 Oct 2022 10:33:35 +0100 Subject: [PATCH 1/6] use less intermediate formatting (-5% improvement) --- src/date.rs | 6 ++++-- src/datetime/mod.rs | 8 ++++++-- src/format/mod.rs | 2 +- src/naive/datetime/mod.rs | 9 +++++++-- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/date.rs b/src/date.rs index 24e4aa9190..1e0bf0e743 100644 --- a/src/date.rs +++ b/src/date.rs @@ -532,7 +532,8 @@ impl Sub> for Date { impl fmt::Debug for Date { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}{:?}", self.naive_local(), self.offset) + self.naive_local().fmt(f)?; + self.offset.fmt(f) } } @@ -541,7 +542,8 @@ where Tz::Offset: fmt::Display, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}{}", self.naive_local(), self.offset) + self.naive_local().fmt(f)?; + self.offset.fmt(f) } } diff --git a/src/datetime/mod.rs b/src/datetime/mod.rs index c5a4c721bb..0a9c610beb 100644 --- a/src/datetime/mod.rs +++ b/src/datetime/mod.rs @@ -11,6 +11,7 @@ use alloc::string::{String, ToString}; #[cfg(any(feature = "alloc", feature = "std", test))] use core::borrow::Borrow; use core::cmp::Ordering; +use core::fmt::Write; use core::ops::{Add, AddAssign, Sub, SubAssign}; use core::{fmt, hash, str}; #[cfg(feature = "std")] @@ -990,7 +991,8 @@ impl Sub for DateTime { impl fmt::Debug for DateTime { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}{:?}", self.naive_local(), self.offset) + self.naive_local().fmt(f)?; + self.offset.fmt(f) } } @@ -999,7 +1001,9 @@ where Tz::Offset: fmt::Display, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} {}", self.naive_local(), self.offset) + self.naive_local().fmt(f)?; + f.write_char(' ')?; + self.offset.fmt(f) } } diff --git a/src/format/mod.rs b/src/format/mod.rs index da347531d3..ea015e7472 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -737,7 +737,7 @@ fn format_inner<'a>( if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) { // reuse `Debug` impls which already print ISO 8601 format. // this is faster in this way. - write!(result, "{:?}T{:?}", d, t)?; + write!(result, "{:?}", crate::NaiveDateTime::new(*d, *t))?; Some(write_local_minus_utc(result, off, false, Colons::Single)) } else { None diff --git a/src/naive/datetime/mod.rs b/src/naive/datetime/mod.rs index f2cee59fd1..5cf29739fa 100644 --- a/src/naive/datetime/mod.rs +++ b/src/naive/datetime/mod.rs @@ -6,6 +6,7 @@ #[cfg(any(feature = "alloc", feature = "std", test))] use core::borrow::Borrow; use core::convert::TryFrom; +use core::fmt::Write; use core::ops::{Add, AddAssign, Sub, SubAssign}; use core::{fmt, str}; @@ -1629,7 +1630,9 @@ impl Sub for NaiveDateTime { /// ``` impl fmt::Debug for NaiveDateTime { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}T{:?}", self.date, self.time) + self.date.fmt(f)?; + f.write_char('T')?; + self.time.fmt(f) } } @@ -1660,7 +1663,9 @@ impl fmt::Debug for NaiveDateTime { /// ``` impl fmt::Display for NaiveDateTime { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} {}", self.date, self.time) + self.date.fmt(f)?; + f.write_char(' ')?; + self.time.fmt(f) } } From a03c3a2bc56048d5ce741b00b2ee276651738d3c Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Mon, 17 Oct 2022 10:43:37 +0100 Subject: [PATCH 2/6] skip DelayedFormat for rfc3339 (net -58% improvement) --- src/datetime/mod.rs | 6 ++- src/format/mod.rs | 93 ++++++++++++++++++++++++--------------------- 2 files changed, 53 insertions(+), 46 deletions(-) diff --git a/src/datetime/mod.rs b/src/datetime/mod.rs index 0a9c610beb..9f10eeefd2 100644 --- a/src/datetime/mod.rs +++ b/src/datetime/mod.rs @@ -608,8 +608,10 @@ where #[cfg(any(feature = "alloc", feature = "std", test))] #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] pub fn to_rfc3339(&self) -> String { - const ITEMS: &[Item<'static>] = &[Item::Fixed(Fixed::RFC3339)]; - self.format_with_items(ITEMS.iter()).to_string() + let mut result = String::with_capacity(32); + crate::format::write_rfc3339(&mut result, self.naive_local(), self.offset.fix()) + .expect("writing rfc3339 datetime to string should never fail"); + result } /// Return an RFC 3339 and ISO 8601 date and time string with subseconds diff --git a/src/format/mod.rs b/src/format/mod.rs index ea015e7472..6705e3ed6a 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -40,6 +40,8 @@ use alloc::string::{String, ToString}; #[cfg(any(feature = "alloc", feature = "std", test))] use core::borrow::Borrow; use core::fmt; +#[cfg(any(feature = "alloc", feature = "std", test))] +use core::fmt::Write; use core::str::FromStr; #[cfg(any(feature = "std", test))] use std::error::Error; @@ -501,7 +503,6 @@ fn format_inner<'a>( ) }; - use core::fmt::Write; use num_integer::{div_floor, mod_floor}; match *item { @@ -577,45 +578,6 @@ fn format_inner<'a>( Item::Fixed(ref spec) => { use self::Fixed::*; - /// Prints an offset from UTC in the format of `+HHMM` or `+HH:MM`. - /// `Z` instead of `+00[:]00` is allowed when `allow_zulu` is true. - fn write_local_minus_utc( - result: &mut String, - off: FixedOffset, - allow_zulu: bool, - colon_type: Colons, - ) -> fmt::Result { - let off = off.local_minus_utc(); - if !allow_zulu || off != 0 { - let (sign, off) = if off < 0 { ('-', -off) } else { ('+', off) }; - - match colon_type { - Colons::None => { - write!(result, "{}{:02}{:02}", sign, off / 3600, off / 60 % 60) - } - Colons::Single => { - write!(result, "{}{:02}:{:02}", sign, off / 3600, off / 60 % 60) - } - Colons::Double => { - write!( - result, - "{}{:02}:{:02}:{:02}", - sign, - off / 3600, - off / 60 % 60, - off % 60 - ) - } - Colons::Triple => { - write!(result, "{}{:02}", sign, off / 3600) - } - } - } else { - result.push('Z'); - Ok(()) - } - } - let ret = match *spec { ShortMonthName => date.map(|d| { @@ -735,10 +697,7 @@ fn format_inner<'a>( // same as `%Y-%m-%dT%H:%M:%S%.f%:z` { if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) { - // reuse `Debug` impls which already print ISO 8601 format. - // this is faster in this way. - write!(result, "{:?}", crate::NaiveDateTime::new(*d, *t))?; - Some(write_local_minus_utc(result, off, false, Colons::Single)) + Some(write_rfc3339(result, crate::NaiveDateTime::new(*d, *t), off)) } else { None } @@ -756,6 +715,52 @@ fn format_inner<'a>( Ok(()) } +/// Prints an offset from UTC in the format of `+HHMM` or `+HH:MM`. +/// `Z` instead of `+00[:]00` is allowed when `allow_zulu` is true. +#[cfg(any(feature = "alloc", feature = "std", test))] +fn write_local_minus_utc( + result: &mut String, + off: FixedOffset, + allow_zulu: bool, + colon_type: Colons, +) -> fmt::Result { + let off = off.local_minus_utc(); + if !allow_zulu || off != 0 { + let (sign, off) = if off < 0 { ('-', -off) } else { ('+', off) }; + + match colon_type { + Colons::None => { + write!(result, "{}{:02}{:02}", sign, off / 3600, off / 60 % 60) + } + Colons::Single => { + write!(result, "{}{:02}:{:02}", sign, off / 3600, off / 60 % 60) + } + Colons::Double => { + write!(result, "{}{:02}:{:02}:{:02}", sign, off / 3600, off / 60 % 60, off % 60) + } + Colons::Triple => { + write!(result, "{}{:02}", sign, off / 3600) + } + } + } else { + result.push('Z'); + Ok(()) + } +} + +/// Writes the date, time and offset to the string. same as `%Y-%m-%dT%H:%M:%S%.f%:z` +#[cfg(any(feature = "alloc", feature = "std", test))] +pub(crate) fn write_rfc3339( + result: &mut String, + dt: crate::NaiveDateTime, + off: FixedOffset, +) -> fmt::Result { + // reuse `Debug` impls which already print ISO 8601 format. + // this is faster in this way. + write!(result, "{:?}", dt)?; + write_local_minus_utc(result, off, false, Colons::Single) +} + /// Tries to format given arguments with given formatting items. /// Internally used by `DelayedFormat`. #[cfg(any(feature = "alloc", feature = "std", test))] From c5c63c3c45cf817d58d85883cf7ec0fc4aaa599c Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Mon, 17 Oct 2022 11:03:13 +0100 Subject: [PATCH 3/6] extract out locales for a later change --- src/format/mod.rs | 130 +++++++++++++++++++++++++++------------------- 1 file changed, 77 insertions(+), 53 deletions(-) diff --git a/src/format/mod.rs b/src/format/mod.rs index 6705e3ed6a..d4560b9396 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -441,6 +441,63 @@ const TOO_SHORT: ParseError = ParseError(ParseErrorKind::TooShort); const TOO_LONG: ParseError = ParseError(ParseErrorKind::TooLong); const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat); +#[cfg(any(feature = "alloc", feature = "std", test))] +struct Locales { + short_months: &'static [&'static str], + long_months: &'static [&'static str], + short_weekdays: &'static [&'static str], + long_weekdays: &'static [&'static str], + am_pm: &'static [&'static str], +} + +#[cfg(any(feature = "alloc", feature = "std", test))] +impl Locales { + fn new(_locale: Option) -> Self { + #[cfg(feature = "unstable-locales")] + { + let locale = _locale.unwrap_or(Locale::POSIX); + Self { + short_months: locales::short_months(locale), + long_months: locales::long_months(locale), + short_weekdays: locales::short_weekdays(locale), + long_weekdays: locales::long_weekdays(locale), + am_pm: locales::am_pm(locale), + } + } + #[cfg(not(feature = "unstable-locales"))] + Self { + short_months: &[ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + ], + long_months: &[ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + ], + short_weekdays: &["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], + long_weekdays: &[ + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + ], + am_pm: &["AM", "PM"], + } + } +} + /// Formats single formatting item #[cfg(any(feature = "alloc", feature = "std", test))] #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] @@ -463,45 +520,9 @@ fn format_inner<'a>( time: Option<&NaiveTime>, off: Option<&(String, FixedOffset)>, item: &Item<'a>, - _locale: Option, + locale: Option, ) -> fmt::Result { - #[cfg(feature = "unstable-locales")] - let (short_months, long_months, short_weekdays, long_weekdays, am_pm, am_pm_lowercase) = { - let locale = _locale.unwrap_or(Locale::POSIX); - let am_pm = locales::am_pm(locale); - ( - locales::short_months(locale), - locales::long_months(locale), - locales::short_weekdays(locale), - locales::long_weekdays(locale), - am_pm, - &[am_pm[0].to_lowercase(), am_pm[1].to_lowercase()], - ) - }; - #[cfg(not(feature = "unstable-locales"))] - let (short_months, long_months, short_weekdays, long_weekdays, am_pm, am_pm_lowercase) = { - ( - &["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], - &[ - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December", - ], - &["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], - &["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], - &["AM", "PM"], - &["am", "pm"], - ) - }; + let locale = Locales::new(locale); use num_integer::{div_floor, mod_floor}; @@ -581,35 +602,38 @@ fn format_inner<'a>( let ret = match *spec { ShortMonthName => date.map(|d| { - result.push_str(short_months[d.month0() as usize]); + result.push_str(locale.short_months[d.month0() as usize]); Ok(()) }), LongMonthName => date.map(|d| { - result.push_str(long_months[d.month0() as usize]); + result.push_str(locale.long_months[d.month0() as usize]); Ok(()) }), ShortWeekdayName => date.map(|d| { - result - .push_str(short_weekdays[d.weekday().num_days_from_sunday() as usize]); + result.push_str( + locale.short_weekdays[d.weekday().num_days_from_sunday() as usize], + ); Ok(()) }), LongWeekdayName => date.map(|d| { - result.push_str(long_weekdays[d.weekday().num_days_from_sunday() as usize]); + result.push_str( + locale.long_weekdays[d.weekday().num_days_from_sunday() as usize], + ); Ok(()) }), LowerAmPm => time.map(|t| { - #[cfg_attr(feature = "cargo-clippy", allow(clippy::useless_asref))] - { - result.push_str(if t.hour12().0 { - am_pm_lowercase[1].as_ref() - } else { - am_pm_lowercase[0].as_ref() - }); + let ampm = if t.hour12().0 { locale.am_pm[1] } else { locale.am_pm[0] }; + for char in ampm.chars() { + result.extend(char.to_lowercase()) } Ok(()) }), UpperAmPm => time.map(|t| { - result.push_str(if t.hour12().0 { am_pm[1] } else { am_pm[0] }); + result.push_str(if t.hour12().0 { + locale.am_pm[1] + } else { + locale.am_pm[0] + }); Ok(()) }), Nanosecond => time.map(|t| { @@ -680,9 +704,9 @@ fn format_inner<'a>( write!( result, "{}, {:02} {} {:04} {:02}:{:02}:{:02} ", - short_weekdays[d.weekday().num_days_from_sunday() as usize], + locale.short_weekdays[d.weekday().num_days_from_sunday() as usize], d.day(), - short_months[d.month0() as usize], + locale.short_months[d.month0() as usize], d.year(), t.hour(), t.minute(), From 30c8b9ed27558148938d339fe1620fee9ada4a13 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Mon, 17 Oct 2022 11:09:01 +0100 Subject: [PATCH 4/6] skip DelayedFormat for rfc2822 (net -55% improvement for 2822) --- src/datetime/mod.rs | 6 +++-- src/format/mod.rs | 54 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/datetime/mod.rs b/src/datetime/mod.rs index 9f10eeefd2..f46cd4fa20 100644 --- a/src/datetime/mod.rs +++ b/src/datetime/mod.rs @@ -600,8 +600,10 @@ where #[cfg(any(feature = "alloc", feature = "std", test))] #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] pub fn to_rfc2822(&self) -> String { - const ITEMS: &[Item<'static>] = &[Item::Fixed(Fixed::RFC2822)]; - self.format_with_items(ITEMS.iter()).to_string() + let mut result = String::with_capacity(32); + crate::format::write_rfc2822(&mut result, self.naive_local(), self.offset.fix()) + .expect("writing rfc2822 datetime to string should never fail"); + result } /// Returns an RFC 3339 and ISO 8601 date and time string such as `1996-12-19T16:39:57-08:00`. diff --git a/src/format/mod.rs b/src/format/mod.rs index d4560b9396..ab020c5cf8 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -700,19 +700,7 @@ fn format_inner<'a>( // same as `%a, %d %b %Y %H:%M:%S %z` { if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) { - let sec = t.second() + t.nanosecond() / 1_000_000_000; - write!( - result, - "{}, {:02} {} {:04} {:02}:{:02}:{:02} ", - locale.short_weekdays[d.weekday().num_days_from_sunday() as usize], - d.day(), - locale.short_months[d.month0() as usize], - d.year(), - t.hour(), - t.minute(), - sec - )?; - Some(write_local_minus_utc(result, off, false, Colons::None)) + Some(write_rfc2822_inner(result, d, t, off, locale)) } else { None } @@ -785,6 +773,46 @@ pub(crate) fn write_rfc3339( write_local_minus_utc(result, off, false, Colons::Single) } +#[cfg(any(feature = "alloc", feature = "std", test))] +/// write datetimes like `Tue, 1 Jul 2003 10:52:37 +0200`, same as `%a, %d %b %Y %H:%M:%S %z` +pub(crate) fn write_rfc2822( + result: &mut String, + dt: crate::NaiveDateTime, + off: FixedOffset, +) -> fmt::Result { + write_rfc2822_inner(result, &dt.date(), &dt.time(), off, Locales::new(None)) +} + +#[cfg(any(feature = "alloc", feature = "std", test))] +/// write datetimes like `Tue, 1 Jul 2003 10:52:37 +0200`, same as `%a, %d %b %Y %H:%M:%S %z` +fn write_rfc2822_inner( + result: &mut String, + d: &NaiveDate, + t: &NaiveTime, + off: FixedOffset, + locale: Locales, +) -> fmt::Result { + let year = d.year(); + // RFC2822 is only defined on years 0 through 9999 + if !(0..=9999).contains(&year) { + return Err(fmt::Error); + } + + let sec = t.second() + t.nanosecond() / 1_000_000_000; + write!( + result, + "{}, {:02} {} {:04} {:02}:{:02}:{:02} ", + locale.short_weekdays[d.weekday().num_days_from_sunday() as usize], + d.day(), + locale.short_months[d.month0() as usize], + year, + t.hour(), + t.minute(), + sec + )?; + write_local_minus_utc(result, off, false, Colons::None) +} + /// Tries to format given arguments with given formatting items. /// Internally used by `DelayedFormat`. #[cfg(any(feature = "alloc", feature = "std", test))] From 040125db2f351b94be5dcb0d5448af2e304c7eaf Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Mon, 17 Oct 2022 11:38:28 +0100 Subject: [PATCH 5/6] avoid int formatting as much as possible (net -70%/-68% on 2822/3339 respectively) --- src/format/mod.rs | 39 +++++++++++++++++++++++++++------------ src/naive/date.rs | 14 +++++++++++--- src/naive/time/mod.rs | 10 ++++++++-- 3 files changed, 46 insertions(+), 17 deletions(-) diff --git a/src/format/mod.rs b/src/format/mod.rs index ab020c5cf8..ed63e4eb7a 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -40,7 +40,6 @@ use alloc::string::{String, ToString}; #[cfg(any(feature = "alloc", feature = "std", test))] use core::borrow::Borrow; use core::fmt; -#[cfg(any(feature = "alloc", feature = "std", test))] use core::fmt::Write; use core::str::FromStr; #[cfg(any(feature = "std", test))] @@ -798,21 +797,37 @@ fn write_rfc2822_inner( return Err(fmt::Error); } + result.push_str(locale.short_weekdays[d.weekday().num_days_from_sunday() as usize]); + result.push_str(", "); + write_hundreds(result, d.day() as u8)?; + result.push(' '); + result.push_str(locale.short_months[d.month0() as usize]); + result.push(' '); + write_hundreds(result, (year / 100) as u8)?; + write_hundreds(result, (year % 100) as u8)?; + result.push(' '); + write_hundreds(result, t.hour() as u8)?; + result.push(':'); + write_hundreds(result, t.minute() as u8)?; + result.push(':'); let sec = t.second() + t.nanosecond() / 1_000_000_000; - write!( - result, - "{}, {:02} {} {:04} {:02}:{:02}:{:02} ", - locale.short_weekdays[d.weekday().num_days_from_sunday() as usize], - d.day(), - locale.short_months[d.month0() as usize], - year, - t.hour(), - t.minute(), - sec - )?; + write_hundreds(result, sec as u8)?; + result.push(' '); write_local_minus_utc(result, off, false, Colons::None) } +/// Equivalent to `{:02}` formatting for n < 100. +pub(crate) fn write_hundreds(w: &mut impl Write, n: u8) -> fmt::Result { + if n >= 100 { + return Err(fmt::Error); + } + + let tens = b'0' + n / 10; + let ones = b'0' + n % 10; + w.write_char(tens as char)?; + w.write_char(ones as char) +} + /// Tries to format given arguments with given formatting items. /// Internally used by `DelayedFormat`. #[cfg(any(feature = "alloc", feature = "std", test))] diff --git a/src/naive/date.rs b/src/naive/date.rs index c5fb7b1725..e362cd96ef 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -16,7 +16,7 @@ use rkyv::{Archive, Deserialize, Serialize}; #[cfg(any(feature = "alloc", feature = "std", test))] use crate::format::DelayedFormat; -use crate::format::{parse, ParseError, ParseResult, Parsed, StrftimeItems}; +use crate::format::{parse, write_hundreds, ParseError, ParseResult, Parsed, StrftimeItems}; use crate::format::{Item, Numeric, Pad}; use crate::month::Months; use crate::naive::{IsoWeek, NaiveDateTime, NaiveTime}; @@ -1830,14 +1830,22 @@ impl DoubleEndedIterator for NaiveDateWeeksIterator { /// ``` impl fmt::Debug for NaiveDate { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use core::fmt::Write; + let year = self.year(); let mdf = self.mdf(); if (0..=9999).contains(&year) { - write!(f, "{:04}-{:02}-{:02}", year, mdf.month(), mdf.day()) + write_hundreds(f, (year / 100) as u8)?; + write_hundreds(f, (year % 100) as u8)?; } else { // ISO 8601 requires the explicit sign for out-of-range years - write!(f, "{:+05}-{:02}-{:02}", year, mdf.month(), mdf.day()) + write!(f, "{:+05}", year)?; } + + f.write_char('-')?; + write_hundreds(f, mdf.month() as u8)?; + f.write_char('-')?; + write_hundreds(f, mdf.day() as u8) } } diff --git a/src/naive/time/mod.rs b/src/naive/time/mod.rs index c79fe6b3c6..d0354a3f0a 100644 --- a/src/naive/time/mod.rs +++ b/src/naive/time/mod.rs @@ -14,7 +14,7 @@ use rkyv::{Archive, Deserialize, Serialize}; #[cfg(any(feature = "alloc", feature = "std", test))] use crate::format::DelayedFormat; -use crate::format::{parse, ParseError, ParseResult, Parsed, StrftimeItems}; +use crate::format::{parse, write_hundreds, ParseError, ParseResult, Parsed, StrftimeItems}; use crate::format::{Fixed, Item, Numeric, Pad}; use crate::oldtime::Duration as OldDuration; use crate::Timelike; @@ -1188,7 +1188,13 @@ impl fmt::Debug for NaiveTime { (sec, self.frac) }; - write!(f, "{:02}:{:02}:{:02}", hour, min, sec)?; + use core::fmt::Write; + write_hundreds(f, hour as u8)?; + f.write_char(':')?; + write_hundreds(f, min as u8)?; + f.write_char(':')?; + write_hundreds(f, sec as u8)?; + if nano == 0 { Ok(()) } else if nano % 1_000_000 == 0 { From c6bb0d17ee40ab08898d9028a30ebd486c730cda Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Tue, 18 Oct 2022 07:53:49 +0100 Subject: [PATCH 6/6] remove dyn formatting from timezone (-74/78% on 2822/3339 respectively) --- src/format/mod.rs | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/format/mod.rs b/src/format/mod.rs index ed63e4eb7a..39b3db7d82 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -736,26 +736,28 @@ fn write_local_minus_utc( colon_type: Colons, ) -> fmt::Result { let off = off.local_minus_utc(); - if !allow_zulu || off != 0 { - let (sign, off) = if off < 0 { ('-', -off) } else { ('+', off) }; + if allow_zulu && off == 0 { + result.push('Z'); + return Ok(()); + } + let (sign, off) = if off < 0 { ('-', -off) } else { ('+', off) }; + result.push(sign); - match colon_type { - Colons::None => { - write!(result, "{}{:02}{:02}", sign, off / 3600, off / 60 % 60) - } - Colons::Single => { - write!(result, "{}{:02}:{:02}", sign, off / 3600, off / 60 % 60) - } - Colons::Double => { - write!(result, "{}{:02}:{:02}:{:02}", sign, off / 3600, off / 60 % 60, off % 60) - } - Colons::Triple => { - write!(result, "{}{:02}", sign, off / 3600) - } + write_hundreds(result, (off / 3600) as u8)?; + + match colon_type { + Colons::None => write_hundreds(result, (off / 60 % 60) as u8), + Colons::Single => { + result.push(':'); + write_hundreds(result, (off / 60 % 60) as u8) } - } else { - result.push('Z'); - Ok(()) + Colons::Double => { + result.push(':'); + write_hundreds(result, (off / 60 % 60) as u8)?; + result.push(':'); + write_hundreds(result, (off % 60) as u8) + } + Colons::Triple => Ok(()), } }