{"record":{"id":"e2ffd475fa8b1440","repo":"nautechsystems/nautilus_trader","slug":"fx-session-date-must-be-representable","errorCode":null,"errorMessage":"FX session date must be representable","messagePattern":"FX session date must be representable","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/trading/src/sessions.rs","lineNumber":142,"sourceCode":"}\n\n/// Returns the previous session end time in UTC.\n#[must_use]\npub fn fx_prev_end(session: ForexSession, time_now: Timestamp) -> Timestamp {\n    let local_now = fx_local_from_utc(session, time_now);\n    let (_, end_time) = session.session_times();\n\n    fx_prev_boundary(&local_now, end_time)\n}\n\nfn fx_next_boundary(local_now: &Zoned, session_time: Time) -> Timestamp {\n    let timezone = local_now.time_zone().clone();\n    let mut date = local_now.date();\n\n    if local_now.time() > session_time {\n        date = date\n            .checked_add(Span::new().days(1))\n            .expect(\"FX session date must be representable\");\n    }\n\n    let weekend_days = match date.weekday() {\n        Weekday::Saturday => 2,\n        Weekday::Sunday => 1,\n        _ => 0,\n    };\n    date = date\n        .checked_add(Span::new().days(weekend_days))\n        .expect(\"FX session date must be representable\");\n\n    timezone\n        .to_ambiguous_timestamp(date.to_datetime(session_time))\n        .unambiguous()\n        .expect(\"FX session boundary must be a unique local time\")\n}\n\nfn fx_prev_boundary(local_now: &Zoned, session_time: Time) -> Timestamp {","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/trading/src/sessions.rs#L124-L160","documentation":"In `fx_next_boundary`, when the current local time is past the session time, the code advances the date by one day via `checked_add` and panics if the resulting date is not representable. `jiff`'s `CivilDate` has bounded min/max years, so this only fires at the extreme edge of the representable calendar range. In practice it is an unreachable defensive panic for normal timestamps.","triggerScenarios":"Calling `fx_next_start`/`fx_next_end` (via `fx_next_boundary`) while the current local date is at `CivilDate`'s maximum (year 9999) and the local time is past the session time, forcing a +1 day overflow.","commonSituations":"Only with fabricated/system clock values at the far end of the supported date range; mocked `Zoned` inputs in tests or corrupted clock data far beyond realistic trading dates.","solutions":["Ensure the system clock and any injected timestamps are within realistic (jiff-representable, well-below year 9999) ranges.","Clamp or validate `local_now` before calling session helpers if timestamps come from external/untrusted data.","If you legitimately operate near the boundary, compute the boundary with `checked_add` and handle `None` yourself instead of the panicking helper.","Report upstream if reachable with a sane clock — it indicates an unexpected input path."],"exampleFix":"// before (caller)\nlet next = fx_next_start(ForexSession::London, &now); // panics on overflow\n\n// after (defensive pre-check)\nlet now = Zoned::now();\nassert!(now.year() < 9999, \"system clock far out of range: {}\", now.year());\nlet next = fx_next_start(ForexSession::London, &now);","handlingStrategy":"validation","validationCode":"// Reject absurd timestamps before calling session helpers\nfn timestamp_in_reasonable_range(now: &jiff::Zoned) -> bool {\n    (-2200..=2200).contains(&now.year())\n}","typeGuard":null,"tryCatchPattern":"// Rust panics are not catchable; pre-validate instead\nlet now = jiff::Zoned::now();\nassert!(timestamp_in_reasonable_range(&now), \"clock outside sane range\");","preventionTips":["Sanity-check the system clock (NTP) — extreme dates only occur with broken clocks or mocks.","When fuzzing/testing with synthetic Zoned values, keep years within a realistic window.","Remember jiff CivilDate bounds (roughly -9999..=9999) and stay far from them."],"tags":["rust","panic","date-overflow","datetime"],"backgroundTag":"value-out-of-range","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}