mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-24 09:57:08 -06:00
* Use chrono for UTC/calendar conversion instead of hand-rolled copies Replaces the civil_from_days copies behind the app's log timestamp, the Measurement Log's date/time, and the perf report date with chrono as a workspace dependency (reusing the version already in the tree). Output formats are unchanged and pinned by the existing tests. AGENTS.md now says time goes through chrono. Follow-up to storytold/cadcraft#1785 * Never panic on a clock outside chrono's range; keep 5-digit years unprefixed chrono's From<SystemTime> unwraps internally, so a clock past year 262143 panicked. logging::timestamp and analysis_cmds::now_iso now read the duration since the epoch and go through the fallible DateTime::from_timestamp, reading an out-of-range or pre-epoch clock as the epoch as the old saturating code did. All three ISO helpers (timestamp, now_iso, perf::iso_utc) write the year with {:04} rather than %Y, which prefixes a '+' past year 9999. Tests cover the far-future and year-10000 cases for timestamp and iso_utc. * Don't build an unrepresentable SystemTime in the far-future test UNIX_EPOCH + Duration::from_secs(u64::MAX / 2) overflows Windows' SystemTime and panicked before timestamp() ran, failing the Windows test job. Build it with checked_add and only assert where it exists. * time: preserve old out-of-range date conversion (no epoch fallback) `DateTime::from_timestamp(...).unwrap_or_default()` silently reset any instant past chrono's year range to 1970-01-01, changing output the old hand-rolled Howard-Hinnant code converted exactly (e.g. 10_000_000_000_000 s was 318857-05-20 17:46:40, not the epoch). Restore the old conversion domain with a cycle-based helper that routes the calendar through chrono (the calendar repeats every 400 years = 146_097 days, so month/day/time come from chrono and only the year is carried in i128, keeping every i64 exact). - apps/photocraft/src/logging.rs: `timestamp` via `utc_parts`; keep the pre-1970 epoch clamp; u64 secs past i64::MAX saturate, not epoch. - crates/engine/src/analysis_cmds.rs: extract pure `iso_from_unix` (cycle based) from `now_iso` so the conversion is tested directly. - xtask/src/perf.rs: `iso_utc` cycle-based, accepts the full u64 range the old code did. Pinned old outputs in tests: logging u64::MAX/2 s -> 292277026596-12-04T15:30:07.000Z (was epoch), 10_000_000_000_000 s -> 318857-05-20T17:46:40.000Z iso_from_unix 10_000_000_000_000 -> 318857-05-20T17:46:40Z, -10_000_000_000_000 -> -314918-08-13T06:13:20Z, i64 extremes iso_utc u64::MAX -> 584554051223-11-09T07:00:15Z (was epoch), 10_000_000_000_000 -> 318857-05-20T17:46:40Z * Don't build a year-318857 SystemTime unchecked in the logging test Windows' SystemTime only reaches about year 30828, so UNIX_EPOCH + Duration::from_secs(10_000_000_000_000) panicked before timestamp() ran and failed the Windows test job. Build it with checked_add and assert where the platform can hold it. The year-10000 case fits everywhere and stays unconditional. * Name the 400-year range extension as the one exception to the time rule The rule said never to hand-roll calendar math, but the range fix (shifting by whole 400-year Gregorian cycles so dates past chrono's year range keep their old output) is cycle arithmetic, so the rule and the code disagreed. The rule now names that exception: the library still computes month, day and time, and only whole cycles are carried in the year. * Say the library computes month and day; time-of-day is plain arithmetic Narrows the range-extension wording: the date library computes the calendar fields, and splitting seconds into hours, minutes and seconds is ordinary arithmetic, which is what the code does.