use Timelike;
use std::ops::{Add, Sub};
use oldtime::Duration;
pub trait SubsecRound {
fn round_subsecs(self, digits: u16) -> Self;
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
}
}
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
}
}
}
fn span_for_digits(digits: u16) -> u32 {
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);
}
}