#![allow(dead_code)]
use std::time::SystemTime;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct CivilUtc {
pub year: i64,
pub month: u64,
pub day: u64,
pub hour: u64,
pub minute: u64,
pub second: u64,
}
impl CivilUtc {
pub fn format_date(&self) -> String {
format!("{:04}-{:02}-{:02}", self.year, self.month, self.day)
}
pub fn format_iso_utc(&self) -> String {
format!(
"{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z",
self.year, self.month, self.day, self.hour, self.minute, self.second
)
}
}
pub fn now_utc() -> CivilUtc {
from_system_time(SystemTime::now())
}
pub fn from_system_time(t: SystemTime) -> CivilUtc {
let duration = t.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default();
from_unix_seconds(duration.as_secs())
}
pub fn from_unix_seconds(secs: u64) -> CivilUtc {
let days = secs / 86_400;
let sod = secs % 86_400;
let z = days as i64 + 719_468;
let era = if z >= 0 { z } else { z - 146_096 } / 146_097;
let doe = (z - era * 146_097) as u64;
let yoe = (doe - doe / 1_460 + doe / 36_524 - doe / 146_096) / 365;
let y = (yoe as i64) + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let d = doy - (153 * mp + 2) / 5 + 1;
let m = if mp < 10 { mp + 3 } else { mp - 9 };
let y = if m <= 2 { y + 1 } else { y };
CivilUtc {
year: y,
month: m,
day: d,
hour: sod / 3_600,
minute: (sod % 3_600) / 60,
second: sod % 60,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unix_epoch_is_1970_01_01() {
let c = from_unix_seconds(0);
assert_eq!(c.format_date(), "1970-01-01");
assert_eq!(c.format_iso_utc(), "1970-01-01T00:00:00Z");
}
#[test]
fn pinned_midday_instant_formats_correctly() {
let c = from_unix_seconds(1_710_506_096);
assert_eq!(c.year, 2024);
assert_eq!(c.month, 3);
assert_eq!(c.day, 15);
assert_eq!(c.hour, 12);
assert_eq!(c.minute, 34);
assert_eq!(c.second, 56);
assert_eq!(c.format_iso_utc(), "2024-03-15T12:34:56Z");
}
#[test]
fn now_utc_is_plausible_shape() {
let s = now_utc().format_iso_utc();
assert_eq!(s.len(), 20);
assert!(s.ends_with('Z'));
assert!(s.contains('T'));
}
}