1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
//! Local time, with the offset that says which local.
//!
//! The ledger is one dataset read from two places. The CLI printed
//! `13:40:32` from UTC arithmetic while the portal printed `19:00:18` from the
//! browser's clock, and neither said which zone it meant, so correlating a
//! portal event with a ledger row was off by the offset and nothing on either
//! screen admitted it. Both now print local time and both name the offset.
//!
//! The offset comes from the operating system, because that is the only thing
//! that knows it: a zone is a political fact with a history, not a formula.
//! There is no new dependency for it — `libc` is already in the tree for the
//! Unix side, and the Windows side is one documented call.
/// Seconds east of UTC, right now, as this machine reckons it.
///
/// Read per call rather than cached at startup: a daemon left running across a
/// daylight-saving change would otherwise print the old offset for months, and
/// the call is a few microseconds.
pub fn utc_offset_secs() -> i32 {
platform_offset().unwrap_or(0)
}
#[cfg(unix)]
fn platform_offset() -> Option<i32> {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.ok()?
.as_secs() as libc::time_t;
let mut out: libc::tm = unsafe { std::mem::zeroed() };
// SAFETY: `now` is a valid `time_t` and `out` is a live, zeroed `tm` that
// outlives the call. `localtime_r` is the reentrant form precisely so it
// writes into the caller's buffer and touches no global state.
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)]
fn platform_offset() -> Option<i32> {
// 0 unknown, 1 standard, 2 daylight, 0xFFFF_FFFF failed.
const INVALID: u32 = 0xFFFF_FFFF;
const DAYLIGHT: u32 = 2;
let mut info: TimeZoneInformation = unsafe { std::mem::zeroed() };
// SAFETY: `info` is a live, zeroed structure of exactly the layout the API
// documents, and it outlives the call.
let which = unsafe { GetTimeZoneInformation(&mut info) };
if which == INVALID {
return None;
}
// Windows reports minutes *west* of UTC, and splits the seasonal part out.
let bias = info.Bias
+ if which == DAYLIGHT {
info.DaylightBias
} else {
info.StandardBias
};
Some(-bias * 60)
}
#[cfg(not(any(unix, windows)))]
fn platform_offset() -> Option<i32> {
None
}
/// `2026-09-17 19:00:18 +05:30` — one timestamp, saying which clock it is on.
///
/// Written out rather than formatted by a date library, because the whole of
/// what is needed is a civil calendar and an offset, and the one thing that
/// must not vary is what the two surfaces print.
pub fn local_stamp(at: i64) -> String {
let offset = utc_offset_secs();
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
)
}
/// `19:00:18 +05:30` — the same clock, without the date, for a dense table.
pub fn local_clock(at: i64) -> String {
let offset = utc_offset_secs();
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
)
}
/// Seconds since the epoch to a civil date, by Howard Hinnant's `civil_from_days`.
///
/// Proleptic Gregorian, valid either side of the epoch, no table and no leap
/// seconds — which is what a Unix timestamp already assumes.
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));
// 2026-09-17T13:52:00Z, the hour of the drive that found this.
assert_eq!(civil_from_unix(1_789_653_120), (2026, 9, 17, 13, 52, 0));
// Before the epoch, which the formula handles and a naive one does not.
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}");
// The clock form is the tail of the full one.
assert!(local_stamp(0).ends_with(&local_clock(0)));
}
}