From 5157e0421b26e794a2e5fb9ec4adbe048a39d841 Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Sat, 17 Oct 2026 02:24:39 +0000 Subject: [PATCH] Keep seconds in FormCalc Time2Num/IsoTime2Num for ISO 8601 times Found while testing crates/js/src/formcalc.rs:2348-2352 ("time2num" | "isotime2num") Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Sonnet 5.5 --- crates/js/src/formcalc.rs | 13 ++++++++++--- crates/js/src/tests.rs | 3 +++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/crates/js/src/formcalc.rs b/crates/js/src/formcalc.rs index 059276e93..449954a7f 100644 --- a/crates/js/src/formcalc.rs +++ b/crates/js/src/formcalc.rs @@ -2347,9 +2347,16 @@ impl Interp<'_> { } "time2num" | "isotime2num" => { let s = self.arg_str(args, 0)?; - let parts: Vec = s.split(':').filter_map(|p| p.trim().parse::().ok()).map(|v| v.min(1_000_000)).collect(); - let (h, m, sec) = (parts.first().copied().unwrap_or(0), parts.get(1).copied().unwrap_or(0), parts.get(2).copied().unwrap_or(0)); - Val::Num(((h * 3600 + m * 60 + sec) * 1000) as f64) + // ISO 8601 allows a leading "T", a trailing "Z" and fractional seconds. + let t = s.trim(); + let t = t.strip_prefix(['T', 't']).unwrap_or(t); + let t = t.strip_suffix(['Z', 'z']).unwrap_or(t); + let mut parts = t.split(':'); + let mut whole = || parts.next().and_then(|p| p.trim().parse::().ok()).unwrap_or(0).min(1_000_000); + let (h, m) = (whole(), whole()); + let sec = + parts.next().and_then(|p| p.trim().parse::().ok()).filter(|v| v.is_finite() && *v >= 0.0).unwrap_or(0.0).min(1_000_000.0); + Val::Num(((h * 3600 + m * 60) * 1000) as f64 + (sec * 1000.0).round()) } // ── financial "fv" => { diff --git a/crates/js/src/tests.rs b/crates/js/src/tests.rs index 55fa45760..877dce52b 100644 --- a/crates/js/src/tests.rs +++ b/crates/js/src/tests.rs @@ -680,6 +680,9 @@ mod formcalc { assert_eq!(calc(r#"Num2Date(45350, "YYYY-MM-DD")"#), "2024-02-29"); assert_eq!(calc(r#"Num2Date(Date2Num("2024-02-29") + 1, "MMMM D, YYYY")"#), "March 1, 2024"); assert_eq!(calc(r#"Time2Num("01:00:00")"#), "3600000"); + assert_eq!(calc(r#"IsoTime2Num("12:30:45Z")"#), "45045000"); + assert_eq!(calc(r#"IsoTime2Num("12:30:45.250")"#), "45045250"); + assert_eq!(calc(r#"IsoTime2Num("T12:30:45")"#), "45045000"); assert_eq!(calc(r#"Num2Time(3661000)"#), "01:01:01"); assert_eq!(calc("Round(Pmt(10000, 0.01, 12), 2)"), "888.49"); assert_eq!(calc("Round(FV(100, 0.01, 12), 2)"), "1268.25");