Skip to content

Commit

Permalink
Optimize to_rfc3339_opts
Browse files Browse the repository at this point in the history
  • Loading branch information
pitdicker committed Jul 25, 2023
1 parent fb646bd commit 161a653
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 57 deletions.
58 changes: 12 additions & 46 deletions src/datetime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,20 @@
//! ISO 8601 date and time with time zone.

#[cfg(all(not(feature = "std"), feature = "alloc"))]
use alloc::string::{String, ToString};
use alloc::string::String;
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")]
use std::string::ToString;
#[cfg(feature = "std")]
use std::time::{SystemTime, UNIX_EPOCH};

#[cfg(any(feature = "alloc", feature = "std"))]
use crate::format::DelayedFormat;
#[cfg(feature = "unstable-locales")]
use crate::format::Locale;
use crate::format::{parse, parse_and_remainder, ParseError, ParseResult, Parsed, StrftimeItems};
#[cfg(any(feature = "alloc", feature = "std"))]
use crate::format::{write_rfc3339, DelayedFormat};
use crate::format::{Fixed, Item};
use crate::naive::{Days, IsoWeek, NaiveDate, NaiveDateTime, NaiveTime};
#[cfg(feature = "clock")]
Expand Down Expand Up @@ -48,6 +46,7 @@ mod tests;
///
/// See the `TimeZone::to_rfc3339_opts` function for usage.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
#[allow(clippy::manual_non_exhaustive)]
pub enum SecondsFormat {
/// Format whole seconds only, with no decimal point nor subseconds.
Secs,
Expand Down Expand Up @@ -508,8 +507,11 @@ impl<Tz: TimeZone> DateTime<Tz> {
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[must_use]
pub fn to_rfc3339(&self) -> String {
// For some reason a string with a capacity less than 32 is ca 20% slower when benchmarking.
let mut result = String::with_capacity(32);
crate::format::write_rfc3339(&mut result, self.naive_local(), self.offset.fix())
let naive = self.naive_local();
let offset = self.offset.fix();
write_rfc3339(&mut result, naive, offset, SecondsFormat::AutoSi, false)
.expect("writing rfc3339 datetime to string should never fail");
result
}
Expand Down Expand Up @@ -542,46 +544,10 @@ impl<Tz: TimeZone> DateTime<Tz> {
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[must_use]
pub fn to_rfc3339_opts(&self, secform: SecondsFormat, use_z: bool) -> String {
use crate::format::Numeric::*;
use crate::format::Pad::Zero;
use crate::SecondsFormat::*;

debug_assert!(secform != __NonExhaustive, "Do not use __NonExhaustive!");

const PREFIX: &[Item<'static>] = &[
Item::Numeric(Year, Zero),
Item::Literal("-"),
Item::Numeric(Month, Zero),
Item::Literal("-"),
Item::Numeric(Day, Zero),
Item::Literal("T"),
Item::Numeric(Hour, Zero),
Item::Literal(":"),
Item::Numeric(Minute, Zero),
Item::Literal(":"),
Item::Numeric(Second, Zero),
];

let ssitem = match secform {
Secs => None,
Millis => Some(Item::Fixed(Fixed::Nanosecond3)),
Micros => Some(Item::Fixed(Fixed::Nanosecond6)),
Nanos => Some(Item::Fixed(Fixed::Nanosecond9)),
AutoSi => Some(Item::Fixed(Fixed::Nanosecond)),
__NonExhaustive => unreachable!(),
};

let tzitem = Item::Fixed(if use_z {
Fixed::TimezoneOffsetColonZ
} else {
Fixed::TimezoneOffsetColon
});

let dt = self.fixed_offset();
match ssitem {
None => dt.format_with_items(PREFIX.iter().chain([tzitem].iter())).to_string(),
Some(s) => dt.format_with_items(PREFIX.iter().chain([s, tzitem].iter())).to_string(),
}
let mut result = String::with_capacity(38);
write_rfc3339(&mut result, self.naive_local(), self.offset.fix(), secform, use_z)
.expect("writing rfc3339 datetime to string should never fail");
result
}

/// The minimum possible `DateTime<Utc>`.
Expand Down
40 changes: 29 additions & 11 deletions src/format/formatting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime};
#[cfg(any(feature = "alloc", feature = "std"))]
use crate::offset::{FixedOffset, Offset};
#[cfg(any(feature = "alloc", feature = "std"))]
use crate::{Datelike, Timelike, Weekday};
use crate::{Datelike, SecondsFormat, Timelike, Weekday};

use super::locales;
#[cfg(any(feature = "alloc", feature = "std"))]
Expand Down Expand Up @@ -427,7 +427,13 @@ fn format_inner(
// same as `%Y-%m-%dT%H:%M:%S%.f%:z`
{
if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
Some(write_rfc3339(w, NaiveDateTime::new(*d, *t), off))
Some(write_rfc3339(
w,
crate::NaiveDateTime::new(*d, *t),
off.fix(),
SecondsFormat::AutoSi,
false,
))
} else {
None
}
Expand Down Expand Up @@ -523,10 +529,13 @@ impl OffsetFormat {

/// 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"))]
#[inline]
pub(crate) fn write_rfc3339(
w: &mut impl Write,
dt: NaiveDateTime,
off: FixedOffset,
secform: SecondsFormat,
use_z: bool,
) -> fmt::Result {
let year = dt.date().year();
if (0..=9999).contains(&year) {
Expand Down Expand Up @@ -556,19 +565,28 @@ pub(crate) fn write_rfc3339(
let sec = sec;
write_hundreds(w, sec as u8)?;

if nano == 0 {
} else if nano % 1_000_000 == 0 {
write!(w, ".{:03}", nano / 1_000_000)?;
} else if nano % 1_000 == 0 {
write!(w, ".{:06}", nano / 1_000)?;
} else {
write!(w, ".{:09}", nano)?;
}
match secform {
SecondsFormat::Secs => {}
SecondsFormat::Millis => write!(w, ".{:03}", nano / 1_000_000)?,
SecondsFormat::Micros => write!(w, ".{:06}", nano / 1000)?,
SecondsFormat::Nanos => write!(w, ".{:09}", nano)?,
SecondsFormat::AutoSi => {
if nano == 0 {
} else if nano % 1_000_000 == 0 {
write!(w, ".{:03}", nano / 1_000_000)?
} else if nano % 1_000 == 0 {
write!(w, ".{:06}", nano / 1_000)?
} else {
write!(w, ".{:09}", nano)?
}
}
SecondsFormat::__NonExhaustive => unreachable!(),
};

OffsetFormat {
precision: OffsetPrecision::Minutes,
colons: Colons::Colon,
allow_zulu: false,
allow_zulu: use_z,
padding: Pad::Zero,
}
.format(w, off)
Expand Down

0 comments on commit 161a653

Please sign in to comment.