{"record":{"id":"4e85b66b7a7c3653","repo":"Hmbown/CodeWhale","slug":"failed-to-parse-once-at-trimmed-use-local-yyy","errorCode":null,"errorMessage":"Failed to parse ONCE AT '{trimmed}'. Use local YYYY-MM-DDTHH:MM[:SS] or RFC3339","messagePattern":"Failed to parse ONCE AT '(.+?)'\\. Use local YYYY-MM-DDTHH:MM\\[:SS\\] or RFC3339","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/automation_manager.rs","lineNumber":639,"sourceCode":"        .filter(|value| !value.is_empty())\n        .ok_or_else(|| anyhow::anyhow!(\"CRON schedules require EXPR\"))?;\n    ParsedCronExpr::parse(&expr)?;\n    Ok(AutomationSchedule::Cron { expr })\n}\n\nfn parse_once_at(raw: &str) -> Result<DateTime<Utc>> {\n    let trimmed = raw.trim();\n    if let Ok(at) = DateTime::parse_from_rfc3339(trimmed) {\n        return Ok(at.with_timezone(&Utc));\n    }\n    for format in [\"%Y-%m-%dT%H:%M:%S\", \"%Y-%m-%dT%H:%M\"] {\n        if let Ok(naive) = NaiveDateTime::parse_from_str(trimmed, format) {\n            return resolve_local_datetime(&Local, naive)\n                .map(|value| value.with_timezone(&Utc))\n                .ok_or_else(|| anyhow::anyhow!(\"ONCE local time does not exist: {trimmed}\"));\n        }\n    }\n    bail!(\"Failed to parse ONCE AT '{trimmed}'. Use local YYYY-MM-DDTHH:MM[:SS] or RFC3339\")\n}\n\n#[derive(Debug, Clone)]\nstruct ParsedCronExpr {\n    minute: CronField,\n    hour: CronField,\n    day_of_month: CronField,\n    month: CronField,\n    day_of_week: CronField,\n}\n\nimpl ParsedCronExpr {\n    fn parse(expr: &str) -> Result<Self> {\n        let fields: Vec<&str> = expr.split_whitespace().collect();\n        if fields.len() != 5 {\n            bail!(\n                \"CRON EXPR must have exactly 5 fields: minute hour day-of-month month day-of-week\"\n            );","sourceCodeStart":621,"sourceCodeEnd":657,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/automation_manager.rs#L621-L657","documentation":"parse_once_at accepts exactly two shapes: RFC3339 with an explicit offset (DateTime::parse_from_rfc3339), or a naive local 'YYYY-MM-DDTHH:MM' / 'YYYY-MM-DDTHH:MM:SS' resolved in the machine's local timezone. Anything else — date-only, a space instead of 'T', fractional seconds, or slash formats — falls through to this bail. (A naive local time that falls in a DST gap yields the sibling 'ONCE local time does not exist' error.)","triggerScenarios":"AT=2026-08-03 (date only); AT='2026-08-03 14:30' (space separator); AT=2026-08-03T14:30:30.5Z (fractional seconds); AT=03/08/2026.","commonSituations":"User-entered timestamps from forms; locales using slash or space formats; pasting output of the date command unmodified.","solutions":["Emit the 'T' separator: 2026-08-03T14:30","For unambiguous scheduling send RFC3339 with an offset, e.g. 2026-08-03T14:30:00Z or +02:00","Normalize timestamps to RFC3339 client-side before create_automation","For naive local input keep seconds optional but avoid sub-second precision"],"exampleFix":"// before\nrrule = \"FREQ=ONCE;AT=2026-08-03 14:30\"\n\n// after (unambiguous UTC)\nrrule = \"FREQ=ONCE;AT=2026-08-03T14:30:00Z\"","handlingStrategy":"validation","validationCode":"fn parseable_once_at(raw: &str) -> bool {\n    let t = raw.trim();\n    if chrono::DateTime::parse_from_rfc3339(t).is_ok() {\n        return true;\n    }\n    [\"%Y-%m-%dT%H:%M:%S\", \"%Y-%m-%dT%H:%M\"]\n        .iter()\n        .any(|f| chrono::NaiveDateTime::parse_from_str(t, f).is_ok())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Prefer RFC3339 with an explicit offset for machine-generated schedules","Use a date/time picker that emits exactly the accepted formats","Never pass shell date output unmodified","Remember naive forms are interpreted in the machine's local timezone, not UTC"],"tags":["rust","datetime","rfc3339","once","validation"],"backgroundTag":"datetime-string-parse-failed","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}