use std::sync::LazyLock;
use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use tokio::time::Instant;
const RESYNC_EVERY_MS: u64 = 60 * 60 * 1000;
struct State {
start_instant: Instant,
skew_ms: AtomicI64,
next_resync_elapsed_ms: AtomicU64,
}
static STATE: LazyLock<State> = LazyLock::new(State::init);
impl State {
fn init() -> Self {
let start_instant = Instant::now();
let unix_ms = unix_timestamp_millis_slow();
Self {
start_instant,
skew_ms: AtomicI64::new(unix_ms),
next_resync_elapsed_ms: AtomicU64::new(RESYNC_EVERY_MS),
}
}
#[inline]
fn elapsed_ms_now(&self) -> u64 {
self.start_instant.elapsed().as_millis() as u64
}
#[inline]
fn elapsed_nanos(&self) -> u64 {
self.start_instant.elapsed().as_nanos() as u64
}
#[inline]
fn instant_from_nanos(&self, nanos: u64) -> Instant {
self.start_instant + Duration::from_nanos(nanos)
}
fn maybe_resync(&self, elapsed_now_ms: u64) {
let next = self.next_resync_elapsed_ms.load(Ordering::Relaxed);
if elapsed_now_ms < next {
return;
}
let new_next = elapsed_now_ms.saturating_add(RESYNC_EVERY_MS);
if self
.next_resync_elapsed_ms
.compare_exchange(next, new_next, Ordering::Relaxed, Ordering::Relaxed)
.is_err()
{
return;
}
let unix_now_ms = unix_timestamp_millis_slow();
let new_skew = unix_now_ms - elapsed_now_ms as i64;
self.skew_ms.store(new_skew, Ordering::Relaxed);
}
fn now_unix_ms(&self) -> i64 {
let elapsed_now_ms = self.elapsed_ms_now();
self.maybe_resync(elapsed_now_ms);
let skew = self.skew_ms.load(Ordering::Relaxed);
elapsed_now_ms as i64 + skew
}
}
#[inline(always)]
pub fn unix_timestamp_millis() -> i64 {
unix_timestamp_millis_slow()
}
fn unix_timestamp_millis_slow() -> i64 {
match SystemTime::now().duration_since(UNIX_EPOCH) {
Ok(d) => d.as_millis().try_into().unwrap_or(i64::MAX),
Err(e) => {
let ms: i64 = e.duration().as_millis().try_into().unwrap_or(i64::MAX);
-ms
}
}
}
pub fn now_unix_ms() -> i64 {
STATE.now_unix_ms()
}
#[must_use]
pub fn now_monotonic_nanos() -> u64 {
STATE.elapsed_nanos()
}
#[inline]
pub fn now_unix() -> i64 {
now_unix_ms().div_euclid(1000)
}
#[inline]
pub fn now_system_time() -> SystemTime {
let ms = now_unix_ms();
let epoch = SystemTime::UNIX_EPOCH;
let elapsed_since_epoch = Duration::from_millis(ms.wrapping_abs() as u64);
if ms >= 0 {
epoch + elapsed_since_epoch
} else {
epoch - elapsed_since_epoch
}
}
pub fn time_modulo_index(len: usize) -> usize {
if len == 0 {
return 0;
}
(now_unix_ms() as u64 % len as u64) as usize
}
#[derive(Debug)]
pub struct AtomicInstant(AtomicU64);
impl AtomicInstant {
#[must_use]
pub fn now() -> Self {
Self(AtomicU64::new(now_monotonic_nanos()))
}
pub fn set_now(&self) {
self.0.store(now_monotonic_nanos(), Ordering::Relaxed);
}
#[must_use]
pub fn as_nanos(&self) -> u64 {
self.0.load(Ordering::Relaxed)
}
#[must_use]
pub fn elapsed(&self) -> Duration {
Duration::from_nanos(now_monotonic_nanos().saturating_sub(self.as_nanos()))
}
#[must_use]
pub fn to_instant(&self) -> Instant {
STATE.instant_from_nanos(self.as_nanos())
}
}
impl Default for AtomicInstant {
fn default() -> Self {
Self::now()
}
}
impl From<&AtomicInstant> for Instant {
fn from(value: &AtomicInstant) -> Self {
value.to_instant()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::{thread, time::Duration};
#[test]
fn test_progression_non_decreasing() {
let a = now_unix_ms();
thread::sleep(Duration::from_millis(2));
let b = now_unix_ms();
assert!(b >= a);
}
#[test]
fn now_unix_ms_is_close_to_system_clock() {
let sys = unix_timestamp_millis();
let approx = now_unix_ms();
let diff = (sys - approx).abs();
assert!(diff <= 1_000);
}
#[test]
fn test_skew_math() {
let state = State::init();
let elapsed = state.elapsed_ms_now();
let target_unix = unix_timestamp_millis_slow() + 1234;
state
.skew_ms
.store(target_unix - elapsed as i64, Ordering::Relaxed);
let now = state.now_unix_ms();
assert!(now >= target_unix);
}
#[test]
fn modulo_index_zero_len() {
assert_eq!(time_modulo_index(0), 0);
}
#[test]
fn atomic_instant_roundtrip_and_elapsed() {
let t = AtomicInstant::now();
thread::sleep(Duration::from_millis(5));
assert!(t.elapsed() >= Duration::from_millis(5));
let later = AtomicInstant::now();
assert!(later.as_nanos() > t.as_nanos());
let recovered = t.to_instant();
assert!(recovered.elapsed() >= Duration::from_millis(5));
let before = t.as_nanos();
thread::sleep(Duration::from_millis(1));
t.set_now();
assert!(t.as_nanos() > before);
}
#[test]
fn now_unix_floor_division() {
let sys_s = unix_timestamp_millis() / 1000;
let approx_s = now_unix();
let ms = now_unix_ms();
assert_eq!(approx_s, ms.div_euclid(1000));
assert!((sys_s - approx_s).abs() <= 1);
}
#[test]
fn test_modulo_bounds() {
for len in 1..50 {
assert!(time_modulo_index(len) < len);
}
}
}