commit
c9609ea6c9
|
@ -413,6 +413,7 @@ pub use date::{Date, MIN_DATE, MAX_DATE};
|
||||||
pub use datetime::{DateTime, SecondsFormat};
|
pub use datetime::{DateTime, SecondsFormat};
|
||||||
#[cfg(feature = "rustc-serialize")] pub use datetime::rustc_serialize::TsSeconds;
|
#[cfg(feature = "rustc-serialize")] pub use datetime::rustc_serialize::TsSeconds;
|
||||||
pub use format::{ParseError, ParseResult};
|
pub use format::{ParseError, ParseResult};
|
||||||
|
pub use round::SubsecRound;
|
||||||
|
|
||||||
/// A convenience module appropriate for glob imports (`use chrono::prelude::*;`).
|
/// A convenience module appropriate for glob imports (`use chrono::prelude::*;`).
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
|
@ -422,6 +423,7 @@ pub mod prelude {
|
||||||
#[doc(no_inline)] pub use {NaiveDate, NaiveTime, NaiveDateTime};
|
#[doc(no_inline)] pub use {NaiveDate, NaiveTime, NaiveDateTime};
|
||||||
#[doc(no_inline)] pub use Date;
|
#[doc(no_inline)] pub use Date;
|
||||||
#[doc(no_inline)] pub use {DateTime, SecondsFormat};
|
#[doc(no_inline)] pub use {DateTime, SecondsFormat};
|
||||||
|
#[doc(no_inline)] pub use SubsecRound;
|
||||||
}
|
}
|
||||||
|
|
||||||
// useful throughout the codebase
|
// useful throughout the codebase
|
||||||
|
@ -468,6 +470,7 @@ pub mod naive {
|
||||||
mod date;
|
mod date;
|
||||||
mod datetime;
|
mod datetime;
|
||||||
pub mod format;
|
pub mod format;
|
||||||
|
mod round;
|
||||||
|
|
||||||
/// Serialization/Deserialization in alternate formats
|
/// Serialization/Deserialization in alternate formats
|
||||||
///
|
///
|
||||||
|
|
|
@ -0,0 +1,178 @@
|
||||||
|
// This is a part of Chrono.
|
||||||
|
// See README.md and LICENSE.txt for details.
|
||||||
|
|
||||||
|
use Timelike;
|
||||||
|
use std::ops::{Add, Sub};
|
||||||
|
use oldtime::Duration;
|
||||||
|
|
||||||
|
/// 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::{DateTime, SubsecRound, Timelike, TimeZone, Utc};
|
||||||
|
/// let dt = Utc.ymd(2018, 1, 11).and_hms_milli(12, 0, 0, 154);
|
||||||
|
/// 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::{DateTime, SubsecRound, Timelike, TimeZone, Utc};
|
||||||
|
/// let dt = Utc.ymd(2018, 1, 11).and_hms_milli(12, 0, 0, 154);
|
||||||
|
/// 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<Duration, Output=T> + Sub<Duration, 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 + Duration::nanoseconds(delta_up.into())
|
||||||
|
} else {
|
||||||
|
self - Duration::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 - Duration::nanoseconds(delta_down.into())
|
||||||
|
} else {
|
||||||
|
self // unchanged
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the maximum span in nanoseconds for the target number of digits.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use Timelike;
|
||||||
|
use offset::{FixedOffset, TimeZone, Utc};
|
||||||
|
use super::SubsecRound;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_round() {
|
||||||
|
let pst = FixedOffset::east(8 * 60 * 60);
|
||||||
|
let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_684);
|
||||||
|
|
||||||
|
assert_eq!(dt.round_subsecs(10), dt);
|
||||||
|
assert_eq!(dt.round_subsecs(9), dt);
|
||||||
|
assert_eq!(dt.round_subsecs(8).nanosecond(), 084_660_680);
|
||||||
|
assert_eq!(dt.round_subsecs(7).nanosecond(), 084_660_700);
|
||||||
|
assert_eq!(dt.round_subsecs(6).nanosecond(), 084_661_000);
|
||||||
|
assert_eq!(dt.round_subsecs(5).nanosecond(), 084_660_000);
|
||||||
|
assert_eq!(dt.round_subsecs(4).nanosecond(), 084_700_000);
|
||||||
|
assert_eq!(dt.round_subsecs(3).nanosecond(), 085_000_000);
|
||||||
|
assert_eq!(dt.round_subsecs(2).nanosecond(), 080_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.ymd(2018, 1, 11).and_hms_nano(10, 5, 27, 750_500_000);
|
||||||
|
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.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 1_750_500_000);
|
||||||
|
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() {
|
||||||
|
let pst = FixedOffset::east(8 * 60 * 60);
|
||||||
|
let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_684);
|
||||||
|
|
||||||
|
assert_eq!(dt.trunc_subsecs(10), dt);
|
||||||
|
assert_eq!(dt.trunc_subsecs(9), dt);
|
||||||
|
assert_eq!(dt.trunc_subsecs(8).nanosecond(), 084_660_680);
|
||||||
|
assert_eq!(dt.trunc_subsecs(7).nanosecond(), 084_660_600);
|
||||||
|
assert_eq!(dt.trunc_subsecs(6).nanosecond(), 084_660_000);
|
||||||
|
assert_eq!(dt.trunc_subsecs(5).nanosecond(), 084_660_000);
|
||||||
|
assert_eq!(dt.trunc_subsecs(4).nanosecond(), 084_600_000);
|
||||||
|
assert_eq!(dt.trunc_subsecs(3).nanosecond(), 084_000_000);
|
||||||
|
assert_eq!(dt.trunc_subsecs(2).nanosecond(), 080_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.ymd(2018, 1, 11).and_hms_nano(10, 5, 27, 750_500_000);
|
||||||
|
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.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 1_750_500_000);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue