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
17#[inline]
23pub fn now_secs() -> u32 {
24 static START: OnceLock<Instant> = OnceLock::new();
25 let start = START.get_or_init(Instant::now);
26 start.elapsed().as_secs() as u32
27}
28
29pub const NO_EXPIRY: u64 = 0;
31
32#[inline]
34pub fn is_expired(expires_at_ms: u64) -> bool {
35 expires_at_ms != NO_EXPIRY && now_ms() >= expires_at_ms
36}
37
38#[inline]
40pub fn expiry_from_duration(ttl: Option<std::time::Duration>) -> u64 {
41 ttl.map(|d| {
42 let ms = d.as_millis().min(u64::MAX as u128) as u64;
43 now_ms().saturating_add(ms)
44 })
45 .unwrap_or(NO_EXPIRY)
46}
47
48#[inline]
50pub fn remaining_secs(expires_at_ms: u64) -> Option<u64> {
51 remaining(expires_at_ms, 1000)
52}
53
54#[inline]
56pub fn remaining_ms(expires_at_ms: u64) -> Option<u64> {
57 remaining(expires_at_ms, 1)
58}
59
60#[inline]
62fn remaining(expires_at_ms: u64, divisor: u64) -> Option<u64> {
63 if expires_at_ms == NO_EXPIRY {
64 None
65 } else {
66 let now = now_ms();
67 Some(expires_at_ms.saturating_sub(now) / divisor)
68 }
69}