{"record":{"id":"385f54b5c0874e94","repo":"nautechsystems/nautilus_trader","slug":"failed-to-add-n-years-to-datetime","errorCode":null,"errorMessage":"Failed to add {n} years to {datetime}","messagePattern":"Failed to add (.+?) years to (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/core/src/datetime.rs","lineNumber":612,"sourceCode":"    let timestamp = result.as_nanosecond();\n\n    let nanos =\n        u64::try_from(timestamp).map_err(|_| anyhow::anyhow!(\"Negative timestamp not allowed\"))?;\n    Ok(UnixNanos::from(nanos))\n}\n\n/// Add `n` years to a Jiff [`Timestamp`].\n///\n/// # Errors\n///\n/// Returns an error if the resulting date would be invalid or out of range.\npub fn add_n_years(datetime: Timestamp, n: u32) -> anyhow::Result<Timestamp> {\n    let months = n.checked_mul(12).ok_or_else(|| {\n        anyhow::anyhow!(\"Failed to add {n} years to {datetime}: month count overflow\")\n    })?;\n\n    shift_months(datetime, i64::from(months))\n        .map_err(|_| anyhow::anyhow!(\"Failed to add {n} years to {datetime}\"))\n}\n\n/// Subtract `n` years from a Jiff [`Timestamp`].\n///\n/// # Errors\n///\n/// Returns an error if the resulting date would be invalid or out of range.\npub fn subtract_n_years(datetime: Timestamp, n: u32) -> anyhow::Result<Timestamp> {\n    let months = n.checked_mul(12).ok_or_else(|| {\n        anyhow::anyhow!(\"Failed to subtract {n} years from {datetime}: month count overflow\")\n    })?;\n\n    shift_months(datetime, -i64::from(months))\n        .map_err(|_| anyhow::anyhow!(\"Failed to subtract {n} years from {datetime}\"))\n}\n\n/// Add `n` years to a given UNIX nanoseconds timestamp.\n///","sourceCodeStart":594,"sourceCodeEnd":630,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/core/src/datetime.rs#L594-L630","documentation":"After computing the month count, add_n_years delegates to shift_months. When the shifted date is invalid or overflows the Timestamp range (e.g. adding years pushes past year 9999 or the i64 nanosecond max), shift_months fails and this error wraps the failure with context. It means the requested addition cannot produce a representable valid timestamp.","triggerScenarios":"add_n_years(datetime, n) where datetime + n*12 months lands outside the representable Timestamp range (beyond ~year 9999 / i64 nanosecond bounds) or produces an invalid calendar date.","commonSituations":"Clamping or extrapolating expiry/schedule dates from bad data; adding years to a timestamp already near the max; tests verifying overflow behavior; timer setup (start_timer_internal) with a far-future offset.","solutions":["Check the target date stays in range before adding: compare datetime with the max supported timestamp minus n years.","Reduce the year offset or clamp the result to a maximum representable timestamp.","Handle the Result with context rather than unwrapping; log the datetime and n to identify the bad input."],"exampleFix":"// before\nlet expiry = add_n_years(now, years).unwrap();\n// after\nlet expiry = add_n_years(now, years)\n    .unwrap_or_else(|_| add_n_years(now, 10_000.min(years)).expect(\"clamped range\"));","handlingStrategy":"try-catch","validationCode":"// pre-check: target must stay in range\nlet max = jiff::Timestamp::MAX;\nif datetime + jiff::Span::new().years(n as i32) > max { /* clamp or reject */ }","typeGuard":null,"tryCatchPattern":"match add_n_years(datetime, n) {\n    Ok(ts) => ts,\n    Err(e) => { log::warn!(\"year shift out of range: {e}\"); clamp_to_max(datetime) }\n}","preventionTips":["Clamp long-horizon offsets (expiries, schedules) to the max representable timestamp.","Log datetime and n when propagating this error to diagnose bad inputs."],"tags":["datetime","overflow","rust"],"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-14T00:17:10.932Z"}