use std::ops::{Add, Mul, Sub};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct Duration {
nanos: i128,
}
impl Duration {
pub const fn from_nanos(n: i128) -> Self {
Duration { nanos: n }
}
pub fn Nanoseconds(&self) -> crate::types::int64 {
self.nanos as crate::types::int64
}
pub fn Microseconds(&self) -> crate::types::int64 {
(self.nanos / 1_000) as crate::types::int64
}
pub fn Milliseconds(&self) -> crate::types::int64 {
(self.nanos / 1_000_000) as crate::types::int64
}
pub fn Seconds(&self) -> crate::types::float64 {
self.nanos as f64 / 1_000_000_000.0
}
pub fn Minutes(&self) -> crate::types::float64 {
self.Seconds() / 60.0
}
pub fn Hours(&self) -> crate::types::float64 {
self.Seconds() / 3600.0
}
pub fn to_std(&self) -> std::time::Duration {
if self.nanos <= 0 {
std::time::Duration::ZERO
} else {
std::time::Duration::from_nanos(self.nanos as u64)
}
}
pub fn String(&self) -> crate::types::string {
if self.nanos == 0 {
return "0s".to_string();
}
let mut n = self.nanos;
let neg = n < 0;
if neg {
n = -n;
}
if n < 1_000_000_000 {
let mut prefix = String::new();
if neg { prefix.push('-'); }
if n < 1_000 {
return format!("{}{}ns", prefix, n);
}
if n < 1_000_000 {
return format!("{}{}µs", prefix, n as f64 / 1_000.0);
}
return format!("{}{}ms", prefix, n as f64 / 1_000_000.0);
}
let mut s = String::new();
if neg { s.push('-'); }
let total_secs = n / 1_000_000_000;
let rem_nanos = n % 1_000_000_000;
let hours = total_secs / 3600;
let mins = (total_secs / 60) % 60;
let secs = total_secs % 60;
if hours > 0 {
s.push_str(&format!("{}h", hours));
}
if mins > 0 || hours > 0 {
s.push_str(&format!("{}m", mins));
}
if rem_nanos == 0 {
s.push_str(&format!("{}s", secs));
} else {
let f = secs as f64 + rem_nanos as f64 / 1_000_000_000.0;
s.push_str(&format!("{}s", f));
}
s
}
}
impl std::fmt::Display for Duration {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.String())
}
}
impl Add for Duration {
type Output = Duration;
fn add(self, other: Duration) -> Duration {
Duration::from_nanos(self.nanos + other.nanos)
}
}
impl Sub for Duration {
type Output = Duration;
fn sub(self, other: Duration) -> Duration {
Duration::from_nanos(self.nanos - other.nanos)
}
}
macro_rules! impl_dur_mul {
($($t:ty),+) => {
$(
impl Mul<$t> for Duration {
type Output = Duration;
fn mul(self, rhs: $t) -> Duration {
Duration::from_nanos(self.nanos * rhs as i128)
}
}
impl Mul<Duration> for $t {
type Output = Duration;
fn mul(self, rhs: Duration) -> Duration {
Duration::from_nanos(rhs.nanos * self as i128)
}
}
)+
};
}
impl_dur_mul!(i32, i64, u32, u64, usize);
impl Mul<f64> for Duration {
type Output = Duration;
fn mul(self, rhs: f64) -> Duration {
Duration::from_nanos((self.nanos as f64 * rhs) as i128)
}
}
impl Mul<Duration> for f64 {
type Output = Duration;
fn mul(self, rhs: Duration) -> Duration {
Duration::from_nanos((rhs.nanos as f64 * self) as i128)
}
}
#[allow(non_upper_case_globals)]
pub const Nanosecond: Duration = Duration::from_nanos(1);
#[allow(non_upper_case_globals)]
pub const Microsecond: Duration = Duration::from_nanos(1_000);
#[allow(non_upper_case_globals)]
pub const Millisecond: Duration = Duration::from_nanos(1_000_000);
#[allow(non_upper_case_globals)]
pub const Second: Duration = Duration::from_nanos(1_000_000_000);
#[allow(non_upper_case_globals)]
pub const Minute: Duration = Duration::from_nanos(60 * 1_000_000_000);
#[allow(non_upper_case_globals)]
pub const Hour: Duration = Duration::from_nanos(3600 * 1_000_000_000);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Time {
instant: std::time::Instant,
}
impl Time {
pub fn Sub(self, earlier: Time) -> Duration {
match self.instant.checked_duration_since(earlier.instant) {
Some(d) => Duration::from_nanos(d.as_nanos() as i128),
None => {
let d = earlier.instant.duration_since(self.instant);
Duration::from_nanos(-(d.as_nanos() as i128))
}
}
}
pub fn Add(self, d: Duration) -> Time {
Time {
instant: self.instant + d.to_std(),
}
}
pub fn After(self, other: Time) -> bool {
self.instant > other.instant
}
pub fn Before(self, other: Time) -> bool {
self.instant < other.instant
}
}
#[allow(non_snake_case)]
pub fn Now() -> Time {
Time { instant: std::time::Instant::now() }
}
#[allow(non_snake_case)]
pub fn Since(t: Time) -> Duration {
Now().Sub(t)
}
#[allow(non_snake_case)]
pub fn Until(t: Time) -> Duration {
t.Sub(Now())
}
#[allow(non_snake_case)]
pub fn Sleep(d: Duration) {
std::thread::sleep(d.to_std());
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn duration_arithmetic() {
let one_sec = Second;
assert_eq!(one_sec.Seconds(), 1.0);
assert_eq!(one_sec.Milliseconds(), 1000);
assert_eq!(one_sec.Nanoseconds(), 1_000_000_000);
let two_sec = Second * 2i64;
assert_eq!(two_sec.Seconds(), 2.0);
let three_sec = Second + Second * 2i64;
assert_eq!(three_sec.Seconds(), 3.0);
let half = Millisecond * 500i64;
assert_eq!(half.Milliseconds(), 500);
}
#[test]
fn duration_mul_commutative() {
assert_eq!(Second * 2i64, 2i64 * Second);
assert_eq!((Millisecond * 500i64).Milliseconds(), 500);
}
#[test]
fn duration_string_formatting() {
assert_eq!(Duration::from_nanos(0).String(), "0s");
assert_eq!((Nanosecond * 500i64).String(), "500ns");
assert_eq!((Millisecond * 1500i64).String(), "1.5s");
assert_eq!((Second * 65i64).String(), "1m5s");
assert_eq!((Hour + Minute * 30i64 + Second * 15i64).String(), "1h30m15s");
}
#[test]
fn now_and_since_monotonic() {
let t = Now();
Sleep(Millisecond * 10i64);
let elapsed = Since(t);
assert!(elapsed.Milliseconds() >= 5, "elapsed = {}ms", elapsed.Milliseconds());
assert!(elapsed.Milliseconds() < 2000, "elapsed = {}ms", elapsed.Milliseconds());
}
#[test]
fn time_sub_returns_duration() {
let t1 = Now();
Sleep(Millisecond * 2i64);
let t2 = Now();
let d = t2.Sub(t1);
assert!(d.Nanoseconds() > 0);
}
#[test]
fn time_add_advances() {
let t1 = Now();
let t2 = t1.Add(Second * 10i64);
assert!(t2.After(t1));
assert!(t1.Before(t2));
}
}