Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  round.rs

  Sprache: Rust
 

// This is a part of Chrono.
// See README.md and LICENSE.txt for details.

//! Functionality for rounding or truncating a `DateTime` by a `TimeDelta`.

use crate::{DateTime, NaiveDateTime, TimeDelta, TimeZone, Timelike};
use core::cmp::Ordering;
use core::fmt;
use core::ops::{Add, Sub};

/// Extension trait for subsecond rounding or truncation to a maximum number
/// of digits. Rounding can be used to decrease the error variance when
/// serializing/persisting to lower precision. Truncation is the default
/// behavior in Chrono display formatting.  Either can be used to guarantee
/// equality (e.g. for testing) when round-tripping through a lower precision
/// format.
pub trait SubsecRound {
    /// Return a copy rounded to the specified number of subsecond digits. With
    /// 9 or more digits, self is returned unmodified. Halfway values are
    /// rounded up (away from zero).
    ///
    /// # Example
    /// ``` rust
    /// # use chrono::{SubsecRound, Timelike, NaiveDate};
    /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11)
    ///     .unwrap()
    ///     .and_hms_milli_opt(12, 0, 0, 154)
    ///     .unwrap()
    ///     .and_utc();
    /// assert_eq!(dt.round_subsecs(2).nanosecond(), 150_000_000);
    /// assert_eq!(dt.round_subsecs(1).nanosecond(), 200_000_000);
    /// ```
    fn round_subsecs(self, digits: u16) -> Self;

    /// Return a copy truncated to the specified number of subsecond
    /// digits. With 9 or more digits, self is returned unmodified.
    ///
    /// # Example
    /// ``` rust
    /// # use chrono::{SubsecRound, Timelike, NaiveDate};
    /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11)
    ///     .unwrap()
    ///     .and_hms_milli_opt(12, 0, 0, 154)
    ///     .unwrap()
    ///     .and_utc();
    /// assert_eq!(dt.trunc_subsecs(2).nanosecond(), 150_000_000);
    /// assert_eq!(dt.trunc_subsecs(1).nanosecond(), 100_000_000);
    /// ```
    fn trunc_subsecs(self, digits: u16) -> Self;
}

impl<T> SubsecRound for T
where
    T: Timelike + Add<TimeDelta, Output = T> + Sub<TimeDelta, Output = T>,
{
    fn round_subsecs(self, digits: u16) -> T {
        let span = span_for_digits(digits);
        let delta_down = self.nanosecond() % span;
        if delta_down > 0 {
            let delta_up = span - delta_down;
            if delta_up <= delta_down {
                self + TimeDelta::nanoseconds(delta_up.into())
            } else {
                self - TimeDelta::nanoseconds(delta_down.into())
            }
        } else {
            self // unchanged
        }
    }

    fn trunc_subsecs(self, digits: u16) -> T {
        let span = span_for_digits(digits);
        let delta_down = self.nanosecond() % span;
        if delta_down > 0 {
            self - TimeDelta::nanoseconds(delta_down.into())
        } else {
            self // unchanged
        }
    }
}

// Return the maximum span in nanoseconds for the target number of digits.
const fn span_for_digits(digits: u16) -> u32 {
    // fast lookup form of: 10^(9-min(9,digits))
    match digits {
        0 => 1_000_000_000,
        1 => 100_000_000,
        2 => 10_000_000,
        3 => 1_000_000,
        4 => 100_000,
        5 => 10_000,
        6 => 1_000,
        7 => 100,
        8 => 10,
        _ => 1,
    }
}

/// Extension trait for rounding or truncating a DateTime by a TimeDelta.
///
/// # Limitations
/// Both rounding and truncating are done via [`TimeDelta::num_nanoseconds`] and
/// [`DateTime::timestamp_nanos_opt`]. This means that they will fail if either the
/// `TimeDelta` or the `DateTime` are too big to represented as nanoseconds. They
/// will also fail if the `TimeDelta` is bigger than the timestamp, negative or zero.
pub trait DurationRound: Sized {
    /// Error that can occur in rounding or truncating
    #[cfg(feature = "std")]
    type Err: std::error::Error;

    /// Error that can occur in rounding or truncating
    #[cfg(all(not(feature = "std"), feature = "core-error"))]
    type Err: core::error::Error;

    /// Error that can occur in rounding or truncating
    #[cfg(all(not(feature = "std"), not(feature = "core-error")))]
    type Err: fmt::Debug + fmt::Display;

    /// Return a copy rounded by TimeDelta.
    ///
    /// # Example
    /// ``` rust
    /// # use chrono::{DurationRound, TimeDelta, NaiveDate};
    /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11)
    ///     .unwrap()
    ///     .and_hms_milli_opt(12, 0, 0, 154)
    ///     .unwrap()
    ///     .and_utc();
    /// assert_eq!(
    ///     dt.duration_round(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(),
    ///     "2018-01-11 12:00:00.150 UTC"
    /// );
    /// assert_eq!(
    ///     dt.duration_round(TimeDelta::try_days(1).unwrap()).unwrap().to_string(),
    ///     "2018-01-12 00:00:00 UTC"
    /// );
    /// ```
    fn duration_round(self, duration: TimeDelta) -> Result<SelfSelf::Err>;

    /// Return a copy truncated by TimeDelta.
    ///
    /// # Example
    /// ``` rust
    /// # use chrono::{DurationRound, TimeDelta, NaiveDate};
    /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11)
    ///     .unwrap()
    ///     .and_hms_milli_opt(12, 0, 0, 154)
    ///     .unwrap()
    ///     .and_utc();
    /// assert_eq!(
    ///     dt.duration_trunc(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(),
    ///     "2018-01-11 12:00:00.150 UTC"
    /// );
    /// assert_eq!(
    ///     dt.duration_trunc(TimeDelta::try_days(1).unwrap()).unwrap().to_string(),
    ///     "2018-01-11 00:00:00 UTC"
    /// );
    /// ```
    fn duration_trunc(self, duration: TimeDelta) -> Result<SelfSelf::Err>;

    /// Return a copy rounded **up** by TimeDelta.
    ///
    /// # Example
    /// ``` rust
    /// # use chrono::{DurationRound, TimeDelta, NaiveDate};
    /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11)
    ///     .unwrap()
    ///     .and_hms_milli_opt(12, 0, 0, 154)
    ///     .unwrap()
    ///     .and_utc();
    /// assert_eq!(
    ///     dt.duration_round_up(TimeDelta::milliseconds(10)).unwrap().to_string(),
    ///     "2018-01-11 12:00:00.160 UTC"
    /// );
    /// assert_eq!(
    ///     dt.duration_round_up(TimeDelta::hours(1)).unwrap().to_string(),
    ///     "2018-01-11 13:00:00 UTC"
    /// );
    ///
    /// assert_eq!(
    ///     dt.duration_round_up(TimeDelta::days(1)).unwrap().to_string(),
    ///     "2018-01-12 00:00:00 UTC"
    /// );
    /// ```
    fn duration_round_up(self, duration: TimeDelta) -> Result<SelfSelf::Err>;
}

impl<Tz: TimeZone> DurationRound for DateTime<Tz> {
    type Err = RoundingError;

    fn duration_round(self, duration: TimeDelta) -> Result<SelfSelf::Err> {
        duration_round(self.naive_local(), self, duration)
    }

    fn duration_trunc(self, duration: TimeDelta) -> Result<SelfSelf::Err> {
        duration_trunc(self.naive_local(), self, duration)
    }

    fn duration_round_up(self, duration: TimeDelta) -> Result<SelfSelf::Err> {
        duration_round_up(self.naive_local(), self, duration)
    }
}

impl DurationRound for NaiveDateTime {
    type Err = RoundingError;

    fn duration_round(self, duration: TimeDelta) -> Result<SelfSelf::Err> {
        duration_round(selfself, duration)
    }

    fn duration_trunc(self, duration: TimeDelta) -> Result<SelfSelf::Err> {
        duration_trunc(selfself, duration)
    }

    fn duration_round_up(self, duration: TimeDelta) -> Result<SelfSelf::Err> {
        duration_round_up(selfself, duration)
    }
}

fn duration_round<T>(
    naive: NaiveDateTime,
    original: T,
    duration: TimeDelta,
) -> Result<T, RoundingError>
where
    T: Timelike + Add<TimeDelta, Output = T> + Sub<TimeDelta, Output = T>,
{
    if let Some(span) = duration.num_nanoseconds() {
        if span <= 0 {
            return Err(RoundingError::DurationExceedsLimit);
        }
        let stamp =
            naive.and_utc().timestamp_nanos_opt().ok_or(RoundingError::TimestampExceedsLimit)?;
        let delta_down = stamp % span;
        if delta_down == 0 {
            Ok(original)
        } else {
            let (delta_up, delta_down) = if delta_down < 0 {
                (delta_down.abs(), span - delta_down.abs())
            } else {
                (span - delta_down, delta_down)
            };
            if delta_up <= delta_down {
                Ok(original + TimeDelta::nanoseconds(delta_up))
            } else {
                Ok(original - TimeDelta::nanoseconds(delta_down))
            }
        }
    } else {
        Err(RoundingError::DurationExceedsLimit)
    }
}

fn duration_trunc<T>(
    naive: NaiveDateTime,
    original: T,
    duration: TimeDelta,
) -> Result<T, RoundingError>
where
    T: Timelike + Add<TimeDelta, Output = T> + Sub<TimeDelta, Output = T>,
{
    if let Some(span) = duration.num_nanoseconds() {
        if span <= 0 {
            return Err(RoundingError::DurationExceedsLimit);
        }
        let stamp =
            naive.and_utc().timestamp_nanos_opt().ok_or(RoundingError::TimestampExceedsLimit)?;
        let delta_down = stamp % span;
        match delta_down.cmp(&0) {
            Ordering::Equal => Ok(original),
            Ordering::Greater => Ok(original - TimeDelta::nanoseconds(delta_down)),
            Ordering::Less => Ok(original - TimeDelta::nanoseconds(span - delta_down.abs())),
        }
    } else {
        Err(RoundingError::DurationExceedsLimit)
    }
}

fn duration_round_up<T>(
    naive: NaiveDateTime,
    original: T,
    duration: TimeDelta,
) -> Result<T, RoundingError>
where
    T: Timelike + Add<TimeDelta, Output = T> + Sub<TimeDelta, Output = T>,
{
    if let Some(span) = duration.num_nanoseconds() {
        if span <= 0 {
            return Err(RoundingError::DurationExceedsLimit);
        }
        let stamp =
            naive.and_utc().timestamp_nanos_opt().ok_or(RoundingError::TimestampExceedsLimit)?;
        let delta_down = stamp % span;
        match delta_down.cmp(&0) {
            Ordering::Equal => Ok(original),
            Ordering::Greater => Ok(original + TimeDelta::nanoseconds(span - delta_down)),
            Ordering::Less => Ok(original + TimeDelta::nanoseconds(delta_down.abs())),
        }
    } else {
        Err(RoundingError::DurationExceedsLimit)
    }
}

/// An error from rounding by `TimeDelta`
///
/// See: [`DurationRound`]
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub enum RoundingError {
    /// Error when the TimeDelta exceeds the TimeDelta from or until the Unix epoch.
    ///
    /// Note: this error is not produced anymore.
    DurationExceedsTimestamp,

    /// Error when `TimeDelta.num_nanoseconds` exceeds the limit.
    ///
    /// ``` rust
    /// # use chrono::{DurationRound, TimeDelta, RoundingError, NaiveDate};
    /// let dt = NaiveDate::from_ymd_opt(2260, 12, 31)
    ///     .unwrap()
    ///     .and_hms_nano_opt(23, 59, 59, 1_75_500_000)
    ///     .unwrap()
    ///     .and_utc();
    ///
    /// assert_eq!(
    ///     dt.duration_round(TimeDelta::try_days(300 * 365).unwrap()),
    ///     Err(RoundingError::DurationExceedsLimit)
    /// );
    /// ```
    DurationExceedsLimit,

    /// Error when `DateTime.timestamp_nanos` exceeds the limit.
    ///
    /// ``` rust
    /// # 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(TimeDelta::try_days(1).unwrap()),
    ///     Err(RoundingError::TimestampExceedsLimit)
    /// );
    /// ```
    TimestampExceedsLimit,
}

impl fmt::Display for RoundingError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            RoundingError::DurationExceedsTimestamp => {
                write!(f, "duration in nanoseconds exceeds timestamp")
            }
            RoundingError::DurationExceedsLimit => {
                write!(f, "duration exceeds num_nanoseconds limit")
            }
            RoundingError::TimestampExceedsLimit => {
                write!(f, "timestamp exceeds num_nanoseconds limit")
            }
        }
    }
}

#[cfg(feature = "std")]
impl std::error::Error for RoundingError {
    #[allow(deprecated)]
    fn description(&self) -> &str {
        "error from rounding or truncating with DurationRound"
    }
}

#[cfg(all(not(feature = "std"), feature = "core-error"))]
impl core::error::Error for RoundingError {
    #[allow(deprecated)]
    fn description(&self) -> &str {
        "error from rounding or truncating with DurationRound"
    }
}

#[cfg(test)]
mod tests {
    use super::{DurationRound, RoundingError, SubsecRound, TimeDelta};
    use crate::Timelike;
    use crate::offset::{FixedOffset, TimeZone, Utc};
    use crate::{DateTime, NaiveDate};

    #[test]
    fn test_round_subsecs() {
        let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap();
        let dt = pst
            .from_local_datetime(
                &NaiveDate::from_ymd_opt(2018111)
                    .unwrap()
                    .and_hms_nano_opt(1051384_660_684)
                    .unwrap(),
            )
            .unwrap();

        assert_eq!(dt.round_subsecs(10), dt);
        assert_eq!(dt.round_subsecs(9), dt);
        assert_eq!(dt.round_subsecs(8).nanosecond(), 84_660_680);
        assert_eq!(dt.round_subsecs(7).nanosecond(), 84_660_700);
        assert_eq!(dt.round_subsecs(6).nanosecond(), 84_661_000);
        assert_eq!(dt.round_subsecs(5).nanosecond(), 84_660_000);
        assert_eq!(dt.round_subsecs(4).nanosecond(), 84_700_000);
        assert_eq!(dt.round_subsecs(3).nanosecond(), 85_000_000);
        assert_eq!(dt.round_subsecs(2).nanosecond(), 80_000_000);
        assert_eq!(dt.round_subsecs(1).nanosecond(), 100_000_000);

        assert_eq!(dt.round_subsecs(0).nanosecond(), 0);
        assert_eq!(dt.round_subsecs(0).second(), 13);

        let dt = Utc
            .from_local_datetime(
                &NaiveDate::from_ymd_opt(2018111)
                    .unwrap()
                    .and_hms_nano_opt(10527750_500_000)
                    .unwrap(),
            )
            .unwrap();
        assert_eq!(dt.round_subsecs(9), dt);
        assert_eq!(dt.round_subsecs(4), dt);
        assert_eq!(dt.round_subsecs(3).nanosecond(), 751_000_000);
        assert_eq!(dt.round_subsecs(2).nanosecond(), 750_000_000);
        assert_eq!(dt.round_subsecs(1).nanosecond(), 800_000_000);

        assert_eq!(dt.round_subsecs(0).nanosecond(), 0);
        assert_eq!(dt.round_subsecs(0).second(), 28);
    }

    #[test]
    fn test_round_leap_nanos() {
        let dt = Utc
            .from_local_datetime(
                &NaiveDate::from_ymd_opt(20161231)
                    .unwrap()
                    .and_hms_nano_opt(2359591_750_500_000)
                    .unwrap(),
            )
            .unwrap();
        assert_eq!(dt.round_subsecs(9), dt);
        assert_eq!(dt.round_subsecs(4), dt);
        assert_eq!(dt.round_subsecs(2).nanosecond(), 1_750_000_000);
        assert_eq!(dt.round_subsecs(1).nanosecond(), 1_800_000_000);
        assert_eq!(dt.round_subsecs(1).second(), 59);

        assert_eq!(dt.round_subsecs(0).nanosecond(), 0);
        assert_eq!(dt.round_subsecs(0).second(), 0);
    }

    #[test]
    fn test_trunc_subsecs() {
        let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap();
        let dt = pst
            .from_local_datetime(
                &NaiveDate::from_ymd_opt(2018111)
                    .unwrap()
                    .and_hms_nano_opt(1051384_660_684)
                    .unwrap(),
            )
            .unwrap();

        assert_eq!(dt.trunc_subsecs(10), dt);
        assert_eq!(dt.trunc_subsecs(9), dt);
        assert_eq!(dt.trunc_subsecs(8).nanosecond(), 84_660_680);
        assert_eq!(dt.trunc_subsecs(7).nanosecond(), 84_660_600);
        assert_eq!(dt.trunc_subsecs(6).nanosecond(), 84_660_000);
        assert_eq!(dt.trunc_subsecs(5).nanosecond(), 84_660_000);
        assert_eq!(dt.trunc_subsecs(4).nanosecond(), 84_600_000);
        assert_eq!(dt.trunc_subsecs(3).nanosecond(), 84_000_000);
        assert_eq!(dt.trunc_subsecs(2).nanosecond(), 80_000_000);
        assert_eq!(dt.trunc_subsecs(1).nanosecond(), 0);

        assert_eq!(dt.trunc_subsecs(0).nanosecond(), 0);
        assert_eq!(dt.trunc_subsecs(0).second(), 13);

        let dt = pst
            .from_local_datetime(
                &NaiveDate::from_ymd_opt(2018111)
                    .unwrap()
                    .and_hms_nano_opt(10527750_500_000)
                    .unwrap(),
            )
            .unwrap();
        assert_eq!(dt.trunc_subsecs(9), dt);
        assert_eq!(dt.trunc_subsecs(4), dt);
        assert_eq!(dt.trunc_subsecs(3).nanosecond(), 750_000_000);
        assert_eq!(dt.trunc_subsecs(2).nanosecond(), 750_000_000);
        assert_eq!(dt.trunc_subsecs(1).nanosecond(), 700_000_000);

        assert_eq!(dt.trunc_subsecs(0).nanosecond(), 0);
        assert_eq!(dt.trunc_subsecs(0).second(), 27);
    }

    #[test]
    fn test_trunc_leap_nanos() {
        let dt = Utc
            .from_local_datetime(
                &NaiveDate::from_ymd_opt(20161231)
                    .unwrap()
                    .and_hms_nano_opt(2359591_750_500_000)
                    .unwrap(),
            )
            .unwrap();
        assert_eq!(dt.trunc_subsecs(9), dt);
        assert_eq!(dt.trunc_subsecs(4), dt);
        assert_eq!(dt.trunc_subsecs(2).nanosecond(), 1_750_000_000);
        assert_eq!(dt.trunc_subsecs(1).nanosecond(), 1_700_000_000);
        assert_eq!(dt.trunc_subsecs(1).second(), 59);

        assert_eq!(dt.trunc_subsecs(0).nanosecond(), 1_000_000_000);
        assert_eq!(dt.trunc_subsecs(0).second(), 59);
    }

    #[test]
    fn test_duration_round() {
        let dt = Utc
            .from_local_datetime(
                &NaiveDate::from_ymd_opt(20161231)
                    .unwrap()
                    .and_hms_nano_opt(235959175_500_000)
                    .unwrap(),
            )
            .unwrap();

        assert_eq!(
            dt.duration_round(TimeDelta::new(-10).unwrap()),
            Err(RoundingError::DurationExceedsLimit)
        );
        assert_eq!(dt.duration_round(TimeDelta::zero()), Err(RoundingError::DurationExceedsLimit));

        assert_eq!(
            dt.duration_round(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(),
            "2016-12-31 23:59:59.180 UTC"
        );

        // round up
        let dt = Utc
            .from_local_datetime(
                &NaiveDate::from_ymd_opt(20121212)
                    .unwrap()
                    .and_hms_milli_opt(1822300)
                    .unwrap(),
            )
            .unwrap();
        assert_eq!(
            dt.duration_round(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(),
            "2012-12-12 18:25:00 UTC"
        );
        // round down
        let dt = Utc
            .from_local_datetime(
                &NaiveDate::from_ymd_opt(20121212)
                    .unwrap()
                    .and_hms_milli_opt(182229999)
                    .unwrap(),
            )
            .unwrap();
        assert_eq!(
            dt.duration_round(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(),
            "2012-12-12 18:20:00 UTC"
        );

        assert_eq!(
            dt.duration_round(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(),
            "2012-12-12 18:20:00 UTC"
        );
        assert_eq!(
            dt.duration_round(TimeDelta::try_minutes(30).unwrap()).unwrap().to_string(),
            "2012-12-12 18:30:00 UTC"
        );
        assert_eq!(
            dt.duration_round(TimeDelta::try_hours(1).unwrap()).unwrap().to_string(),
            "2012-12-12 18:00:00 UTC"
        );
        assert_eq!(
            dt.duration_round(TimeDelta::try_days(1).unwrap()).unwrap().to_string(),
            "2012-12-13 00:00:00 UTC"
        );

        // timezone east
        let dt =
            FixedOffset::east_opt(3600).unwrap().with_ymd_and_hms(202010271500).unwrap();
        assert_eq!(
            dt.duration_round(TimeDelta::try_days(1).unwrap()).unwrap().to_string(),
            "2020-10-28 00:00:00 +01:00"
        );
        assert_eq!(
            dt.duration_round(TimeDelta::try_weeks(1).unwrap()).unwrap().to_string(),
            "2020-10-29 00:00:00 +01:00"
        );

        // timezone west
        let dt =
            FixedOffset::west_opt(3600).unwrap().with_ymd_and_hms(202010271500).unwrap();
        assert_eq!(
            dt.duration_round(TimeDelta::try_days(1).unwrap()).unwrap().to_string(),
            "2020-10-28 00:00:00 -01:00"
        );
        assert_eq!(
            dt.duration_round(TimeDelta::try_weeks(1).unwrap()).unwrap().to_string(),
            "2020-10-29 00:00:00 -01:00"
        );
    }

    #[test]
    fn test_duration_round_naive() {
        let dt = Utc
            .from_local_datetime(
                &NaiveDate::from_ymd_opt(20161231)
                    .unwrap()
                    .and_hms_nano_opt(235959175_500_000)
                    .unwrap(),
            )
            .unwrap()
            .naive_utc();

        assert_eq!(
            dt.duration_round(TimeDelta::new(-10).unwrap()),
            Err(RoundingError::DurationExceedsLimit)
        );
        assert_eq!(dt.duration_round(TimeDelta::zero()), Err(RoundingError::DurationExceedsLimit));

        assert_eq!(
            dt.duration_round(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(),
            "2016-12-31 23:59:59.180"
        );

        // round up
        let dt = Utc
            .from_local_datetime(
                &NaiveDate::from_ymd_opt(20121212)
                    .unwrap()
                    .and_hms_milli_opt(1822300)
                    .unwrap(),
            )
            .unwrap()
            .naive_utc();
        assert_eq!(
            dt.duration_round(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(),
            "2012-12-12 18:25:00"
        );
        // round down
        let dt = Utc
            .from_local_datetime(
                &NaiveDate::from_ymd_opt(20121212)
                    .unwrap()
                    .and_hms_milli_opt(182229999)
                    .unwrap(),
            )
            .unwrap()
            .naive_utc();
        assert_eq!(
            dt.duration_round(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(),
            "2012-12-12 18:20:00"
        );

        assert_eq!(
            dt.duration_round(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(),
            "2012-12-12 18:20:00"
        );
        assert_eq!(
            dt.duration_round(TimeDelta::try_minutes(30).unwrap()).unwrap().to_string(),
            "2012-12-12 18:30:00"
        );
        assert_eq!(
            dt.duration_round(TimeDelta::try_hours(1).unwrap()).unwrap().to_string(),
            "2012-12-12 18:00:00"
        );
        assert_eq!(
            dt.duration_round(TimeDelta::try_days(1).unwrap()).unwrap().to_string(),
            "2012-12-13 00:00:00"
        );
    }

    #[test]
    fn test_duration_round_pre_epoch() {
        let dt = Utc.with_ymd_and_hms(19691212121212).unwrap();
        assert_eq!(
            dt.duration_round(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(),
            "1969-12-12 12:10:00 UTC"
        );
    }

    #[test]
    fn test_duration_trunc() {
        let dt = Utc
            .from_local_datetime(
                &NaiveDate::from_ymd_opt(20161231)
                    .unwrap()
                    .and_hms_nano_opt(235959175_500_000)
                    .unwrap(),
            )
            .unwrap();

        assert_eq!(
            dt.duration_trunc(TimeDelta::new(-10).unwrap()),
            Err(RoundingError::DurationExceedsLimit)
        );
        assert_eq!(dt.duration_trunc(TimeDelta::zero()), Err(RoundingError::DurationExceedsLimit));

        assert_eq!(
            dt.duration_trunc(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(),
            "2016-12-31 23:59:59.170 UTC"
        );

        // would round up
        let dt = Utc
            .from_local_datetime(
                &NaiveDate::from_ymd_opt(20121212)
                    .unwrap()
                    .and_hms_milli_opt(1822300)
                    .unwrap(),
            )
            .unwrap();
        assert_eq!(
            dt.duration_trunc(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(),
            "2012-12-12 18:20:00 UTC"
        );
        // would round down
        let dt = Utc
            .from_local_datetime(
                &NaiveDate::from_ymd_opt(20121212)
                    .unwrap()
                    .and_hms_milli_opt(182229999)
                    .unwrap(),
            )
            .unwrap();
        assert_eq!(
            dt.duration_trunc(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(),
            "2012-12-12 18:20:00 UTC"
        );
        assert_eq!(
            dt.duration_trunc(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(),
            "2012-12-12 18:20:00 UTC"
        );
        assert_eq!(
            dt.duration_trunc(TimeDelta::try_minutes(30).unwrap()).unwrap().to_string(),
            "2012-12-12 18:00:00 UTC"
        );
        assert_eq!(
            dt.duration_trunc(TimeDelta::try_hours(1).unwrap()).unwrap().to_string(),
            "2012-12-12 18:00:00 UTC"
        );
        assert_eq!(
            dt.duration_trunc(TimeDelta::try_days(1).unwrap()).unwrap().to_string(),
            "2012-12-12 00:00:00 UTC"
        );

        // timezone east
        let dt =
            FixedOffset::east_opt(3600).unwrap().with_ymd_and_hms(202010271500).unwrap();
        assert_eq!(
            dt.duration_trunc(TimeDelta::try_days(1).unwrap()).unwrap().to_string(),
            "2020-10-27 00:00:00 +01:00"
        );
        assert_eq!(
            dt.duration_trunc(TimeDelta::try_weeks(1).unwrap()).unwrap().to_string(),
            "2020-10-22 00:00:00 +01:00"
        );

        // timezone west
        let dt =
            FixedOffset::west_opt(3600).unwrap().with_ymd_and_hms(202010271500).unwrap();
        assert_eq!(
            dt.duration_trunc(TimeDelta::try_days(1).unwrap()).unwrap().to_string(),
            "2020-10-27 00:00:00 -01:00"
        );
        assert_eq!(
            dt.duration_trunc(TimeDelta::try_weeks(1).unwrap()).unwrap().to_string(),
            "2020-10-22 00:00:00 -01:00"
        );
    }

    #[test]
    fn test_duration_trunc_naive() {
        let dt = Utc
            .from_local_datetime(
                &NaiveDate::from_ymd_opt(20161231)
                    .unwrap()
                    .and_hms_nano_opt(235959175_500_000)
                    .unwrap(),
            )
            .unwrap()
            .naive_utc();

        assert_eq!(
            dt.duration_trunc(TimeDelta::new(-10).unwrap()),
            Err(RoundingError::DurationExceedsLimit)
        );
        assert_eq!(dt.duration_trunc(TimeDelta::zero()), Err(RoundingError::DurationExceedsLimit));

        assert_eq!(
            dt.duration_trunc(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(),
            "2016-12-31 23:59:59.170"
        );

        // would round up
        let dt = Utc
            .from_local_datetime(
                &NaiveDate::from_ymd_opt(20121212)
                    .unwrap()
                    .and_hms_milli_opt(1822300)
                    .unwrap(),
            )
            .unwrap()
            .naive_utc();
        assert_eq!(
            dt.duration_trunc(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(),
            "2012-12-12 18:20:00"
        );
        // would round down
        let dt = Utc
            .from_local_datetime(
                &NaiveDate::from_ymd_opt(20121212)
                    .unwrap()
                    .and_hms_milli_opt(182229999)
                    .unwrap(),
            )
            .unwrap()
            .naive_utc();
        assert_eq!(
            dt.duration_trunc(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(),
            "2012-12-12 18:20:00"
        );
        assert_eq!(
            dt.duration_trunc(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(),
            "2012-12-12 18:20:00"
        );
        assert_eq!(
            dt.duration_trunc(TimeDelta::try_minutes(30).unwrap()).unwrap().to_string(),
            "2012-12-12 18:00:00"
        );
        assert_eq!(
            dt.duration_trunc(TimeDelta::try_hours(1).unwrap()).unwrap().to_string(),
            "2012-12-12 18:00:00"
        );
        assert_eq!(
            dt.duration_trunc(TimeDelta::try_days(1).unwrap()).unwrap().to_string(),
            "2012-12-12 00:00:00"
        );
    }

    #[test]
    fn test_duration_trunc_pre_epoch() {
        let dt = Utc.with_ymd_and_hms(19691212121212).unwrap();
        assert_eq!(
            dt.duration_trunc(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(),
            "1969-12-12 12:10:00 UTC"
        );
    }

    #[test]
    fn issue1010() {
        let dt = DateTime::from_timestamp(-4_227_854_320678_774_288).unwrap();
        let span = TimeDelta::microseconds(-7_019_067_213_869_040);
        assert_eq!(dt.duration_trunc(span), Err(RoundingError::DurationExceedsLimit));

        let dt = DateTime::from_timestamp(320_041_586920_103_021).unwrap();
        let span = TimeDelta::nanoseconds(-8_923_838_508_697_114_584);
        assert_eq!(dt.duration_round(span), Err(RoundingError::DurationExceedsLimit));

        let dt = DateTime::from_timestamp(-2_621_4400).unwrap();
        let span = TimeDelta::nanoseconds(-9_223_372_036_854_771_421);
        assert_eq!(dt.duration_round(span), Err(RoundingError::DurationExceedsLimit));
    }

    #[test]
    fn test_duration_trunc_close_to_epoch() {
        let span = TimeDelta::try_minutes(15).unwrap();

        let dt = NaiveDate::from_ymd_opt(197011).unwrap().and_hms_opt(0015).unwrap();
        assert_eq!(dt.duration_trunc(span).unwrap().to_string(), "1970-01-01 00:00:00");

        let dt = NaiveDate::from_ymd_opt(19691231).unwrap().and_hms_opt(235945).unwrap();
        assert_eq!(dt.duration_trunc(span).unwrap().to_string(), "1969-12-31 23:45:00");
    }

    #[test]
    fn test_duration_round_close_to_epoch() {
        let span = TimeDelta::try_minutes(15).unwrap();

        let dt = NaiveDate::from_ymd_opt(197011).unwrap().and_hms_opt(0015).unwrap();
        assert_eq!(dt.duration_round(span).unwrap().to_string(), "1970-01-01 00:00:00");

        let dt = NaiveDate::from_ymd_opt(19691231).unwrap().and_hms_opt(235945).unwrap();
        assert_eq!(dt.duration_round(span).unwrap().to_string(), "1970-01-01 00:00:00");
    }

    #[test]
    fn test_duration_round_close_to_min_max() {
        let span = TimeDelta::nanoseconds(i64::MAX);

        let dt = DateTime::from_timestamp_nanos(i64::MIN / 2 - 1);
        assert_eq!(
            dt.duration_round(span).unwrap().to_string(),
            "1677-09-21 00:12:43.145224193 UTC"
        );

        let dt = DateTime::from_timestamp_nanos(i64::MIN / 2 + 1);
        assert_eq!(dt.duration_round(span).unwrap().to_string(), "1970-01-01 00:00:00 UTC");

        let dt = DateTime::from_timestamp_nanos(i64::MAX / 2 + 1);
        assert_eq!(
            dt.duration_round(span).unwrap().to_string(),
            "2262-04-11 23:47:16.854775807 UTC"
        );

        let dt = DateTime::from_timestamp_nanos(i64::MAX / 2 - 1);
        assert_eq!(dt.duration_round(span).unwrap().to_string(), "1970-01-01 00:00:00 UTC");
    }

    #[test]
    fn test_duration_round_up() {
        let dt = NaiveDate::from_ymd_opt(20161231)
            .unwrap()
            .and_hms_nano_opt(235959175_500_000)
            .unwrap()
            .and_utc();

        assert_eq!(
            dt.duration_round_up(TimeDelta::new(-10).unwrap()),
            Err(RoundingError::DurationExceedsLimit)
        );

        assert_eq!(
            dt.duration_round_up(TimeDelta::zero()),
            Err(RoundingError::DurationExceedsLimit)
        );

        assert_eq!(dt.duration_round_up(TimeDelta::MAX), Err(RoundingError::DurationExceedsLimit));

        assert_eq!(
            dt.duration_round_up(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(),
            "2016-12-31 23:59:59.180 UTC"
        );

        // round up
        let dt = NaiveDate::from_ymd_opt(20121212)
            .unwrap()
            .and_hms_milli_opt(1822300)
            .unwrap()
            .and_utc();

        assert_eq!(
            dt.duration_round_up(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(),
            "2012-12-12 18:25:00 UTC"
        );

        assert_eq!(
            dt.duration_round_up(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(),
            "2012-12-12 18:30:00 UTC"
        );
        assert_eq!(
            dt.duration_round_up(TimeDelta::try_minutes(30).unwrap()).unwrap().to_string(),
            "2012-12-12 18:30:00 UTC"
        );
        assert_eq!(
            dt.duration_round_up(TimeDelta::try_hours(1).unwrap()).unwrap().to_string(),
            "2012-12-12 19:00:00 UTC"
        );
        assert_eq!(
            dt.duration_round_up(TimeDelta::try_days(1).unwrap()).unwrap().to_string(),
            "2012-12-13 00:00:00 UTC"
        );

        // timezone east
        let dt =
            FixedOffset::east_opt(3600).unwrap().with_ymd_and_hms(202010271500).unwrap();
        assert_eq!(
            dt.duration_round_up(TimeDelta::try_days(1).unwrap()).unwrap().to_string(),
            "2020-10-28 00:00:00 +01:00"
        );
        assert_eq!(
            dt.duration_round_up(TimeDelta::try_weeks(1).unwrap()).unwrap().to_string(),
            "2020-10-29 00:00:00 +01:00"
        );

        // timezone west
        let dt =
            FixedOffset::west_opt(3600).unwrap().with_ymd_and_hms(202010271500).unwrap();
        assert_eq!(
            dt.duration_round_up(TimeDelta::try_days(1).unwrap()).unwrap().to_string(),
            "2020-10-28 00:00:00 -01:00"
        );
        assert_eq!(
            dt.duration_round_up(TimeDelta::try_weeks(1).unwrap()).unwrap().to_string(),
            "2020-10-29 00:00:00 -01:00"
        );
    }

    #[test]
    fn test_duration_round_up_naive() {
        let dt = NaiveDate::from_ymd_opt(20161231)
            .unwrap()
            .and_hms_nano_opt(235959175_500_000)
            .unwrap();

        assert_eq!(
            dt.duration_round_up(TimeDelta::new(-10).unwrap()),
            Err(RoundingError::DurationExceedsLimit)
        );
        assert_eq!(
            dt.duration_round_up(TimeDelta::zero()),
            Err(RoundingError::DurationExceedsLimit)
        );

        assert_eq!(dt.duration_round_up(TimeDelta::MAX), Err(RoundingError::DurationExceedsLimit));

        assert_eq!(
            dt.duration_round_up(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(),
            "2016-12-31 23:59:59.180"
        );

        let dt = Utc
            .from_local_datetime(
                &NaiveDate::from_ymd_opt(20121212)
                    .unwrap()
                    .and_hms_milli_opt(1822300)
                    .unwrap(),
            )
            .unwrap()
            .naive_utc();
        assert_eq!(
            dt.duration_round_up(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(),
            "2012-12-12 18:25:00"
        );
        assert_eq!(
            dt.duration_round_up(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(),
            "2012-12-12 18:30:00"
        );
        assert_eq!(
            dt.duration_round_up(TimeDelta::try_minutes(30).unwrap()).unwrap().to_string(),
            "2012-12-12 18:30:00"
        );
        assert_eq!(
            dt.duration_round_up(TimeDelta::try_hours(1).unwrap()).unwrap().to_string(),
            "2012-12-12 19:00:00"
        );
        assert_eq!(
            dt.duration_round_up(TimeDelta::try_days(1).unwrap()).unwrap().to_string(),
            "2012-12-13 00:00:00"
        );
    }

    #[test]
    fn test_duration_round_up_pre_epoch() {
        let dt = Utc.with_ymd_and_hms(19691212121212).unwrap();
        assert_eq!(
            dt.duration_round_up(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(),
            "1969-12-12 12:20:00 UTC"
        );

        let time_delta = TimeDelta::minutes(30);
        assert_eq!(
            DateTime::UNIX_EPOCH.duration_round_up(time_delta).unwrap().to_string(),
            "1970-01-01 00:00:00 UTC"
        )
    }

    #[test]
    fn test_duration_round_up_close_to_min_max() {
        let mut dt = NaiveDate::from_ymd_opt(20121212)
            .unwrap()
            .and_hms_milli_opt(1822300)
            .unwrap()
            .and_utc();

        let span = TimeDelta::nanoseconds(i64::MAX);

        assert_eq!(
            dt.duration_round_up(span).unwrap().to_string(),
            DateTime::from_timestamp_nanos(i64::MAX).to_string()
        );

        dt = DateTime::UNIX_EPOCH + TimeDelta::nanoseconds(1);
        assert_eq!(dt.duration_round_up(span).unwrap(), DateTime::from_timestamp_nanos(i64::MAX));

        let dt = DateTime::from_timestamp_nanos(1);
        assert_eq!(
            dt.duration_round_up(span).unwrap().to_string(),
            "2262-04-11 23:47:16.854775807 UTC"
        );

        let dt = DateTime::from_timestamp_nanos(-1);
        assert_eq!(dt.duration_round_up(span).unwrap(), DateTime::UNIX_EPOCH);

        // Rounds to 1677-09-21 00:12:43.145224193 UTC if at i64::MIN.
        // because i64::MIN is 1677-09-21 00:12:43.145224192 UTC.
        //
        //                                                v
        // We add 2 to get to 1677-09-21 00:12:43.145224194 UTC
        // this issue is because abs(i64::MIN) == i64::MAX + 1
        let dt = DateTime::from_timestamp_nanos(i64::MIN + 2);
        assert_eq!(dt.duration_round_up(span).unwrap(), DateTime::UNIX_EPOCH);
    }
}

Messung V0.5 in Prozent
C=85 H=100 G=92

¤ Dauer der Verarbeitung: 0.14 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002