{"record":{"id":"6b9b5c58f35713f9","repo":"GitoxideLabs/gitoxide","slug":"date-lies-out-of-range-02","errorCode":null,"errorMessage":"Date lies out of range: {}-{:02}","messagePattern":"Date lies out of range: (.+?)-(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-date/src/parse/relative.rs","lineNumber":170,"sourceCode":"\n    impl From<Zoned> for Fields {\n        fn from(zdt: Zoned) -> Self {\n            Fields {\n                year: zdt.year(),\n                month: zdt.month(),\n                day: zdt.day(),\n                time: zdt.time(),\n                timezone: zdt.time_zone().clone(),\n            }\n        }\n    }\n\n    impl Fields {\n        /// Turn the fields back into a point in time: a day beyond the end of the month rolls over into the\n        /// following month. One month before May 31st is thus May 1st, a day after April 30th.\n        fn normalize(&self) -> Result<Zoned, Exn<Error>> {\n            let first_of_month = civil::Date::new(self.year, self.month, 1)\n                .or_raise(|| Error::new(format!(\"Date lies out of range: {}-{:02}\", self.year, self.month)))?;\n            let days_beyond_first = SignedDuration::from_secs((i64::from(self.day) - 1) * 24 * 60 * 60);\n            first_of_month\n                .checked_add(days_beyond_first)\n                .or_raise(|| Error::new(format!(\"Day {} lies out of range\", self.day)))?\n                .to_datetime(self.time)\n                .to_zoned(self.timezone.clone())\n                .or_raise(|| Error::new(\"Could not convert date to a point in time\"))\n        }\n    }\n\n    let now = now.ok_or(ValidationError::new(\"Missing current time\"))?;\n    let mut fields = Fields::from(now);\n    for Pair { period, count, unit } in pairs {\n        let err = || Error::new(format!(\"Couldn't parse span from '{period} {count}'\"));\n        match unit {\n            Unit::Seconds(factor) => {\n                let seconds = count\n                    .checked_mul(*factor)","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-date/src/parse/relative.rs#L152-L188","documentation":"In gix-date's relative-date parsing, `Fields::normalize()` rebuilds a point in time from year/month/day fields. `civil::Date::new(year, month, 1)` fails when the year or month values themselves are outside jiff's supported civil-date range; the code then raises Error 'Date lies out of range: {year}-{month}' as context over the jiff error.","triggerScenarios":"`subtract_pairs` (reached via relative-date `parse`) producing year/month fields outside the supported calendar range — e.g. adding or subtracting huge period counts (like '90000 years ago') that push the computed year/month below jiff's minimum (-9999) or above its maximum year.","commonSituations":"Parsing user input like '999999 years ago' or extreme relative dates in commit-date filters or `git log --since`-style expressions implemented on gix-date.","solutions":["Validate/clamp the requested period count so the resulting date stays within the supported calendar range (roughly years -9999..=9999)","Catch the Error and reject the relative date expression with a user-facing message about the supported range","Parse to an absolute timestamp instead of extreme relative offsets"],"exampleFix":"// before\nparse(\"now\", \"999999 years ago\")\n// after\nlet years = count.min(9999);\nparse(\"now\", &format!(\"{years} years ago\"))","handlingStrategy":"validation","validationCode":"fn within_calendar_range(year: i32, month: u8) -> bool {\n    (-9999..=9999).contains(&year) && (1..=12).contains(&month)\n}","typeGuard":null,"tryCatchPattern":"match relative.parse(...) {\n    Ok(t) => t,\n    Err(e) => return Err(format!(\"relative date out of supported range: {e}\")),\n}","preventionTips":["Clamp relative period counts to sane magnitudes","Reject absurd inputs ('999999 years ago') at the UI layer","Document the supported date range"],"tags":["date","parsing","range"],"backgroundTag":"value-out-of-range","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"}