diff --git a/crates/core/src/parse.rs b/crates/core/src/parse.rs index b465beaf9..f39e914ae 100644 --- a/crates/core/src/parse.rs +++ b/crates/core/src/parse.rs @@ -184,7 +184,7 @@ fn parse_time(t: &str) -> Option<(f64, bool, bool)> { if parts.len() == 1 && ampm.is_none() { return None; } - if parts.len() > 3 || parts.iter().any(|p| p.is_empty()) { + if parts.len() > 3 || parts.iter().any(|p| p.is_empty() || p.starts_with('+')) { return None; } let mut h: f64 = parts.first()?.parse().ok()?; @@ -211,7 +211,13 @@ fn parse_time(t: &str) -> Option<(f64, bool, bool)> { fn parse_date(t: &str, sys: DateSystem) -> Option<(f64, &'static str)> { let t = t.trim().trim_end_matches(','); - let num = |s: &str| s.trim().parse::().ok(); + let num = |s: &str| { + let s = s.trim(); + if s.is_empty() || !s.bytes().all(|b| b.is_ascii_digit()) { + return None; + } + s.parse::().ok() + }; // ISO yyyy-mm-dd or yyyy/mm/dd let seps: &[char] = &['/', '-', '.']; let parts: Vec<&str> = t.split(seps).collect(); @@ -326,6 +332,14 @@ mod tests { parse_input(s, DateSystem::D1900) } + #[test] + fn signed_date_parts_stay_text() { + for s in ["+3/4/2026", "3/+4/2026", "3/4/+2026", "1/1/2020 +5:00"] { + assert_eq!(p(s).value, Value::text(s), "{s}"); + } + assert_eq!(p("3/4/2026").value, Value::Number(46085.0)); + } + #[test] fn month_name_short_year() { for s in ["Mar 4, 26", "Mar 4 26", "March 4 26", "4 March 26", "Mar 4, 2026"] {