{"record":{"id":"89c3da56c20c1c77","repo":"can1357/oh-my-pi","slug":"err-to-string-timestamp-parse-error","errorCode":null,"errorMessage":"err.to_string() (timestamp parse error)","messagePattern":"err\\.to_string\\(\\) \\(timestamp parse error\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/pi-builtins/src/fd.rs","lineNumber":1340,"sourceCode":"\t\t\"ti\" => 1_099_511_627_776,\n\t\t_ => {\n\t\t\treturn Err(io::Error::new(\n\t\t\t\tio::ErrorKind::InvalidInput,\n\t\t\t\tformat!(\"invalid size unit: {unit}\"),\n\t\t\t));\n\t\t},\n\t};\n\tlet bytes = count.checked_mul(multiplier).ok_or_else(|| {\n\t\tio::Error::new(io::ErrorKind::InvalidInput, format!(\"size is too large: {value}\"))\n\t})?;\n\tOk(SizeFilter { ordering, bytes })\n}\n\nfn parse_time_filter(value: &str) -> io::Result<SystemTime> {\n\tif let Some(timestamp) = value.strip_prefix('@') {\n\t\tlet seconds = timestamp\n\t\t\t.parse::<u64>()\n\t\t\t.map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err.to_string()))?;\n\t\treturn Ok(UNIX_EPOCH + Duration::from_secs(seconds));\n\t}\n\tif let Some(duration) = parse_duration(value)? {\n\t\treturn SystemTime::now().checked_sub(duration).ok_or_else(|| {\n\t\t\tio::Error::new(io::ErrorKind::InvalidInput, format!(\"duration is too large: {value}\"))\n\t\t});\n\t}\n\tparse_utc_datetime(value)\n}\n\nfn parse_duration(value: &str) -> io::Result<Option<Duration>> {\n\tlet trimmed = value.trim();\n\tlet split = trimmed\n\t\t.char_indices()\n\t\t.find(|(_, ch)| !ch.is_ascii_digit())\n\t\t.map_or(trimmed.len(), |(index, _)| index);\n\tif split == 0 || split == trimmed.len() {\n\t\treturn Ok(None);","sourceCodeStart":1322,"sourceCodeEnd":1358,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/crates/pi-builtins/src/fd.rs#L1322-L1358","documentation":"This is an `io::Error` with `ErrorKind::InvalidInput` produced by the fd builtin's time-filter parser (`parse_time_filter`). When a time-filter value is given in `@SECONDS` form (seconds since the Unix epoch), the numeric portion must parse as a `u64`; if it doesn't, the library wraps the standard `ParseIntError` message via `err.to_string()` and surfaces it as InvalidInput. The error means the caller supplied a non-numeric (or out-of-u64-range) timestamp after the `@` prefix.","triggerScenarios":"Passing a time filter value starting with `@` whose remainder is not a valid unsigned integer, e.g. `@abc`, `@12.5`, `@-100`, `@` with nothing after it, or `@99999999999999999999` (overflows u64).","commonSituations":"Copy-pasting an ISO date into an `@`-style argument instead of an epoch value; using a negative or fractional epoch from another language (JS Date.getTime() milliseconds); forgetting the timestamp entirely and leaving a bare `@`; shell quoting dropping characters so the number is malformed.","solutions":["Convert the timestamp to whole-number seconds since the Unix epoch, e.g. `date -d '2024-01-01' +%s`, and pass `@<seconds>`.","Remove any fractional/negative part: the parser only accepts non-negative integer seconds, so drop milliseconds or use a duration form like `2d` instead.","If you want a relative time, drop the `@` and use a duration value such as `1h`, `3d`, or `2weeks` (accepted units: s/sec(s), m/min(s), h/hr(s), d/day(s), w/week(s)).","If you want an absolute calendar date, drop the `@` and use `YYYY-MM-DD[ HH:MM:SS]` (UTC), which goes through `parse_utc_datetime` instead."],"exampleFix":"// before (invalid: fractional epoch)\nfind --changed @1704067200.5\n// after (valid: integer epoch seconds, or duration)\nfind --changed @1704067200\nfind --changed 2d","handlingStrategy":"validation","validationCode":"fn validate_epoch_filter(value: &str) -> Result<(), String> {\n    match value.strip_prefix('@') {\n        Some(ts) if !ts.is_empty() && ts.bytes().all(|b| b.is_ascii_digit()) => {\n            match ts.parse::<u64>() {\n                Ok(_) => Ok(()),\n                Err(e) => Err(format!(\"epoch seconds invalid: {e}\")),\n            }\n        }\n        _ => Ok(()), // not an @-timestamp; other parse paths apply\n    }\n}","typeGuard":"fn is_epoch_filter(value: &str) -> bool {\n    match value.strip_prefix('@') {\n        Some(ts) => !ts.is_empty() && ts.parse::<u64>().is_ok(),\n        None => false,\n    }\n}","tryCatchPattern":"match parse_time_filter(input) {\n    Ok(time) => use_time(time),\n    Err(e) if e.kind() == io::ErrorKind::InvalidInput => {\n        eprintln!(\"bad time filter '{input}': {e}; expected @<epoch-seconds>, <duration>, or YYYY-MM-DD [HH:MM:SS]\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Generate epoch values with `date +%s` (whole seconds, not milliseconds).","Normalize any fractional/negative epoch to integer seconds before passing it.","Use duration forms (`2d`) for relative filters to avoid epoch conversion mistakes.","Validate user-supplied filter strings with a regex like `^@\\d+$` before invoking the library."],"tags":["input-validation","timestamp","rust","invalid-input"],"backgroundTag":"invalid-timestamp-format","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}