use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DurationUnit {
Nanoseconds,
Microseconds,
Milliseconds,
Seconds,
Minutes,
Hours,
Days,
}
impl DurationUnit {
#[inline]
pub const fn suffix(self) -> &'static str {
match self {
DurationUnit::Nanoseconds => "ns",
DurationUnit::Microseconds => "us",
DurationUnit::Milliseconds => "ms",
DurationUnit::Seconds => "s",
DurationUnit::Minutes => "m",
DurationUnit::Hours => "h",
DurationUnit::Days => "d",
}
}
#[inline]
pub fn from_suffix(suffix: &str) -> Option<Self> {
match suffix {
"ns" => Some(DurationUnit::Nanoseconds),
"us" | "µs" | "μs" => Some(DurationUnit::Microseconds),
"ms" => Some(DurationUnit::Milliseconds),
"s" => Some(DurationUnit::Seconds),
"m" => Some(DurationUnit::Minutes),
"h" => Some(DurationUnit::Hours),
"d" => Some(DurationUnit::Days),
_ => None,
}
}
pub fn duration_from_u64(self, value: u64) -> Result<Duration, String> {
match self {
DurationUnit::Nanoseconds => Ok(Duration::from_nanos(value)),
DurationUnit::Microseconds => Ok(Duration::from_micros(value)),
DurationUnit::Milliseconds => Ok(Duration::from_millis(value)),
DurationUnit::Seconds => Ok(Duration::from_secs(value)),
DurationUnit::Minutes => checked_secs(value, 60, "minutes"),
DurationUnit::Hours => checked_secs(value, 60 * 60, "hours"),
DurationUnit::Days => checked_secs(value, 24 * 60 * 60, "days"),
}
}
pub fn rounded_units(self, duration: Duration) -> u128 {
let total_nanos = duration.as_nanos();
let unit_nanos = self.nanos_per_unit();
let quotient = total_nanos / unit_nanos;
let remainder = total_nanos % unit_nanos;
let rounding_threshold = unit_nanos.div_ceil(2);
if remainder >= rounding_threshold {
quotient + 1
} else {
quotient
}
}
const fn nanos_per_unit(self) -> u128 {
match self {
DurationUnit::Nanoseconds => 1,
DurationUnit::Microseconds => 1_000,
DurationUnit::Milliseconds => 1_000_000,
DurationUnit::Seconds => 1_000_000_000,
DurationUnit::Minutes => 60 * 1_000_000_000,
DurationUnit::Hours => 60 * 60 * 1_000_000_000,
DurationUnit::Days => 24 * 60 * 60 * 1_000_000_000,
}
}
}
impl Default for DurationUnit {
fn default() -> Self {
DurationUnit::Milliseconds
}
}
fn checked_secs(value: u64, seconds_per_unit: u64, unit_name: &str) -> Result<Duration, String> {
value
.checked_mul(seconds_per_unit)
.map(Duration::from_secs)
.ok_or_else(|| format!("duration {unit_name} overflow u64 seconds"))
}