use std::time::Instant;
pub trait Clock: Send + Sync {
fn now(&self) -> Instant;
fn stamp(&self) -> String;
fn unix(&self) -> i64 {
self.stamp().parse().unwrap_or(0)
}
}
#[derive(Clone, Copy)]
pub struct SystemClock;
impl Clock for SystemClock {
fn now(&self) -> Instant {
Instant::now()
}
fn stamp(&self) -> String {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_or(0, |d| d.as_secs())
.to_string()
}
}
pub(crate) fn format_iso8601(
year: i64,
month: i64,
day: i64,
hour: i64,
minute: i64,
second: i64,
) -> String {
format!("{year:04}-{month:02}-{day:02} {hour:02}:{minute:02}:{second:02}Z")
}
pub fn iso8601_extended(epoch_secs: i64) -> String {
let days = epoch_secs.div_euclid(86_400);
let secs_of_day = epoch_secs.rem_euclid(86_400);
let (year, month, day) = civil_from_days(days);
format_iso8601(
year,
month,
day,
secs_of_day / 3600,
(secs_of_day % 3600) / 60,
secs_of_day % 60,
)
}
fn civil_from_days(z: i64) -> (i64, i64, i64) {
let z = z + 719_468;
let era = if z >= 0 { z } else { z - 146_096 } / 146_097;
let doe = z - era * 146_097; let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365; let y = yoe + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100); let mp = (5 * doy + 2) / 153; let day = doy - (153 * mp + 2) / 5 + 1; let month = if mp < 10 { mp + 3 } else { mp - 9 }; let year = if month <= 2 { y + 1 } else { y };
(year, month, day)
}
pub fn epoch_from_iso8601(stamp: &str) -> Option<i64> {
let b = stamp.as_bytes();
if b.len() != 20 || b.last() != Some(&b'Z') {
return None;
}
let at = |a: usize, z: usize| stamp.get(a..z)?.parse::<i64>().ok();
let sep = |i: usize, c: u8| (b.get(i) == Some(&c)).then_some(());
sep(4, b'-')?;
sep(7, b'-')?;
sep(10, b'T')?;
sep(13, b':')?;
sep(16, b':')?;
let secs = at(11, 13)? * 3600 + at(14, 16)? * 60 + at(17, 19)?;
Some(days_from_civil(at(0, 4)?, at(5, 7)?, at(8, 10)?) * 86_400 + secs)
}
fn days_from_civil(year: i64, month: i64, day: i64) -> i64 {
let y = if month <= 2 { year - 1 } else { year };
let era = if y >= 0 { y } else { y - 399 } / 400;
let yoe = y - era * 400; let mp = if month > 2 { month - 3 } else { month + 9 }; let doy = (153 * mp + 2) / 5 + day - 1; let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; era * 146_097 + doe - 719_468
}