pub const DAY_MS: u64 = 86_400_000;
pub fn civil_from_unix_ms(unix_ms: u64) -> (i64, u32, u32) {
let z = (unix_ms / DAY_MS) as i64 + 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 doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let d = (doy - (153 * mp + 2) / 5 + 1) as u32;
let m = if mp < 10 { mp + 3 } else { mp - 9 } as u32;
let y = yoe + era * 400 + if m <= 2 { 1 } else { 0 };
(y, m, d)
}
pub fn unix_ms_from_civil(y: i64, m: u32, d: u32) -> u64 {
let y = if m <= 2 { y - 1 } else { y };
let era = if y >= 0 { y } else { y - 399 } / 400;
let yoe = y - era * 400;
let mp = if m > 2 { m - 3 } else { m + 9 } as i64;
let doy = (153 * mp + 2) / 5 + d as i64 - 1;
let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
let days = era * 146_097 + doe - 719_468;
if days < 0 { 0 } else { days as u64 * DAY_MS }
}
pub fn days_in_month(y: i64, m: u32) -> u32 {
match m {
1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
4 | 6 | 9 | 11 => 30,
_ => {
if (y % 4 == 0 && y % 100 != 0) || y % 400 == 0 { 29 } else { 28 }
}
}
}
pub fn iso_date(unix_ms: u64) -> String {
let (y, m, d) = civil_from_unix_ms(unix_ms);
format!("{y:04}-{m:02}-{d:02}")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trips_known_dates() {
for (y, m, d) in [(1970, 1, 1), (2000, 2, 29), (2026, 9, 3), (2100, 12, 31)] {
let ms = unix_ms_from_civil(y, m, d);
assert_eq!(civil_from_unix_ms(ms), (y, m, d));
assert_eq!(civil_from_unix_ms(ms + DAY_MS - 1), (y, m, d), "end of day stays");
}
assert_eq!(iso_date(unix_ms_from_civil(2026, 9, 3)), "2026-09-03");
}
#[test]
fn refuse_twin_the_day_after_is_not_the_same_day() {
let ms = unix_ms_from_civil(2026, 9, 3);
assert_ne!(civil_from_unix_ms(ms + DAY_MS), (2026, 9, 3));
assert_eq!(iso_date(ms + DAY_MS), "2026-09-04");
assert_eq!(days_in_month(2024, 2), 29);
assert_eq!(days_in_month(1900, 2), 28);
}
}