use std::sync::OnceLock;
use std::time::Instant;
#[inline]
pub fn now_ms() -> u64 {
static START: OnceLock<Instant> = OnceLock::new();
let start = START.get_or_init(Instant::now);
start.elapsed().as_millis() as u64
}
#[inline]
pub fn now_secs() -> u32 {
static START: OnceLock<Instant> = OnceLock::new();
let start = START.get_or_init(Instant::now);
start.elapsed().as_secs() as u32
}
pub const NO_EXPIRY: u64 = 0;
#[inline]
pub fn is_expired(expires_at_ms: u64) -> bool {
expires_at_ms != NO_EXPIRY && now_ms() >= expires_at_ms
}
#[inline]
pub fn expiry_from_duration(ttl: Option<std::time::Duration>) -> u64 {
ttl.map(|d| {
let ms = d.as_millis().min(u64::MAX as u128) as u64;
now_ms().saturating_add(ms)
})
.unwrap_or(NO_EXPIRY)
}
#[inline]
pub fn remaining_secs(expires_at_ms: u64) -> Option<u64> {
remaining(expires_at_ms, 1000)
}
#[inline]
pub fn remaining_ms(expires_at_ms: u64) -> Option<u64> {
remaining(expires_at_ms, 1)
}
#[inline]
fn remaining(expires_at_ms: u64, divisor: u64) -> Option<u64> {
if expires_at_ms == NO_EXPIRY {
None
} else {
let now = now_ms();
Some(expires_at_ms.saturating_sub(now) / divisor)
}
}