From e315994177279a9e517d4ffc629fcc567518b2a3 Mon Sep 17 00:00:00 2001 From: Stephen Dennis Date: Mon, 10 Aug 2026 19:18:00 -0600 Subject: [PATCH] fix(softcode): timefmt rejects unparseable seconds argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SetSecondsString was ignored on failure, so a default-constructed CLinearTimeAbsolute (FILETIME zero / 1601-01-01 UTC) was formatted. In western zones that looked like "Sunday, December 31, 1600, 05:00 PM" — e.g. timefmt(..., #-1 INVALID DATE) after a failed convtime(). Return #-1 INVALID DATE like convsecs/convtime on bad input. Smoke TC004 pins the case. --- mux/modules/engine/functions.cpp | 12 +++++++++++- testcases/etimefmt_fn.mux | 23 +++++++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/mux/modules/engine/functions.cpp b/mux/modules/engine/functions.cpp index 9679c3f2f..ab38e8ed6 100644 --- a/mux/modules/engine/functions.cpp +++ b/mux/modules/engine/functions.cpp @@ -1028,7 +1028,17 @@ static FUNCTION(fun_timefmt) CLinearTimeAbsolute lta, ltaUTC; if (nfargs == 2) { - ltaUTC.SetSecondsString(fargs[1]); + // SetSecondsString fails closed without changing the absolute; a + // default-constructed ltaUTC is FILETIME zero (1601-01-01 UTC). + // Ignoring the failure made garbage such as "#-1 INVALID DATE" + // format as "Sunday, December 31, 1600, …" in western timezones + // instead of rejecting the seconds argument. + // + if (!ltaUTC.SetSecondsString(fargs[1])) + { + safe_str(S_("#-1 INVALID DATE"), buff, bufc); + return; + } } else { diff --git a/testcases/etimefmt_fn.mux b/testcases/etimefmt_fn.mux index a2bd5a42b..b08354a98 100644 --- a/testcases/etimefmt_fn.mux +++ b/testcases/etimefmt_fn.mux @@ -70,11 +70,30 @@ eq(strlen(timefmt($Y)), 4) )= { - @log smoke=TC003: timefmt documented shorthands. Succeeded.; + @log smoke=TC003: timefmt documented shorthands. Succeeded. + }, + { + @log smoke=TC003: timefmt documented shorthands. Failed (F=[timefmt($F, 946684800)] ymd=[timefmt($Y-$m-$d, 946684800)] R=[timefmt($R, 946684800)] hm=[timefmt($H:$M, 946684800)] r=[timefmt($r, 946684800)]). + } +- + +# Test Case #4 - timefmt rejects unparseable instead of formatting +# FILETIME zero (1601-01-01 UTC), which surfaces as Dec 31 1600 in western +# zones. A failed convtime() result fed into timefmt used to do exactly that. +# (Last case: @notify via tr.done so the suite can complete.) +# +&tr.tc004 test_etimefmt_fn= + @if cand( + strmatch(timefmt($A\, $B $d\, $Y\, $I:$M $p,#-1 INVALID DATE), #-1 INVALID DATE), + strmatch(timefmt($Y, not-a-number), #-1 INVALID DATE), + strmatch(timefmt($Y, xyzzy), #-1 INVALID DATE) + )= + { + @log smoke=TC004: timefmt invalid secs. Succeeded.; @trig me/tr.done }, { - @log smoke=TC003: timefmt documented shorthands. Failed (F=[timefmt($F, 946684800)] ymd=[timefmt($Y-$m-$d, 946684800)] R=[timefmt($R, 946684800)] hm=[timefmt($H:$M, 946684800)] r=[timefmt($r, 946684800)]).; + @log smoke=TC004: timefmt invalid secs. Failed (inv=[timefmt($A\, $B $d\, $Y\, $I:$M $p,#-1 INVALID DATE)] nan=[timefmt($Y, not-a-number)] xyz=[timefmt($Y, xyzzy)]).; @trig me/tr.done } -