use std::fmt;
use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};
use std::time::Duration as StdDuration;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Duration {
pub secs: i64,
pub nanos: i32,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct TimeDelta {
pub secs: i64,
pub nanos: i32,
}
impl Duration {
pub const ZERO: Duration = Duration { secs: 0, nanos: 0 };
pub const SECOND: Duration = Duration { secs: 1, nanos: 0 };
pub const MINUTE: Duration = Duration { secs: 60, nanos: 0 };
pub const HOUR: Duration = Duration { secs: 3600, nanos: 0 };
pub const DAY: Duration = Duration {
secs: 86400,
nanos: 0,
};
pub fn try_new(secs: i64, nanos: i32) -> Option<Duration> {
let d = Self::normalize(secs, nanos);
if d.secs < 0 || d.nanos < 0 {
None
} else {
Some(d)
}
}
pub fn new(secs: i64, nanos: i32) -> Duration {
Self::try_new(secs, nanos).expect("Duration must not be negative")
}
fn normalize(secs: i64, nanos: i32) -> Duration {
let total_nanos = secs as i128 * 1_000_000_000 + nanos as i128;
let s = (total_nanos / 1_000_000_000) as i64;
let n = (total_nanos % 1_000_000_000) as i32;
let n = if n < 0 {
(n + 1_000_000_000) as i32
} else {
n
};
Duration { secs: s, nanos: n }
}
pub fn from_secs(secs: i64) -> Duration {
Duration::new(secs, 0)
}
pub fn from_millis(ms: i64) -> Duration {
Duration::new(ms / 1000, ((ms % 1000) * 1_000_000) as i32)
}
pub fn from_nanos(ns: i64) -> Duration {
Duration::new(ns / 1_000_000_000, (ns % 1_000_000_000) as i32)
}
pub fn from_std(d: StdDuration) -> Duration {
Duration {
secs: d.as_secs() as i64,
nanos: d.subsec_nanos() as i32,
}
}
pub fn to_std(self) -> Option<StdDuration> {
if self.secs >= 0 {
Some(StdDuration::new(self.secs as u64, self.nanos as u32))
} else {
None
}
}
pub fn to_time_delta(self) -> TimeDelta {
TimeDelta {
secs: self.secs,
nanos: self.nanos,
}
}
pub fn total_seconds(&self) -> f64 {
self.secs as f64 + self.nanos as f64 / 1_000_000_000.0
}
pub fn total_millis(&self) -> i64 {
self.secs * 1000 + self.nanos as i64 / 1_000_000
}
}
impl TimeDelta {
pub fn new(secs: i64, nanos: i32) -> TimeDelta {
let total_nanos = secs as i128 * 1_000_000_000 + nanos as i128;
let s = (total_nanos / 1_000_000_000) as i64;
let n = (total_nanos % 1_000_000_000) as i32;
let n = if n < 0 && s <= 0 {
n
} else if n < 0 {
(n + 1_000_000_000) as i32
} else {
n
};
TimeDelta { secs: s, nanos: n }
}
pub fn abs(self) -> Duration {
Duration {
secs: self.secs.abs(),
nanos: self.nanos.abs(),
}
}
pub fn total_seconds(&self) -> f64 {
self.secs as f64 + self.nanos as f64 / 1_000_000_000.0
}
}
impl Add for Duration {
type Output = Duration;
fn add(self, rhs: Duration) -> Duration {
Self::normalize(self.secs + rhs.secs, self.nanos + rhs.nanos)
}
}
impl AddAssign for Duration {
fn add_assign(&mut self, rhs: Duration) {
*self = *self + rhs;
}
}
impl Sub for Duration {
type Output = Duration;
fn sub(self, rhs: Duration) -> Duration {
let secs = self.secs - rhs.secs;
let nanos = self.nanos - rhs.nanos;
let d = Self::normalize(secs, nanos);
if d.secs < 0 {
panic!("Duration subtraction resulted in negative value: {} - {}", self, rhs);
}
d
}
}
impl SubAssign for Duration {
fn sub_assign(&mut self, rhs: Duration) {
*self = *self - rhs;
}
}
impl Neg for TimeDelta {
type Output = TimeDelta;
fn neg(self) -> TimeDelta {
TimeDelta::new(-self.secs, -self.nanos)
}
}
impl Add for TimeDelta {
type Output = TimeDelta;
fn add(self, rhs: TimeDelta) -> TimeDelta {
TimeDelta::new(self.secs + rhs.secs, self.nanos + rhs.nanos)
}
}
impl Sub for TimeDelta {
type Output = TimeDelta;
fn sub(self, rhs: TimeDelta) -> TimeDelta {
TimeDelta::new(self.secs - rhs.secs, self.nanos - rhs.nanos)
}
}
impl From<Duration> for TimeDelta {
fn from(d: Duration) -> TimeDelta {
d.to_time_delta()
}
}
impl fmt::Display for Duration {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}s", self.total_seconds())
}
}
impl fmt::Display for TimeDelta {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}s", self.total_seconds())
}
}