{"record":{"id":"9ffd4cfedcedac62","repo":"GitoxideLabs/gitoxide","slug":"failed-to-subtract-duration-from-now","errorCode":null,"errorMessage":"Failed to subtract {duration} from {now}","messagePattern":"Failed to subtract (.+?) from (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-date/src/parse/relative.rs","lineNumber":213,"sourceCode":"                let months = count.checked_mul(*factor).ok_or_else(err)?;\n                fields = fields.normalize()?.into();\n                let total = (i64::from(fields.year) * 12 + i64::from(fields.month) - 1)\n                    .checked_sub(months)\n                    .ok_or_else(err)?;\n                fields.year = i16::try_from(total.div_euclid(12)).ok().ok_or_else(err)?;\n                fields.month = i8::try_from(total.rem_euclid(12) + 1).expect(\"a value in 1..=12\");\n            }\n        }\n    }\n    fields.normalize()\n}\n\nfn subtract_duration(now: Option<&Zoned>, duration: SignedDuration) -> Result<Zoned, Exn<ValidationError>> {\n    let now = now.ok_or(ValidationError::new(\"Missing current time\"))?;\n    now.timestamp()\n        .checked_sub(duration)\n        .map(|timestamp| timestamp.to_zoned(now.time_zone().clone()))\n        .or_raise(|| Error::new(format!(\"Failed to subtract {duration} from {now}\")))\n}\n","sourceCodeStart":195,"sourceCodeEnd":215,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-date/src/parse/relative.rs#L195-L215","documentation":"gix-date's relative-date parser (`subtract_duration`, used by `parse_named`) computes an absolute timestamp by subtracting a parsed duration (e.g. from \"2 hours ago\") from the current time via `checked_sub`. This error is raised when that arithmetic overflows — the duration is too large to subtract from the given `now` timestamp — and wraps the failure with context showing the duration and the base time.","triggerScenarios":"Calling `gix_date::parse::named` (or the relative parser it delegates to) with a relative date spec whose duration exceeds the representable timestamp range when subtracted from `now`, e.g. an enormous number of years/durations near the timestamp limits.","commonSituations":"User-supplied free-form date input like \"999999999999 years ago\" accepted by a CLI or config; fuzzed or malicious input to a date parser; unit tests passing extreme durations.","solutions":["Validate the magnitude of the relative duration before parsing (reject absurdly large numbers).","Provide a sane `now` reference (`Some(&Zoned::now())`) so subtraction happens in a normal range.","Catch the error and surface 'invalid relative date' to the user instead of propagating."],"exampleFix":"// before: extreme spec overflows when subtracted from now\nlet t = gix_date::parse::named(\"99999999999 years ago\", Some(&now))?;\n\n// after: bound the numeric part before parsing\nlet n: u64 = spec.trim_end_matches(...).parse()?;\nif n > 1_000_000 { return Err(anyhow::anyhow!(\"relative date too large\")); }\nlet t = gix_date::parse::named(spec, Some(&now))?;","handlingStrategy":"try-catch","validationCode":"let n: u64 = /* numeric part of spec */;\nif n > 1_000_000 { return Err(anyhow::anyhow!(\"relative date magnitude too large\")); }","typeGuard":null,"tryCatchPattern":"match gix_date::parse::named(spec, Some(&now)) {\n    Ok(t) => t,\n    Err(_) => anyhow::bail!(\"invalid relative date: {spec}\"),\n}","preventionTips":["Always pass an explicit `now` reference instead of None.","Bound user-supplied numeric durations before parsing.","Map parse failures to a user-facing 'invalid date' message in CLI input handling."],"tags":["date","parsing","overflow","rust"],"backgroundTag":"invalid-date-format","analyzedSha":"e73179060badf27222d790981fac3f84c1830a7e","analyzedAt":"2026-09-08T11:26:50.865Z","contentChangedAt":"2026-09-08T11:26:50.865Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}