Rename Duration to TimeDelta in round module

This commit is contained in:
Paul Dicker 2024-02-02 19:55:18 +01:00 committed by Paul Dicker
parent 20e9a73aa3
commit 3bd9a90e91

View File

@ -1,13 +1,9 @@
// This is a part of Chrono.
// See README.md and LICENSE.txt for details.
//! Functionality for rounding or truncating a `DateTime` by a `Duration`.
//! Functionality for rounding or truncating a `DateTime` by a `TimeDelta`.
use crate::datetime::DateTime;
use crate::duration::Duration;
use crate::NaiveDateTime;
use crate::TimeZone;
use crate::Timelike;
use crate::{DateTime, NaiveDateTime, TimeDelta, TimeZone, Timelike};
use core::cmp::Ordering;
use core::fmt;
use core::marker::Sized;
@ -48,7 +44,7 @@ pub trait SubsecRound {
impl<T> SubsecRound for T
where
T: Timelike + Add<Duration, Output = T> + Sub<Duration, Output = T>,
T: Timelike + Add<TimeDelta, Output = T> + Sub<TimeDelta, Output = T>,
{
fn round_subsecs(self, digits: u16) -> T {
let span = span_for_digits(digits);
@ -56,9 +52,9 @@ where
if delta_down > 0 {
let delta_up = span - delta_down;
if delta_up <= delta_down {
self + Duration::nanoseconds(delta_up.into())
self + TimeDelta::nanoseconds(delta_up.into())
} else {
self - Duration::nanoseconds(delta_down.into())
self - TimeDelta::nanoseconds(delta_down.into())
}
} else {
self // unchanged
@ -69,7 +65,7 @@ where
let span = span_for_digits(digits);
let delta_down = self.nanosecond() % span;
if delta_down > 0 {
self - Duration::nanoseconds(delta_down.into())
self - TimeDelta::nanoseconds(delta_down.into())
} else {
self // unchanged
}
@ -93,13 +89,13 @@ const fn span_for_digits(digits: u16) -> u32 {
}
}
/// Extension trait for rounding or truncating a DateTime by a Duration.
/// Extension trait for rounding or truncating a DateTime by a TimeDelta.
///
/// # Limitations
/// Both rounding and truncating are done via [`Duration::num_nanoseconds`] and
/// Both rounding and truncating are done via [`TimeDelta::num_nanoseconds`] and
/// [`DateTime::timestamp_nanos_opt`]. This means that they will fail if either the
/// `Duration` or the `DateTime` are too big to represented as nanoseconds. They
/// will also fail if the `Duration` is bigger than the timestamp.
/// `TimeDelta` or the `DateTime` are too big to represented as nanoseconds. They
/// will also fail if the `TimeDelta` is bigger than the timestamp.
pub trait DurationRound: Sized {
/// Error that can occur in rounding or truncating
#[cfg(feature = "std")]
@ -109,49 +105,49 @@ pub trait DurationRound: Sized {
#[cfg(not(feature = "std"))]
type Err: fmt::Debug + fmt::Display;
/// Return a copy rounded by Duration.
/// Return a copy rounded by TimeDelta.
///
/// # Example
/// ``` rust
/// # use chrono::{DurationRound, Duration, Utc, NaiveDate};
/// # use chrono::{DurationRound, TimeDelta, Utc, NaiveDate};
/// let dt = NaiveDate::from_ymd_opt(2018, 1, 11).unwrap().and_hms_milli_opt(12, 0, 0, 154).unwrap().and_local_timezone(Utc).unwrap();
/// assert_eq!(
/// dt.duration_round(Duration::milliseconds(10)).unwrap().to_string(),
/// dt.duration_round(TimeDelta::milliseconds(10)).unwrap().to_string(),
/// "2018-01-11 12:00:00.150 UTC"
/// );
/// assert_eq!(
/// dt.duration_round(Duration::days(1)).unwrap().to_string(),
/// dt.duration_round(TimeDelta::days(1)).unwrap().to_string(),
/// "2018-01-12 00:00:00 UTC"
/// );
/// ```
fn duration_round(self, duration: Duration) -> Result<Self, Self::Err>;
fn duration_round(self, duration: TimeDelta) -> Result<Self, Self::Err>;
/// Return a copy truncated by Duration.
/// Return a copy truncated by TimeDelta.
///
/// # Example
/// ``` rust
/// # use chrono::{DurationRound, Duration, Utc, NaiveDate};
/// # use chrono::{DurationRound, TimeDelta, Utc, NaiveDate};
/// let dt = NaiveDate::from_ymd_opt(2018, 1, 11).unwrap().and_hms_milli_opt(12, 0, 0, 154).unwrap().and_local_timezone(Utc).unwrap();
/// assert_eq!(
/// dt.duration_trunc(Duration::milliseconds(10)).unwrap().to_string(),
/// dt.duration_trunc(TimeDelta::milliseconds(10)).unwrap().to_string(),
/// "2018-01-11 12:00:00.150 UTC"
/// );
/// assert_eq!(
/// dt.duration_trunc(Duration::days(1)).unwrap().to_string(),
/// dt.duration_trunc(TimeDelta::days(1)).unwrap().to_string(),
/// "2018-01-11 00:00:00 UTC"
/// );
/// ```
fn duration_trunc(self, duration: Duration) -> Result<Self, Self::Err>;
fn duration_trunc(self, duration: TimeDelta) -> Result<Self, Self::Err>;
}
impl<Tz: TimeZone> DurationRound for DateTime<Tz> {
type Err = RoundingError;
fn duration_round(self, duration: Duration) -> Result<Self, Self::Err> {
fn duration_round(self, duration: TimeDelta) -> Result<Self, Self::Err> {
duration_round(self.naive_local(), self, duration)
}
fn duration_trunc(self, duration: Duration) -> Result<Self, Self::Err> {
fn duration_trunc(self, duration: TimeDelta) -> Result<Self, Self::Err> {
duration_trunc(self.naive_local(), self, duration)
}
}
@ -159,11 +155,11 @@ impl<Tz: TimeZone> DurationRound for DateTime<Tz> {
impl DurationRound for NaiveDateTime {
type Err = RoundingError;
fn duration_round(self, duration: Duration) -> Result<Self, Self::Err> {
fn duration_round(self, duration: TimeDelta) -> Result<Self, Self::Err> {
duration_round(self, self, duration)
}
fn duration_trunc(self, duration: Duration) -> Result<Self, Self::Err> {
fn duration_trunc(self, duration: TimeDelta) -> Result<Self, Self::Err> {
duration_trunc(self, self, duration)
}
}
@ -171,10 +167,10 @@ impl DurationRound for NaiveDateTime {
fn duration_round<T>(
naive: NaiveDateTime,
original: T,
duration: Duration,
duration: TimeDelta,
) -> Result<T, RoundingError>
where
T: Timelike + Add<Duration, Output = T> + Sub<Duration, Output = T>,
T: Timelike + Add<TimeDelta, Output = T> + Sub<TimeDelta, Output = T>,
{
if let Some(span) = duration.num_nanoseconds() {
if span < 0 {
@ -197,9 +193,9 @@ where
(span - delta_down, delta_down)
};
if delta_up <= delta_down {
Ok(original + Duration::nanoseconds(delta_up))
Ok(original + TimeDelta::nanoseconds(delta_up))
} else {
Ok(original - Duration::nanoseconds(delta_down))
Ok(original - TimeDelta::nanoseconds(delta_down))
}
}
} else {
@ -210,10 +206,10 @@ where
fn duration_trunc<T>(
naive: NaiveDateTime,
original: T,
duration: Duration,
duration: TimeDelta,
) -> Result<T, RoundingError>
where
T: Timelike + Add<Duration, Output = T> + Sub<Duration, Output = T>,
T: Timelike + Add<TimeDelta, Output = T> + Sub<TimeDelta, Output = T>,
{
if let Some(span) = duration.num_nanoseconds() {
if span < 0 {
@ -226,40 +222,40 @@ where
let delta_down = stamp % span;
match delta_down.cmp(&0) {
Ordering::Equal => Ok(original),
Ordering::Greater => Ok(original - Duration::nanoseconds(delta_down)),
Ordering::Less => Ok(original - Duration::nanoseconds(span - delta_down.abs())),
Ordering::Greater => Ok(original - TimeDelta::nanoseconds(delta_down)),
Ordering::Less => Ok(original - TimeDelta::nanoseconds(span - delta_down.abs())),
}
} else {
Err(RoundingError::DurationExceedsLimit)
}
}
/// An error from rounding by `Duration`
/// An error from rounding by `TimeDelta`
///
/// See: [`DurationRound`]
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub enum RoundingError {
/// Error when the Duration exceeds the Duration from or until the Unix epoch.
/// Error when the TimeDelta exceeds the TimeDelta from or until the Unix epoch.
///
/// ``` rust
/// # use chrono::{DurationRound, Duration, RoundingError, TimeZone, Utc};
/// # use chrono::{DurationRound, TimeDelta, RoundingError, TimeZone, Utc};
/// let dt = Utc.with_ymd_and_hms(1970, 12, 12, 0, 0, 0).unwrap();
///
/// assert_eq!(
/// dt.duration_round(Duration::days(365)),
/// dt.duration_round(TimeDelta::days(365)),
/// Err(RoundingError::DurationExceedsTimestamp),
/// );
/// ```
DurationExceedsTimestamp,
/// Error when `Duration.num_nanoseconds` exceeds the limit.
/// Error when `TimeDelta.num_nanoseconds` exceeds the limit.
///
/// ``` rust
/// # use chrono::{DurationRound, Duration, RoundingError, Utc, NaiveDate};
/// # use chrono::{DurationRound, TimeDelta, RoundingError, Utc, NaiveDate};
/// let dt = NaiveDate::from_ymd_opt(2260, 12, 31).unwrap().and_hms_nano_opt(23, 59, 59, 1_75_500_000).unwrap().and_local_timezone(Utc).unwrap();
///
/// assert_eq!(
/// dt.duration_round(Duration::days(300 * 365)),
/// dt.duration_round(TimeDelta::days(300 * 365)),
/// Err(RoundingError::DurationExceedsLimit)
/// );
/// ```
@ -268,10 +264,10 @@ pub enum RoundingError {
/// Error when `DateTime.timestamp_nanos` exceeds the limit.
///
/// ``` rust
/// # use chrono::{DurationRound, Duration, RoundingError, TimeZone, Utc};
/// # use chrono::{DurationRound, TimeDelta, RoundingError, TimeZone, Utc};
/// let dt = Utc.with_ymd_and_hms(2300, 12, 12, 0, 0, 0).unwrap();
///
/// assert_eq!(dt.duration_round(Duration::days(1)), Err(RoundingError::TimestampExceedsLimit),);
/// assert_eq!(dt.duration_round(TimeDelta::days(1)), Err(RoundingError::TimestampExceedsLimit),);
/// ```
TimestampExceedsLimit,
}
@ -302,7 +298,7 @@ impl std::error::Error for RoundingError {
#[cfg(test)]
mod tests {
use super::{Duration, DurationRound, RoundingError, SubsecRound};
use super::{DurationRound, RoundingError, SubsecRound, TimeDelta};
use crate::offset::{FixedOffset, TimeZone, Utc};
use crate::Timelike;
use crate::{NaiveDate, NaiveDateTime};
@ -447,12 +443,12 @@ mod tests {
.unwrap();
assert_eq!(
dt.duration_round(Duration::zero()).unwrap().to_string(),
dt.duration_round(TimeDelta::zero()).unwrap().to_string(),
"2016-12-31 23:59:59.175500 UTC"
);
assert_eq!(
dt.duration_round(Duration::milliseconds(10)).unwrap().to_string(),
dt.duration_round(TimeDelta::milliseconds(10)).unwrap().to_string(),
"2016-12-31 23:59:59.180 UTC"
);
@ -466,7 +462,7 @@ mod tests {
)
.unwrap();
assert_eq!(
dt.duration_round(Duration::minutes(5)).unwrap().to_string(),
dt.duration_round(TimeDelta::minutes(5)).unwrap().to_string(),
"2012-12-12 18:25:00 UTC"
);
// round down
@ -479,24 +475,24 @@ mod tests {
)
.unwrap();
assert_eq!(
dt.duration_round(Duration::minutes(5)).unwrap().to_string(),
dt.duration_round(TimeDelta::minutes(5)).unwrap().to_string(),
"2012-12-12 18:20:00 UTC"
);
assert_eq!(
dt.duration_round(Duration::minutes(10)).unwrap().to_string(),
dt.duration_round(TimeDelta::minutes(10)).unwrap().to_string(),
"2012-12-12 18:20:00 UTC"
);
assert_eq!(
dt.duration_round(Duration::minutes(30)).unwrap().to_string(),
dt.duration_round(TimeDelta::minutes(30)).unwrap().to_string(),
"2012-12-12 18:30:00 UTC"
);
assert_eq!(
dt.duration_round(Duration::hours(1)).unwrap().to_string(),
dt.duration_round(TimeDelta::hours(1)).unwrap().to_string(),
"2012-12-12 18:00:00 UTC"
);
assert_eq!(
dt.duration_round(Duration::days(1)).unwrap().to_string(),
dt.duration_round(TimeDelta::days(1)).unwrap().to_string(),
"2012-12-13 00:00:00 UTC"
);
@ -504,11 +500,11 @@ mod tests {
let dt =
FixedOffset::east_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap();
assert_eq!(
dt.duration_round(Duration::days(1)).unwrap().to_string(),
dt.duration_round(TimeDelta::days(1)).unwrap().to_string(),
"2020-10-28 00:00:00 +01:00"
);
assert_eq!(
dt.duration_round(Duration::weeks(1)).unwrap().to_string(),
dt.duration_round(TimeDelta::weeks(1)).unwrap().to_string(),
"2020-10-29 00:00:00 +01:00"
);
@ -516,11 +512,11 @@ mod tests {
let dt =
FixedOffset::west_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap();
assert_eq!(
dt.duration_round(Duration::days(1)).unwrap().to_string(),
dt.duration_round(TimeDelta::days(1)).unwrap().to_string(),
"2020-10-28 00:00:00 -01:00"
);
assert_eq!(
dt.duration_round(Duration::weeks(1)).unwrap().to_string(),
dt.duration_round(TimeDelta::weeks(1)).unwrap().to_string(),
"2020-10-29 00:00:00 -01:00"
);
}
@ -538,12 +534,12 @@ mod tests {
.naive_utc();
assert_eq!(
dt.duration_round(Duration::zero()).unwrap().to_string(),
dt.duration_round(TimeDelta::zero()).unwrap().to_string(),
"2016-12-31 23:59:59.175500"
);
assert_eq!(
dt.duration_round(Duration::milliseconds(10)).unwrap().to_string(),
dt.duration_round(TimeDelta::milliseconds(10)).unwrap().to_string(),
"2016-12-31 23:59:59.180"
);
@ -558,7 +554,7 @@ mod tests {
.unwrap()
.naive_utc();
assert_eq!(
dt.duration_round(Duration::minutes(5)).unwrap().to_string(),
dt.duration_round(TimeDelta::minutes(5)).unwrap().to_string(),
"2012-12-12 18:25:00"
);
// round down
@ -572,24 +568,24 @@ mod tests {
.unwrap()
.naive_utc();
assert_eq!(
dt.duration_round(Duration::minutes(5)).unwrap().to_string(),
dt.duration_round(TimeDelta::minutes(5)).unwrap().to_string(),
"2012-12-12 18:20:00"
);
assert_eq!(
dt.duration_round(Duration::minutes(10)).unwrap().to_string(),
dt.duration_round(TimeDelta::minutes(10)).unwrap().to_string(),
"2012-12-12 18:20:00"
);
assert_eq!(
dt.duration_round(Duration::minutes(30)).unwrap().to_string(),
dt.duration_round(TimeDelta::minutes(30)).unwrap().to_string(),
"2012-12-12 18:30:00"
);
assert_eq!(
dt.duration_round(Duration::hours(1)).unwrap().to_string(),
dt.duration_round(TimeDelta::hours(1)).unwrap().to_string(),
"2012-12-12 18:00:00"
);
assert_eq!(
dt.duration_round(Duration::days(1)).unwrap().to_string(),
dt.duration_round(TimeDelta::days(1)).unwrap().to_string(),
"2012-12-13 00:00:00"
);
}
@ -598,7 +594,7 @@ mod tests {
fn test_duration_round_pre_epoch() {
let dt = Utc.with_ymd_and_hms(1969, 12, 12, 12, 12, 12).unwrap();
assert_eq!(
dt.duration_round(Duration::minutes(10)).unwrap().to_string(),
dt.duration_round(TimeDelta::minutes(10)).unwrap().to_string(),
"1969-12-12 12:10:00 UTC"
);
}
@ -615,7 +611,7 @@ mod tests {
.unwrap();
assert_eq!(
dt.duration_trunc(Duration::milliseconds(10)).unwrap().to_string(),
dt.duration_trunc(TimeDelta::milliseconds(10)).unwrap().to_string(),
"2016-12-31 23:59:59.170 UTC"
);
@ -629,7 +625,7 @@ mod tests {
)
.unwrap();
assert_eq!(
dt.duration_trunc(Duration::minutes(5)).unwrap().to_string(),
dt.duration_trunc(TimeDelta::minutes(5)).unwrap().to_string(),
"2012-12-12 18:20:00 UTC"
);
// would round down
@ -642,23 +638,23 @@ mod tests {
)
.unwrap();
assert_eq!(
dt.duration_trunc(Duration::minutes(5)).unwrap().to_string(),
dt.duration_trunc(TimeDelta::minutes(5)).unwrap().to_string(),
"2012-12-12 18:20:00 UTC"
);
assert_eq!(
dt.duration_trunc(Duration::minutes(10)).unwrap().to_string(),
dt.duration_trunc(TimeDelta::minutes(10)).unwrap().to_string(),
"2012-12-12 18:20:00 UTC"
);
assert_eq!(
dt.duration_trunc(Duration::minutes(30)).unwrap().to_string(),
dt.duration_trunc(TimeDelta::minutes(30)).unwrap().to_string(),
"2012-12-12 18:00:00 UTC"
);
assert_eq!(
dt.duration_trunc(Duration::hours(1)).unwrap().to_string(),
dt.duration_trunc(TimeDelta::hours(1)).unwrap().to_string(),
"2012-12-12 18:00:00 UTC"
);
assert_eq!(
dt.duration_trunc(Duration::days(1)).unwrap().to_string(),
dt.duration_trunc(TimeDelta::days(1)).unwrap().to_string(),
"2012-12-12 00:00:00 UTC"
);
@ -666,11 +662,11 @@ mod tests {
let dt =
FixedOffset::east_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap();
assert_eq!(
dt.duration_trunc(Duration::days(1)).unwrap().to_string(),
dt.duration_trunc(TimeDelta::days(1)).unwrap().to_string(),
"2020-10-27 00:00:00 +01:00"
);
assert_eq!(
dt.duration_trunc(Duration::weeks(1)).unwrap().to_string(),
dt.duration_trunc(TimeDelta::weeks(1)).unwrap().to_string(),
"2020-10-22 00:00:00 +01:00"
);
@ -678,11 +674,11 @@ mod tests {
let dt =
FixedOffset::west_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap();
assert_eq!(
dt.duration_trunc(Duration::days(1)).unwrap().to_string(),
dt.duration_trunc(TimeDelta::days(1)).unwrap().to_string(),
"2020-10-27 00:00:00 -01:00"
);
assert_eq!(
dt.duration_trunc(Duration::weeks(1)).unwrap().to_string(),
dt.duration_trunc(TimeDelta::weeks(1)).unwrap().to_string(),
"2020-10-22 00:00:00 -01:00"
);
}
@ -700,7 +696,7 @@ mod tests {
.naive_utc();
assert_eq!(
dt.duration_trunc(Duration::milliseconds(10)).unwrap().to_string(),
dt.duration_trunc(TimeDelta::milliseconds(10)).unwrap().to_string(),
"2016-12-31 23:59:59.170"
);
@ -715,7 +711,7 @@ mod tests {
.unwrap()
.naive_utc();
assert_eq!(
dt.duration_trunc(Duration::minutes(5)).unwrap().to_string(),
dt.duration_trunc(TimeDelta::minutes(5)).unwrap().to_string(),
"2012-12-12 18:20:00"
);
// would round down
@ -729,23 +725,23 @@ mod tests {
.unwrap()
.naive_utc();
assert_eq!(
dt.duration_trunc(Duration::minutes(5)).unwrap().to_string(),
dt.duration_trunc(TimeDelta::minutes(5)).unwrap().to_string(),
"2012-12-12 18:20:00"
);
assert_eq!(
dt.duration_trunc(Duration::minutes(10)).unwrap().to_string(),
dt.duration_trunc(TimeDelta::minutes(10)).unwrap().to_string(),
"2012-12-12 18:20:00"
);
assert_eq!(
dt.duration_trunc(Duration::minutes(30)).unwrap().to_string(),
dt.duration_trunc(TimeDelta::minutes(30)).unwrap().to_string(),
"2012-12-12 18:00:00"
);
assert_eq!(
dt.duration_trunc(Duration::hours(1)).unwrap().to_string(),
dt.duration_trunc(TimeDelta::hours(1)).unwrap().to_string(),
"2012-12-12 18:00:00"
);
assert_eq!(
dt.duration_trunc(Duration::days(1)).unwrap().to_string(),
dt.duration_trunc(TimeDelta::days(1)).unwrap().to_string(),
"2012-12-12 00:00:00"
);
}
@ -754,7 +750,7 @@ mod tests {
fn test_duration_trunc_pre_epoch() {
let dt = Utc.with_ymd_and_hms(1969, 12, 12, 12, 12, 12).unwrap();
assert_eq!(
dt.duration_trunc(Duration::minutes(10)).unwrap().to_string(),
dt.duration_trunc(TimeDelta::minutes(10)).unwrap().to_string(),
"1969-12-12 12:10:00 UTC"
);
}
@ -762,15 +758,15 @@ mod tests {
#[test]
fn issue1010() {
let dt = NaiveDateTime::from_timestamp_opt(-4_227_854_320, 678_774_288).unwrap();
let span = Duration::microseconds(-7_019_067_213_869_040);
let span = TimeDelta::microseconds(-7_019_067_213_869_040);
assert_eq!(dt.duration_trunc(span), Err(RoundingError::DurationExceedsLimit));
let dt = NaiveDateTime::from_timestamp_opt(320_041_586, 920_103_021).unwrap();
let span = Duration::nanoseconds(-8_923_838_508_697_114_584);
let span = TimeDelta::nanoseconds(-8_923_838_508_697_114_584);
assert_eq!(dt.duration_round(span), Err(RoundingError::DurationExceedsLimit));
let dt = NaiveDateTime::from_timestamp_opt(-2_621_440, 0).unwrap();
let span = Duration::nanoseconds(-9_223_372_036_854_771_421);
let span = TimeDelta::nanoseconds(-9_223_372_036_854_771_421);
assert_eq!(dt.duration_round(span), Err(RoundingError::DurationExceedsLimit));
}
}