use std::sync::OnceLock;
use std::time::{Instant, SystemTime, UNIX_EPOCH};
static MONOTONIC_ORIGIN: OnceLock<Instant> = OnceLock::new();
#[must_use]
pub fn monotonic_ns() -> u64 {
let origin = MONOTONIC_ORIGIN.get_or_init(Instant::now);
let elapsed = origin.elapsed().as_nanos();
u64::try_from(elapsed).unwrap_or(u64::MAX)
}
#[must_use]
pub fn wall_clock_ns() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_or(0, |d| u64::try_from(d.as_nanos()).unwrap_or(u64::MAX))
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread::sleep;
use std::time::Duration;
#[test]
fn monotonic_ns_is_nondecreasing() {
let a = monotonic_ns();
let b = monotonic_ns();
assert!(b >= a, "monotonic clock went backwards: {a} -> {b}");
}
#[test]
fn monotonic_ns_advances() {
let a = monotonic_ns();
sleep(Duration::from_millis(2));
let b = monotonic_ns();
assert!(
b - a >= 1_000_000,
"monotonic clock advanced too little: {a} -> {b}"
);
}
#[test]
fn wall_clock_ns_is_in_recent_epoch() {
let t = wall_clock_ns();
assert!(t > 1_577_000_000_000_000_000, "wall clock too low: {t}");
assert!(t < u64::MAX, "wall clock saturated: {t}");
}
#[test]
fn monotonic_and_wall_clock_are_independent() {
let m = monotonic_ns();
let w = wall_clock_ns();
assert_ne!(m, w);
}
}