use core::time::Duration;
#[cfg(not(feature = "wasm"))]
mod native;
#[cfg(feature = "wasm")]
mod wasm;
#[cfg(not(feature = "wasm"))]
pub use std::time::{SystemTime, UNIX_EPOCH};
#[cfg(feature = "wasm")]
pub use web_time::{SystemTime, UNIX_EPOCH};
#[cfg(not(feature = "wasm"))]
pub use native::now_nanos;
#[cfg(feature = "wasm")]
pub use wasm::now_nanos;
#[must_use]
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::missing_const_for_fn)]
pub fn from_nanos(timestamp_nanos: u128) -> SystemTime {
let secs = (timestamp_nanos / 1_000_000_000) as u64;
let subsec_nanos = (timestamp_nanos % 1_000_000_000) as u32;
UNIX_EPOCH + Duration::new(secs, subsec_nanos)
}
#[must_use]
#[allow(clippy::cast_possible_truncation)]
pub const fn to_duration(timestamp_nanos: u128) -> Duration {
let secs = (timestamp_nanos / 1_000_000_000) as u64;
let subsec_nanos = (timestamp_nanos % 1_000_000_000) as u32;
Duration::new(secs, subsec_nanos)
}
#[cfg(test)]
mod tests {
use super::*;
const MAX_TIMESTAMP_NANOS: u128 = (1u128 << 68) - 1;
#[test]
fn test_now_nanos() {
let nanos = now_nanos().unwrap();
assert!(nanos > 0);
assert!(nanos < MAX_TIMESTAMP_NANOS);
}
#[test]
#[cfg(feature = "wasm")]
fn test_wasm_nanosecond_precision() {
let mut has_non_zero_nanos = false;
let mut last_three_digits_seen = std::collections::HashSet::new();
for _ in 0..1000 {
let nanos = now_nanos().unwrap();
let last_three_digits = nanos % 1000;
last_three_digits_seen.insert(last_three_digits);
if last_three_digits != 0 {
has_non_zero_nanos = true;
}
}
assert!(
has_non_zero_nanos,
"Expected at least microsecond precision with web-time, but all samples had zero nanoseconds"
);
assert!(
last_three_digits_seen.len() > 5,
"Expected variety in nanosecond digits, but only saw {} unique values: {:?}",
last_three_digits_seen.len(),
last_three_digits_seen
);
}
#[test]
#[cfg(feature = "wasm")]
fn test_wasm_non_zero_nanoseconds() {
const MAX_ZERO_ALLOWED: usize = 500; let mut zero_count = 0;
for _ in 0..1000 {
let nanos = now_nanos().unwrap();
let last_three_digits = nanos % 1000;
if last_three_digits == 0 {
zero_count += 1;
assert!(
zero_count <= MAX_ZERO_ALLOWED,
"Too many zero nanosecond values: {zero_count}/1000 samples had zero nanoseconds"
);
}
}
assert!(
zero_count <= MAX_ZERO_ALLOWED,
"Too many zero nanosecond values: {zero_count}/1000 samples had zero nanoseconds"
);
}
#[test]
fn test_from_nanos() {
let nanos = 1_234_567_890_123_456_789u128;
let time = from_nanos(nanos);
let duration = time.duration_since(UNIX_EPOCH).unwrap();
let reconstructed =
u128::from(duration.as_secs()) * 1_000_000_000 + u128::from(duration.subsec_nanos());
assert_eq!(reconstructed, nanos);
}
#[test]
fn test_to_duration() {
let nanos = 5_123_456_789u128;
let duration = to_duration(nanos);
assert_eq!(duration.as_secs(), 5);
assert_eq!(duration.subsec_nanos(), 123_456_789);
}
#[test]
fn test_round_trip() {
let nanos = now_nanos().unwrap();
let time = from_nanos(nanos);
let duration = time.duration_since(UNIX_EPOCH).unwrap();
let reconstructed =
u128::from(duration.as_secs()) * 1_000_000_000 + u128::from(duration.subsec_nanos());
assert_eq!(reconstructed, nanos);
}
#[test]
fn test_zero() {
let time = from_nanos(0);
assert_eq!(time, UNIX_EPOCH);
let duration = to_duration(0);
assert_eq!(duration.as_secs(), 0);
assert_eq!(duration.subsec_nanos(), 0);
}
#[test]
fn test_large_value() {
let nanos = 9_999_999_999_999_999_999u128; let time = from_nanos(nanos);
let duration = time.duration_since(UNIX_EPOCH).unwrap();
assert!(duration.as_secs() > 0);
}
#[test]
fn test_monotonic_ordering() {
let mut prev_nanos = now_nanos().unwrap();
for _ in 0..100 {
let nanos = now_nanos().unwrap();
assert!(
nanos >= prev_nanos,
"Timestamps should be monotonically increasing: {nanos} >= {prev_nanos}"
);
prev_nanos = nanos;
}
}
#[test]
fn test_nanosecond_storage() {
let nanos1 = now_nanos().unwrap();
let nanos2 = now_nanos().unwrap();
assert!(nanos2 >= nanos1);
assert!(nanos1 > 1_000_000_000_000_000_000);
let test_nanos = 1_234_567_890_123_456_789u128;
let time = from_nanos(test_nanos);
let duration = time.duration_since(UNIX_EPOCH).unwrap();
let reconstructed =
u128::from(duration.as_secs()) * 1_000_000_000 + u128::from(duration.subsec_nanos());
assert_eq!(reconstructed, test_nanos);
}
}