{"record":{"id":"094d7db631d4239b","repo":"databendlabs/databend","slug":"date-day-count-is-inside-the-chrono-civil-range","errorCode":null,"errorMessage":"date day count is inside the chrono civil range","messagePattern":"date day count is inside the chrono civil range","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/query/expression/src/types/date.rs","lineNumber":52,"sourceCode":"use crate::ScalarRef;\nuse crate::property::Domain;\nuse crate::values::Column;\nuse crate::values::Scalar;\n\npub const DATE_FORMAT: &str = \"%Y-%m-%d\";\n/// SQL DATE bounds, represented as days since 1970-01-01.\n/// Calendar inputs and computed DATE values both use years 0001..=9999.\n/// 0001-01-01\npub const DATE_MIN: i32 = -719_162;\n/// 9999-12-31\npub const DATE_MAX: i32 = 2_932_896;\n\n/// Converts internal epoch days. SQL inputs must pass `check_date` first.\npub fn date_from_days(days: impl AsPrimitive<i64>) -> NaiveDate {\n    NaiveDate::from_ymd_opt(1970, 1, 1)\n        .expect(\"epoch date is valid\")\n        .checked_add_signed(TimeDelta::days(days.as_()))\n        .expect(\"date day count is inside the chrono civil range\")\n}\n\n/// Preserve the legacy conversion policy: either bound overflow maps to DATE_MIN.\n#[inline]\npub fn clamp_date(days: i64) -> i32 {\n    if (DATE_MIN as i64..=DATE_MAX as i64).contains(&days) {\n        days as i32\n    } else {\n        DATE_MIN\n    }\n}\n\n/// Validate the SQL DATE range without silently changing the value.\n#[inline]\npub fn check_date(days: i64) -> Result<i32, String> {\n    if (i64::from(DATE_MIN)..=i64::from(DATE_MAX)).contains(&days) {\n        Ok(days as i32)\n    } else {","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/databendlabs/databend/blob/288d84d76e20a2f8f7173bda9691eb6ece301aa9/src/query/expression/src/types/date.rs#L34-L70","documentation":"date_from_days adds the given epoch-day count to the 1970-01-01 base date. This panic fires when the day count is outside chrono's supported civil range (roughly years -262144..=262143 in checked arithmetic, and practically outside DATE_MIN/DATE_MAX of year ~-4713 to 9999). SQL inputs are expected to pass check_date first, so hitting this means an unchecked or computed value escaped validation.","triggerScenarios":"Calling date_from_days with days outside the valid date range — e.g. i64 values from unvalidated user input, arithmetic results of DATE_ADD/DATE_DIFF overflowing the calendar, or paths that bypass check_date.","commonSituations":"User SQL producing extreme dates (huge DATE_ADD intervals); internal arithmetic like date arithmetic in eval_date_diff feeding unchecked results; data files containing out-of-range day numbers loaded without validation.","solutions":["Call check_date (or clamp_date) on the day value before date_from_days.","Validate user-supplied intervals in date arithmetic so results stay within DATE_MIN..=DATE_MAX (days -719162..=2932896).","Replace expect with a graceful error mapping to NULL/ErrorCode for out-of-range inputs in expression evaluation."],"exampleFix":"// before\nlet date = date_from_days(days);\n// after\nif !(DATE_MIN as i64..=DATE_MAX as i64).contains(&days) {\n    return Err(ErrorCode::BadArguments(\"date out of range\"));\n}\nlet date = date_from_days(days);","handlingStrategy":"validation","validationCode":"if !(DATE_MIN as i64..=DATE_MAX as i64).contains(&days) {\n    return Err(ErrorCode::BadArguments(format!(\"date out of range: {days}\")));\n}\nlet d = date_from_days(days);","typeGuard":"fn in_date_range(days: i64) -> bool { (DATE_MIN as i64..=DATE_MAX as i64).contains(&days) }","tryCatchPattern":null,"preventionTips":["Always route SQL date inputs through check_date before date_from_days.","Bounds-check results of date arithmetic (DATE_ADD, intervals) before further conversion.","Validate day values from external data files at load time."],"tags":["panic","date","range-check"],"backgroundTag":"value-out-of-range","analyzedSha":"288d84d76e20a2f8f7173bda9691eb6ece301aa9","analyzedAt":"2026-09-11T11:29:36.208Z","contentChangedAt":"2026-09-11T11:29:36.208Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}