use core::time::Duration;
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a wall-clock source",
label = "this type does not implement `Clock`",
note = "implement `Clock` to inject a wall-clock under `no_std`, or enable the `std` feature to use the built-in `SystemClock`"
)]
pub trait Clock: Send + Sync {
fn unix_time(&self) -> Duration;
}
#[cfg(feature = "std")]
#[derive(Debug, Clone, Copy, Default)]
pub struct SystemClock;
#[cfg(feature = "std")]
impl Clock for SystemClock {
fn unix_time(&self) -> Duration {
#[expect(
clippy::expect_used,
reason = "the system clock predates the Unix epoch only on a grossly misconfigured host"
)]
std::time::SystemTime::now()
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.expect("system time is before the Unix epoch")
}
}
pub fn unix_timestamp() -> Duration {
#[cfg(test)]
#[allow(clippy::significant_drop_in_scrutinee, clippy::expect_used)]
{
if let Some(cell) = NOW_OVERRIDE.get()
&& let Some(override_val) = *cell.lock().expect("lock is poisoned")
{
return override_val;
}
}
#[cfg(feature = "std")]
{
SystemClock.unix_time()
}
#[cfg(not(feature = "std"))]
{
nostd_clock::now()
}
}
#[cfg(feature = "std")]
pub use std::time::Instant;
#[cfg(not(feature = "std"))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Instant;
#[cfg(not(feature = "std"))]
impl Instant {
#[must_use]
pub const fn now() -> Self {
Self
}
#[must_use]
pub const fn elapsed(&self) -> core::time::Duration {
core::time::Duration::ZERO
}
}
#[cfg(not(feature = "std"))]
pub fn set_clock(clock: alloc::boxed::Box<dyn Clock>) {
nostd_clock::set(clock);
}
#[cfg(not(feature = "std"))]
mod nostd_clock {
use super::Clock;
use alloc::boxed::Box;
use core::time::Duration;
use once_cell::race::OnceBox;
static CLOCK: OnceBox<Box<dyn Clock>> = OnceBox::new();
pub fn set(clock: Box<dyn Clock>) {
let _ = CLOCK.set(Box::new(clock));
}
pub fn now() -> Duration {
CLOCK
.get()
.map_or(Duration::ZERO, |clock| clock.unix_time())
}
}
#[cfg(test)]
use std::sync::{Mutex, OnceLock};
#[cfg(test)]
static NOW_OVERRIDE: OnceLock<Mutex<Option<std::time::Duration>>> = OnceLock::new();
#[cfg(test)]
#[allow(clippy::expect_used)]
pub fn set_unix_timestamp_for_test(value: Option<std::time::Duration>) {
let cell = NOW_OVERRIDE.get_or_init(|| Mutex::new(None));
*cell.lock().expect("lock is poisoned") = value;
}
#[cfg(test)]
mod tests {
use super::*;
use test_log::test;
#[cfg(feature = "std")]
#[test]
fn system_clock_unix_time_is_after_a_known_recent_epoch() {
let known_past = Duration::from_secs(1_700_000_000);
assert!(
SystemClock.unix_time() > known_past,
"SystemClock must read the real system clock"
);
}
#[cfg(feature = "std")]
#[test]
fn unix_timestamp_honours_the_test_override() {
set_unix_timestamp_for_test(None);
assert!(
unix_timestamp() > Duration::from_secs(1_700_000_000),
"without an override, unix_timestamp must read the real system clock"
);
let fixed = Duration::from_secs(42);
set_unix_timestamp_for_test(Some(fixed));
assert_eq!(unix_timestamp(), fixed);
set_unix_timestamp_for_test(None);
}
}