pub fn utc_offset_secs() -> i32 {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_or(0, |d| d.as_secs() as i64);
offset_at(now)
}
pub fn offset_at(at: i64) -> i32 {
platform_offset(at).unwrap_or(0)
}
#[cfg(unix)]
fn platform_offset(at: i64) -> Option<i32> {
let now = at as libc::time_t;
let mut out: libc::tm = unsafe { std::mem::zeroed() };
let filled = unsafe { libc::localtime_r(&now, &mut out) };
if filled.is_null() {
return None;
}
Some(out.tm_gmtoff as i32)
}
#[cfg(windows)]
#[repr(C)]
#[allow(non_snake_case)]
struct TimeZoneInformation {
Bias: i32,
StandardName: [u16; 32],
StandardDate: [u16; 8],
StandardBias: i32,
DaylightName: [u16; 32],
DaylightDate: [u16; 8],
DaylightBias: i32,
}
#[cfg(windows)]
unsafe extern "system" {
fn GetTimeZoneInformation(info: *mut TimeZoneInformation) -> u32;
}
#[cfg(windows)]
#[repr(C)]
#[derive(Default, Clone, Copy)]
#[allow(non_snake_case)]
struct SystemTime {
wYear: u16,
wMonth: u16,
wDayOfWeek: u16,
wDay: u16,
wHour: u16,
wMinute: u16,
wSecond: u16,
wMilliseconds: u16,
}
#[cfg(windows)]
unsafe extern "system" {
fn SystemTimeToTzSpecificLocalTime(
zone: *const TimeZoneInformation,
universal: *const SystemTime,
local: *mut SystemTime,
) -> i32;
}
#[cfg(windows)]
fn platform_offset(at: i64) -> Option<i32> {
let (y, mo, d, h, mi, s) = civil_from_unix(at);
let universal = SystemTime {
wYear: u16::try_from(y).ok()?,
wMonth: mo as u16,
wDay: d as u16,
wHour: h as u16,
wMinute: mi as u16,
wSecond: s as u16,
..Default::default()
};
let mut local = SystemTime::default();
let ok = unsafe { SystemTimeToTzSpecificLocalTime(std::ptr::null(), &universal, &mut local) };
if ok == 0 {
const INVALID: u32 = 0xFFFF_FFFF;
const DAYLIGHT: u32 = 2;
let mut info: TimeZoneInformation = unsafe { std::mem::zeroed() };
let which = unsafe { GetTimeZoneInformation(&mut info) };
if which == INVALID {
return None;
}
let bias = info.Bias
+ if which == DAYLIGHT {
info.DaylightBias
} else {
info.StandardBias
};
return Some(-bias * 60);
}
let local_secs = days_from_civil(
i64::from(local.wYear),
u32::from(local.wMonth),
u32::from(local.wDay),
) * 86_400
+ i64::from(local.wHour) * 3600
+ i64::from(local.wMinute) * 60
+ i64::from(local.wSecond);
i32::try_from(local_secs - at).ok()
}
#[cfg(windows)]
fn days_from_civil(y: i64, m: u32, d: u32) -> i64 {
let y = if m <= 2 { y - 1 } else { y };
let era = y.div_euclid(400);
let yoe = y.rem_euclid(400);
let m = i64::from(m);
let doy = (153 * (if m > 2 { m - 3 } else { m + 9 }) + 2) / 5 + i64::from(d) - 1;
let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
era * 146_097 + doe - 719_468
}
#[cfg(not(any(unix, windows)))]
fn platform_offset(_at: i64) -> Option<i32> {
None
}
pub fn local_stamp(at: i64) -> String {
let offset = offset_at(at);
let (y, mo, d, h, mi, s) = civil_from_unix(at + offset as i64);
let sign = if offset < 0 { '-' } else { '+' };
let off = offset.abs();
format!(
"{y:04}-{mo:02}-{d:02} {h:02}:{mi:02}:{s:02} {sign}{:02}:{:02}",
off / 3600,
(off % 3600) / 60
)
}
pub fn local_clock(at: i64) -> String {
let offset = offset_at(at);
let (_, _, _, h, mi, s) = civil_from_unix(at + offset as i64);
let sign = if offset < 0 { '-' } else { '+' };
let off = offset.abs();
format!(
"{h:02}:{mi:02}:{s:02} {sign}{:02}:{:02}",
off / 3600,
(off % 3600) / 60
)
}
pub fn local_when(at: i64, now: i64) -> String {
let day = |t: i64| {
let (y, mo, d, ..) = civil_from_unix(t + offset_at(t) as i64);
(y, mo, d)
};
if day(at) == day(now) {
local_clock(at)
} else {
local_stamp(at)
}
}
pub fn utc_rfc3339(at: i64) -> String {
let (y, mo, d, h, mi, s) = civil_from_unix(at);
format!("{y:04}-{mo:02}-{d:02}T{h:02}:{mi:02}:{s:02}Z")
}
fn civil_from_unix(at: i64) -> (i64, u32, u32, u32, u32, u32) {
let days = at.div_euclid(86_400);
let rest = at.rem_euclid(86_400);
let z = days + 719_468;
let era = z.div_euclid(146_097);
let doe = z.rem_euclid(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 d = (doy - (153 * mp + 2) / 5 + 1) as u32;
let m = if mp < 10 { mp + 3 } else { mp - 9 } as u32;
let y = if m <= 2 { y + 1 } else { y };
(
y,
m,
d,
(rest / 3600) as u32,
((rest % 3600) / 60) as u32,
(rest % 60) as u32,
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_epoch_and_a_known_instant_convert() {
assert_eq!(civil_from_unix(0), (1970, 1, 1, 0, 0, 0));
assert_eq!(civil_from_unix(1_789_653_120), (2026, 9, 17, 13, 52, 0));
assert_eq!(civil_from_unix(-1), (1969, 12, 31, 23, 59, 59));
}
#[test]
fn a_stamp_carries_its_offset() {
let stamp = local_stamp(1_789_653_120);
let offset = &stamp[stamp.len() - 6..];
assert!(
offset.starts_with('+') || offset.starts_with('-'),
"a timestamp with no offset is the bug this exists to fix: {stamp}"
);
assert_eq!(&offset[3..4], ":", "the offset is written +HH:MM: {stamp}");
assert!(local_stamp(0).ends_with(&local_clock(0)));
}
#[cfg(unix)]
#[test]
fn a_row_carries_the_offset_of_its_own_day() {
unsafe {
std::env::set_var("TZ", "America/New_York");
unsafe extern "C" {
fn tzset();
}
tzset();
}
assert_eq!(local_stamp(1_768_478_400), "2026-01-15 07:00:00 -05:00");
assert_eq!(local_stamp(1_784_116_800), "2026-07-15 08:00:00 -04:00");
let now = 1_784_116_800 + 3_600;
assert_eq!(local_when(1_784_116_800, now), "08:00:00 -04:00");
assert_eq!(local_when(1_768_478_400, now), "2026-01-15 07:00:00 -05:00");
}
}