{"record":{"id":"f840659f043d2893","repo":"can1357/oh-my-pi","slug":"date-is-out-of-range","errorCode":null,"errorMessage":"date is out of range","messagePattern":"date is out of range","errorType":"validation","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/pi-builtins/src/fd.rs","lineNumber":1404,"sourceCode":"\t}\n\tlet mut time_parts = time.split(':');\n\tlet hour = parse_u32_part(time_parts.next(), \"hour\")?;\n\tlet minute = parse_u32_part(time_parts.next(), \"minute\")?;\n\tlet second = parse_u32_part(time_parts.next(), \"second\")?;\n\tif time_parts.next().is_some()\n\t\t|| !(1..=12).contains(&month)\n\t\t|| !(1..=31).contains(&day)\n\t\t|| hour > 23\n\t\t|| minute > 59\n\t\t|| second > 59\n\t{\n\t\treturn Err(io::Error::new(io::ErrorKind::InvalidInput, format!(\"invalid date: {value}\")));\n\t}\n\tlet days = days_from_civil(year, month, day);\n\tlet seconds = days\n\t\t.checked_mul(86_400)\n\t\t.and_then(|base| base.checked_add(i64::from(hour * 3_600 + minute * 60 + second)))\n\t\t.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, \"date is out of range\"))?;\n\tif seconds < 0 {\n\t\treturn Err(io::Error::new(io::ErrorKind::InvalidInput, \"dates before 1970 are unsupported\"));\n\t}\n\tOk(UNIX_EPOCH + Duration::from_secs(u64::try_from(seconds).unwrap_or(u64::MAX)))\n}\n\nfn parse_i32_part(value: Option<&str>, name: &str) -> io::Result<i32> {\n\tvalue\n\t\t.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, format!(\"missing {name}\")))?\n\t\t.parse::<i32>()\n\t\t.map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err.to_string()))\n}\n\nfn parse_u32_part(value: Option<&str>, name: &str) -> io::Result<u32> {\n\tvalue\n\t\t.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, format!(\"missing {name}\")))?\n\t\t.parse::<u32>()\n\t\t.map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err.to_string()))","sourceCodeStart":1386,"sourceCodeEnd":1422,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/crates/pi-builtins/src/fd.rs#L1386-L1422","documentation":"`parse_utc_datetime` computes total seconds since the epoch with `days_from_civil(...)` then `checked_mul(86_400)` and `checked_add(...)`; if any step overflows `i64`, the value cannot be represented and the library throws `date is out of range` (`InvalidInput`). This only happens with extreme years (roughly outside ±250 million years), far beyond normal usage, and indicates an arithmetic-overflow cutoff distinct from the `dates before 1970 are unsupported` rejection that follows.","triggerScenarios":"Passing an absolute datetime with an astronomically large or small year, e.g. `999999999999-01-01` or `-99999999999-01-01`, such that days*86400 overflows i64.","commonSituations":"Unvalidated user input or config values fed straight into the filter with missing digit-length checks; a bug where a millisecond/microsecond timestamp is used as the year field; template/variable substitution gone wrong producing a huge year.","solutions":["Correct the year to a realistic 4-digit (or at least sane) value; dates must represent real calendar dates ≥ 1970-01-01 UTC.","Sanitize upstream input to reject absurd year values before building the filter string.","Switch machine-generated absolute times to the `@<epoch-seconds>` form, which validates via u64 parsing instead of civil-date arithmetic."],"exampleFix":"// before (year overflow)\nfind --changed 999999999999-01-01\n// after (sane date, post-1970 UTC)\nfind --changed 2024-01-15\nfind --changed @1705276800","handlingStrategy":"validation","validationCode":"fn validate_year_range(value: &str) -> Result<(), String> {\n    let date = value.trim().split(' ').next().unwrap_or(\"\");\n    let year_str = date.split('-').next().unwrap_or(\"\");\n    let year: i64 = year_str.parse().map_err(|_| format!(\"bad year '{year_str}'\"))?;\n    if !(-999..=9999).contains(&year) {\n        return Err(format!(\"year '{year}' is implausible; expect a 4-digit year\"));\n    }\n    Ok(())\n}","typeGuard":"fn has_plausible_year(value: &str) -> bool {\n    let date = value.trim().split(' ').next().unwrap_or(\"\");\n    date.split('-').next()\n        .and_then(|y| y.parse::<i64>().ok())\n        .map(|y| (0..=9999).contains(&y))\n        .unwrap_or(false)\n}","tryCatchPattern":"match parse_time_filter(input) {\n    Ok(time) => use_time(time),\n    Err(e) if e.to_string() == \"date is out of range\" => {\n        eprintln!(\"'{input}' overflows epoch arithmetic; check the year field for corrupted or oversized digits\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Validate the year is a plausible 4-digit value before constructing the filter string.","Check for unit-confusion bugs (millisecond timestamps used as year fields).","Bound or sanitize all user/config input feeding datetime strings.","Use `@<epoch-seconds>` for machine-generated times; u64 parsing fails fast instead of overflowing."],"tags":["input-validation","date-parsing","overflow","rust","invalid-input"],"backgroundTag":"date-out-of-range","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}