1use std::time::{SystemTime, UNIX_EPOCH};
9
10pub fn now_ms() -> u64 {
13 SystemTime::now()
14 .duration_since(UNIX_EPOCH)
15 .map(|d| u64::try_from(d.as_millis()).unwrap_or(u64::MAX))
16 .unwrap_or(0)
17}
18
19pub fn iso8601_ms(epoch_ms: u64) -> String {
23 let secs = epoch_ms / crate::units::MS_PER_SEC;
24 let ms = epoch_ms % crate::units::MS_PER_SEC;
25 let days = secs / crate::units::SECS_PER_DAY;
26 let rem = secs % crate::units::SECS_PER_DAY;
27 let (h, m, s) = (rem / 3600, (rem % 3600) / 60, rem % 60);
28 let (year, month, day) = civil_from_days(days as i64);
29 format!("{year:04}-{month:02}-{day:02}T{h:02}:{m:02}:{s:02}.{ms:03}Z")
30}
31
32pub fn now_iso8601() -> String {
34 iso8601_ms(now_ms())
35}
36
37pub fn elapsed_us(started: std::time::Instant) -> u64 {
40 u64::try_from(started.elapsed().as_micros()).unwrap_or(u64::MAX)
41}
42
43fn civil_from_days(z: i64) -> (i64, u32, u32) {
45 let z = z + 719_468;
46 let era = if z >= 0 { z } else { z - 146_096 } / 146_097;
47 let doe = z - era * 146_097;
48 let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365;
49 let y = yoe + era * 400;
50 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
51 let mp = (5 * doy + 2) / 153;
52 let d = doy - (153 * mp + 2) / 5 + 1;
53 let m = if mp < 10 { mp + 3 } else { mp - 9 };
54 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
55 (if m <= 2 { y + 1 } else { y }, m as u32, d as u32)
56}
57
58#[cfg(test)]
59mod tests {
60 #![allow(clippy::indexing_slicing)]
61
62 use super::*;
63
64 #[test]
68 fn elapsed_us_measures_real_time() {
69 let started = std::time::Instant::now();
70 std::thread::sleep(std::time::Duration::from_millis(10));
71 assert!(elapsed_us(started) >= 2_000);
72 }
73
74 #[test]
75 fn epoch_zero() {
76 assert_eq!(iso8601_ms(0), "1970-01-01T00:00:00.000Z");
77 }
78
79 #[test]
80 fn known_timestamp() {
81 assert_eq!(iso8601_ms(1_786_320_000_000), "2026-08-10T00:00:00.000Z");
83 }
84
85 #[test]
93 fn civil_from_days_handles_negative_days_and_era_boundaries() {
94 assert_eq!(civil_from_days(-1), (1969, 12, 31));
96 assert_eq!(civil_from_days(-719_468), (0, 3, 1));
98 assert_eq!(civil_from_days(-719_469), (0, 2, 29));
101 assert_eq!(civil_from_days(11_016), (2000, 2, 29));
104 assert_eq!(civil_from_days(47_540), (2100, 2, 28));
105 assert_eq!(civil_from_days(47_541), (2100, 3, 1));
106 }
107
108 #[test]
109 fn leap_year_feb_29() {
110 assert_eq!(iso8601_ms(1_709_210_096_789), "2024-02-29T12:34:56.789Z");
112 }
113
114 #[test]
115 fn year_2038_safe() {
116 assert_eq!(iso8601_ms(4_102_444_800_000), "2100-01-01T00:00:00.000Z");
118 }
119
120 #[test]
121 fn now_is_sane() {
122 let now = now_ms();
123 assert!(now > 1_700_000_000_000, "clock reads before Nov 2023: {now}");
124 let iso = now_iso8601();
125 assert!(iso.ends_with('Z') && iso.len() == 24, "bad iso format: {iso}");
126 }
127
128 #[test]
129 fn civil_from_days_exercises_month_and_day_arithmetic() {
130 let cases: &[(u64, &str)] = &[
135 (0, "1970-01-01T00:00:00.000Z"),
136 (1_709_251_200_000, "2024-03-01T00:00:00.000Z"),
137 (4_102_358_400_000, "2099-12-31T00:00:00.000Z"),
138 (951_782_400_000, "2000-02-29T00:00:00.000Z"),
139 ];
140 for (epoch_ms, expected) in cases {
141 assert_eq!(&iso8601_ms(*epoch_ms), expected, "epoch_ms={epoch_ms}");
142 }
143 }
144
145 #[test]
152 fn iso8601_ms_always_emits_utc_zulu() {
153 for &epoch_ms in &[0u64, 1_000, 1_786_320_000_000, 4_102_444_800_000] {
154 let iso = iso8601_ms(epoch_ms);
155 assert!(iso.ends_with('Z'), "must end with Z: {iso}");
156 assert!(!iso.contains('+'), "must not carry a `+HH:MM` offset: {iso}");
157 assert!(
158 !iso[1..].contains('-') || iso.matches('-').count() == 2,
159 "only date separators are allowed: {iso}",
160 );
161 }
162 }
163
164 #[test]
166 fn iso8601_ms_millisecond_precision_is_lossless() {
167 let base = 1_786_320_000_000u64; for ms in 0..1000 {
169 let iso = iso8601_ms(base + ms);
170 let expected = format!("2026-08-10T00:00:00.{ms:03}Z");
171 assert_eq!(iso, expected, "ms={ms}");
172 }
173 }
174
175 #[test]
178 fn iso8601_ms_is_deterministic_across_calls() {
179 let cases = [0u64, 1_000, 86_400_000, 1_786_320_000_000, 4_102_444_800_000];
180 for &epoch_ms in &cases {
181 let a = iso8601_ms(epoch_ms);
182 let b = iso8601_ms(epoch_ms);
183 let c = iso8601_ms(epoch_ms);
184 assert_eq!(a, b);
185 assert_eq!(b, c);
186 }
187 }
188
189 #[test]
193 fn iso8601_ms_stays_24_chars_through_year_9999() {
194 let last_4digit_ms = 253_402_300_799_999u64;
196 let iso = iso8601_ms(last_4digit_ms);
197 assert_eq!(iso, "9999-12-31T23:59:59.999Z");
198 assert_eq!(iso.len(), 24, "must stay at 24 chars through year 9999");
199 }
200
201 #[test]
206 fn iso8601_ms_does_not_panic_at_u64_max() {
207 let iso = iso8601_ms(u64::MAX);
208 assert!(iso.ends_with('Z'), "still emits UTC-Z: {iso}");
209 assert!(iso.len() >= 24, "still valid-shape: {iso}");
210 }
211
212 #[test]
218 fn now_ms_never_panics_and_is_monotonic_under_normal_operation() {
219 let mut previous = now_ms();
220 for _ in 0..1_000 {
221 let current = now_ms();
222 assert!(
223 current >= previous,
224 "wall clock ran backward: {previous} -> {current}",
225 );
226 previous = current;
227 }
228 }
229
230 #[test]
233 fn now_iso8601_shape_holds_for_real_clock() {
234 for _ in 0..100 {
235 let iso = now_iso8601();
236 assert!(iso.ends_with('Z'), "{iso}");
237 assert_eq!(iso.len(), 24, "{iso}");
238 let bytes = iso.as_bytes();
240 assert_eq!(bytes[4], b'-');
241 assert_eq!(bytes[7], b'-');
242 assert_eq!(bytes[10], b'T');
243 assert_eq!(bytes[13], b':');
244 assert_eq!(bytes[16], b':');
245 assert_eq!(bytes[19], b'.');
246 }
247 }
248
249 #[test]
252 fn iso8601_ms_leap_year_boundaries_are_correct() {
253 let cases: &[(u64, &str)] = &[
260 (951_696_000_000, "2000-02-29T00:00:00.000Z"), (4_107_456_000_000, "2100-03-01T00:00:00.000Z"), (
263 13_569_465_600_000 + 58 * crate::units::MS_PER_DAY,
264 "2400-02-29T00:00:00.000Z",
265 ), (1_709_078_400_000, "2024-02-29T00:00:00.000Z"), (1_677_542_400_000, "2023-03-01T00:00:00.000Z"), ];
269 for &(feb_28_ms, expected_next) in cases {
270 let next_day = iso8601_ms(feb_28_ms + crate::units::MS_PER_DAY);
271 assert_eq!(next_day, expected_next, "feb_28_ms={feb_28_ms}");
272 }
273 }
274
275 #[test]
279 fn elapsed_us_never_panics_and_is_monotone() {
280 let started = std::time::Instant::now();
281 let a = elapsed_us(started);
282 let b = elapsed_us(started);
283 assert!(b >= a, "elapsed_us reversed: {a} -> {b}");
284 assert!(a < u64::MAX);
285 }
286}
287
288#[cfg(test)]
289mod calendar_tests {
290 #[test]
295 fn iso8601_ms_pins_known_dates() {
296 assert_eq!(super::iso8601_ms(0), "1970-01-01T00:00:00.000Z");
297 assert_eq!(super::iso8601_ms(951_782_400_000), "2000-02-29T00:00:00.000Z");
298 assert_eq!(super::iso8601_ms(4_107_542_399_000), "2100-02-28T23:59:59.000Z");
299 }
300}