use std::cell::RefCell;
use std::time::SystemTime;
use time::OffsetDateTime;
type Clock = Box<dyn FnMut() -> SystemTime>;
thread_local! {
static CLOCK: RefCell<Option<Clock>> = const { RefCell::new(None) };
}
#[doc(hidden)]
pub fn with_clock<R>(clock: impl FnMut() -> SystemTime + 'static, f: impl FnOnce() -> R) -> R {
let previous = CLOCK.with(|c| c.borrow_mut().replace(Box::new(clock)));
struct Restore(Option<Clock>);
impl Drop for Restore {
fn drop(&mut self) {
CLOCK.with(|c| *c.borrow_mut() = self.0.take());
}
}
let _restore = Restore(previous);
f()
}
fn fixed_width(t: OffsetDateTime) -> Option<String> {
let t = t.to_offset(time::UtcOffset::UTC);
(0..=9999).contains(&t.year()).then(|| {
format!(
"{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:09}Z",
t.year(),
u8::from(t.month()),
t.day(),
t.hour(),
t.minute(),
t.second(),
t.nanosecond()
)
})
}
const EPOCH: &str = "1970-01-01T00:00:00.000000000Z";
pub fn now_iso8601() -> String {
let overridden = CLOCK.with(|c| c.borrow_mut().as_mut().map(|clock| clock()));
match overridden {
Some(t) => system_time_iso8601(t),
None => fixed_width(OffsetDateTime::now_utc()).unwrap_or_else(|| EPOCH.to_string()),
}
}
pub fn system_time_iso8601(t: SystemTime) -> String {
fixed_width(OffsetDateTime::from(t)).unwrap_or_else(|| EPOCH.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::{Duration, UNIX_EPOCH};
#[test]
fn every_instant_is_written_thirty_characters_wide() {
for nanos in [0u32, 1, 100, 123_400_000, 123_456_000, 999_999_999] {
let t = UNIX_EPOCH + Duration::new(1_790_000_000, nanos);
let text = system_time_iso8601(t);
assert_eq!(text.len(), 30, "{text}");
assert!(text.ends_with('Z'), "{text}");
}
assert_eq!(now_iso8601().len(), 30);
assert_eq!(
system_time_iso8601(UNIX_EPOCH + Duration::new(1_790_000_000, 123_400_000)),
"2026-09-21T14:13:20.123400000Z"
);
}
#[test]
fn text_order_is_time_order() {
let base = 1_790_000_000;
let mut instants: Vec<std::time::SystemTime> = Vec::new();
for secs in [base, base + 1, base + 60, base + 86_400] {
for nanos in [
0,
1,
999,
123_400_000,
123_456_000,
123_456_789,
500_000_000,
] {
instants.push(UNIX_EPOCH + Duration::new(secs, nanos));
}
}
for a in &instants {
for b in &instants {
assert_eq!(
system_time_iso8601(*a).cmp(&system_time_iso8601(*b)),
a.cmp(b),
"{a:?} vs {b:?}"
);
}
}
let early = UNIX_EPOCH + Duration::new(base, 123_400_000);
let late = UNIX_EPOCH + Duration::new(base, 123_456_000);
assert!(system_time_iso8601(early) < system_time_iso8601(late));
}
}