[
https://issues.apache.org/jira/browse/WW-5686?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Lukasz Lenart updated WW-5686:
------------------------------
Summary: <s:date/> renders a java.sql.Date with the current wall-clock
time, making DateTest.testJavaSqlDate flaky (was: <s:date/> renders a
java.sql.Date with the current wall-clock time, making DateTest.testJavaSqlDate
flaky)
> <s:date/> renders a java.sql.Date with the current wall-clock time, making
> DateTest.testJavaSqlDate flaky
> ---------------------------------------------------------------------------------------------------------
>
> Key: WW-5686
> URL: https://issues.apache.org/jira/browse/WW-5686
> Project: Struts 2
> Issue Type: Bug
> Reporter: Lukasz Lenart
> Priority: Minor
>
> {{org.apache.struts2.components.Date.end}} fills in the missing time-of-day
> of a {{java.sql.Date}} from the current clock:
> {code:java}Object dateObject = findValue(name);
> if (dateObject instanceof java.sql.Date) {
> date = ((java.sql.Date)
> dateObject).toLocalDate().atTime(LocalTime.now(tz)).atZone(tz);
> } else if (dateObject instanceof java.sql.Time) {
> date = ((java.sql.Time)
> dateObject).toLocalTime().atDate(ZonedDateTime.now(tz).toLocalDate()).atZone(tz);
> }
> {code}
> Two consequences.
> h3. 1. The rendered output is non-deterministic
> A {{java.sql.Date}} carries no time-of-day, so any format that includes time
> fields renders whatever the wall clock happens to say at render time. The
> same value rendered twice in one page produces two different strings.
> This was not always the behaviour. WW-5197 (6.0.3) added {{java.sql.Date}}
> support as {{toLocalDate().atStartOfDay(tz)}} — deterministic midnight.
> WW-5272 (commit {{08de24588}}, 6.2.0, "Extends <s:date/> to support
> java.sql.Time") changed it to {{atTime(LocalTime.now(tz))}} while adding the
> {{java.sql.Time}} branch, and changed {{DateTest}} in the same commit from
> {{getDateInstance(MEDIUM)}} to {{getDateTimeInstance(MEDIUM, MEDIUM)}} so the
> assertion would cover the newly rendered time. The {{java.sql.Time}} branch
> genuinely needs a date to complete a {{ZonedDateTime}}, but the
> {{java.sql.Date}} branch has no equivalent need for _now's_ time — midnight
> completed it just as well, and did so reproducibly. This looks like
> collateral of the {{java.sql.Time}} work rather than an intended change to
> {{java.sql.Date}} rendering, and it shipped without a note.
> h3. 2. {{DateTest.testJavaSqlDate}} is inherently flaky
> The test builds its expected string from {{new
> java.sql.Date(System.currentTimeMillis())}}, whose underlying millis still
> carry the capture-time time-of-day, and compares it against output whose time
> comes from {{LocalTime.now()}} at render. The two disagree whenever the clock
> crosses a second boundary between the two calls.
> Observed on the SonarCloud job of [PR
> #1859|https://github.com/apache/struts/pull/1859] (an unrelated OGNL change):
> {code}DateTest.testJavaSqlDate:104 expected:<Aug 23, 2026, 4:37:1[4] PM> but
> was:<Aug 23, 2026, 4:37:1[5] PM>
> {code}
> The same commit passed Build and Test on JDK 17, 21 and 25 — only that one
> job happened to straddle the boundary. The failure window is roughly the
> elapsed time between the two calls, so it is rare but will keep failing
> unrelated PRs.
> {{testJavaSqlTime}} is not affected in practice: it formats with
> {{hh:mm:ss}}, and {{toLocalTime()}} preserves the value's own time, so the
> substituted date is never rendered. It would only misfire across a midnight
> boundary.
> h3. Suggested fix
> Restore {{toLocalDate().atStartOfDay(tz)}} for {{java.sql.Date}} and assert
> against a fixed instant in the test rather than the current time. That
> reverts {{java.sql.Date}} rendering to its pre-6.2.0 semantics, so it needs a
> Version Notes entry.
> If the current behaviour is instead considered intentional, the test still
> has to stop racing the clock — inject a fixed clock or assert only the date
> fields — and the substitution should be documented on the tag.
> _Affects:_ 6.2.0 onwards, on every maintained line.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)