use crate::{Roundable, Tie};
use core::time::Duration;
pub const MICROSECOND: Duration = Duration::from_micros(1);
pub const MILLISECOND: Duration = Duration::from_millis(1);
pub const SECOND: Duration = Duration::from_secs(1);
pub const MINUTE: Duration = Duration::from_secs(60);
pub const HOUR: Duration = Duration::from_secs(60 * 60);
impl Roundable for Duration {
fn try_round_to(self, factor: Self, tie: Tie) -> Option<Self> {
self.as_nanos()
.try_round_to(factor.as_nanos(), tie)
.map(nanos_to_duration)
}
}
#[must_use]
pub fn nanos_to_duration(total: u128) -> Duration {
const NANOS_PER_SECOND: u128 = 1_000_000_000;
#[allow(clippy::integer_division)]
Duration::new(
(total / NANOS_PER_SECOND).try_into().expect(
"nanos_to_duration() overflowed seconds value for Duration",
),
(total % NANOS_PER_SECOND).try_into().unwrap(),
)
}
#[cfg(test)]
#[allow(clippy::cognitive_complexity)]
mod tests {
use super::*;
use assert2::check;
const fn ms(ms: u64) -> Duration {
Duration::from_millis(ms)
}
#[test]
fn round_millisecond_to_nearest_millisecond() {
check!(ms(10) == ms(10).round_to(MILLISECOND, Tie::Up));
check!(ms(10) == ms(10).round_to(ms(2), Tie::Up));
check!(ms(10) == ms(9).round_to(ms(2), Tie::Up));
check!(ms(9) == ms(9).round_to(ms(3), Tie::Up));
check!(ms(9) == ms(10).round_to(ms(3), Tie::Up));
check!(ms(12) == ms(11).round_to(ms(3), Tie::Up));
check!(ms(12) == ms(12).round_to(ms(3), Tie::Up));
}
#[test]
fn round_second_to_nearest_millisecond() {
check!(ms(1_010) == ms(1_010).round_to(MILLISECOND, Tie::Up));
check!(ms(1_010) == ms(1_010).round_to(ms(2), Tie::Up));
check!(ms(1_010) == ms(1_009).round_to(ms(2), Tie::Up));
check!(ms(1_008) == ms(1_008).round_to(ms(3), Tie::Up));
check!(ms(1_008) == ms(1_009).round_to(ms(3), Tie::Up));
check!(ms(1_011) == ms(1_010).round_to(ms(3), Tie::Up));
check!(ms(1_011) == ms(1_011).round_to(ms(3), Tie::Up));
}
#[test]
fn round_second_to_nearest_second() {
check!(ms(0) == ms(499).round_to(SECOND, Tie::Up));
check!(SECOND == ms(500).round_to(SECOND, Tie::Up));
check!(SECOND == ms(1_010).round_to(SECOND, Tie::Up));
check!(SECOND == ms(1_499).round_to(SECOND, Tie::Up));
check!(ms(2_000) == ms(1_500).round_to(SECOND, Tie::Up));
check!(ms(1_001) == ms(1_000).round_to(ms(1_001), Tie::Up));
check!(ms(1_001) == ms(1_001).round_to(ms(1_001), Tie::Up));
check!(ms(1_001) == ms(1_002).round_to(ms(1_001), Tie::Up));
}
#[test]
fn round_to_giant_factor() {
check!(ms(0) == ms(1_000_000).round_to(Duration::MAX, Tie::Up));
check!(Duration::MAX == Duration::MAX.round_to(Duration::MAX, Tie::Up));
}
#[test]
#[should_panic(expected = "try_round_to() requires positive factor")]
fn round_to_zero_factor() {
let _ = ms(10).round_to(ms(0), Tie::Up);
}
const NANOS_MAX: u128 = u64::MAX as u128 * 1_000_000_000 + 999_999_999;
#[test]
#[allow(clippy::arithmetic_side_effects)]
fn nanos_to_duration_ok() {
check!(Duration::ZERO == nanos_to_duration(0));
check!(Duration::new(1, 1) == nanos_to_duration(1_000_000_001));
check!(Duration::MAX == nanos_to_duration(Duration::MAX.as_nanos()));
check!(
Duration::new(u64::MAX, 999_999_999)
== nanos_to_duration(NANOS_MAX)
);
}
#[test]
#[should_panic(
expected = "nanos_to_duration() overflowed seconds value for Duration: TryFromIntError(())"
)]
fn nanos_to_duration_overflow() {
let _ = nanos_to_duration(Duration::MAX.as_nanos() + 1);
}
#[test]
#[should_panic(
expected = "nanos_to_duration() overflowed seconds value for Duration: TryFromIntError(())"
)]
#[allow(clippy::arithmetic_side_effects)]
fn nanos_to_duration_overflow_manual() {
let _ = nanos_to_duration(NANOS_MAX + 1);
}
}