1use std::sync::OnceLock;
7use std::time::Instant;
8
9#[inline]
11pub fn now_ms() -> u64 {
12 static START: OnceLock<Instant> = OnceLock::new();
13 let start = START.get_or_init(Instant::now);
14 start.elapsed().as_millis() as u64
15}
16
17pub const NO_EXPIRY: u64 = 0;
19
20#[inline]
22pub fn is_expired(expires_at_ms: u64) -> bool {
23 expires_at_ms != NO_EXPIRY && now_ms() >= expires_at_ms
24}
25
26#[inline]
28pub fn expiry_from_duration(ttl: Option<std::time::Duration>) -> u64 {
29 ttl.map(|d| {
30 let ms = d.as_millis().min(u64::MAX as u128) as u64;
31 now_ms().saturating_add(ms)
32 })
33 .unwrap_or(NO_EXPIRY)
34}
35
36#[inline]
38pub fn remaining_secs(expires_at_ms: u64) -> Option<u64> {
39 remaining(expires_at_ms, 1000)
40}
41
42#[inline]
44pub fn remaining_ms(expires_at_ms: u64) -> Option<u64> {
45 remaining(expires_at_ms, 1)
46}
47
48#[inline]
50fn remaining(expires_at_ms: u64, divisor: u64) -> Option<u64> {
51 if expires_at_ms == NO_EXPIRY {
52 None
53 } else {
54 let now = now_ms();
55 Some(expires_at_ms.saturating_sub(now) / divisor)
56 }
57}