This is an automated email from the ASF dual-hosted git repository.

lukaszlenart pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/struts.git

commit 711f3ffb12fbbaf7cd618cbfc7608ca0fd4df1f1
Author: Lukasz Lenart <[email protected]>
AuthorDate: Sun Oct 25 10:05:42 2026 +0100

    WW-5816 fix(validation): parse localdate XML bounds strictly as ISO dates
    
    XML min/max params reached setMin/setMax through DateConverter, which
    tries the application's struts.date.format before ISO: with yyyy-dd-MM,
    a min of 2026-01-02 became February 1. The annotation bounds were
    already ISO-only.
    
    LocalDateRangeFieldValidator-conversion.properties maps min and max to
    LocalDateRangeBoundConverter, which parses them with LocalDate.parse, so
    XML bounds no longer depend on the display format.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../validators/LocalDateRangeBoundConverter.java   | 51 ++++++++++++++++++++++
 ...alDateRangeFieldValidator-conversion.properties | 20 +++++++++
 .../LocalDateRangeFieldValidatorTest.java          | 16 +++++++
 3 files changed, 87 insertions(+)

diff --git 
a/core/src/main/java/org/apache/struts2/validator/validators/LocalDateRangeBoundConverter.java
 
b/core/src/main/java/org/apache/struts2/validator/validators/LocalDateRangeBoundConverter.java
new file mode 100644
index 000000000..e6d7be474
--- /dev/null
+++ 
b/core/src/main/java/org/apache/struts2/validator/validators/LocalDateRangeBoundConverter.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.validator.validators;
+
+import org.apache.struts2.conversion.TypeConversionException;
+import org.apache.struts2.conversion.impl.DefaultTypeConverter;
+
+import java.lang.reflect.Member;
+import java.time.LocalDate;
+import java.time.format.DateTimeParseException;
+import java.util.Map;
+
+/**
+ * Converts the {@code min} / {@code max} params of {@link 
LocalDateRangeFieldValidator} strictly as ISO dates,
+ * mapped in {@code LocalDateRangeFieldValidator-conversion.properties}, so a 
bound does not depend on the
+ * application's {@code struts.date.format}.
+ *
+ * @since 7.5.0
+ */
+public final class LocalDateRangeBoundConverter extends DefaultTypeConverter {
+
+    @Override
+    public Object convertValue(Map<String, Object> context, Object target, 
Member member, String propertyName,
+                               Object value, Class toType) {
+        if (value instanceof String bound) {
+            try {
+                return LocalDate.parse(bound.trim());
+            } catch (DateTimeParseException e) {
+                throw new TypeConversionException("Could not parse " + 
propertyName + " as an ISO date (yyyy-MM-dd)", e);
+            }
+        }
+        return value;
+    }
+
+}
diff --git 
a/core/src/main/resources/org/apache/struts2/validator/validators/LocalDateRangeFieldValidator-conversion.properties
 
b/core/src/main/resources/org/apache/struts2/validator/validators/LocalDateRangeFieldValidator-conversion.properties
new file mode 100644
index 000000000..b9fa510e8
--- /dev/null
+++ 
b/core/src/main/resources/org/apache/struts2/validator/validators/LocalDateRangeFieldValidator-conversion.properties
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+min=org.apache.struts2.validator.validators.LocalDateRangeBoundConverter
+max=org.apache.struts2.validator.validators.LocalDateRangeBoundConverter
diff --git 
a/core/src/test/java/org/apache/struts2/validator/validators/LocalDateRangeFieldValidatorTest.java
 
b/core/src/test/java/org/apache/struts2/validator/validators/LocalDateRangeFieldValidatorTest.java
index b1d6eb495..c4fc4e968 100644
--- 
a/core/src/test/java/org/apache/struts2/validator/validators/LocalDateRangeFieldValidatorTest.java
+++ 
b/core/src/test/java/org/apache/struts2/validator/validators/LocalDateRangeFieldValidatorTest.java
@@ -21,6 +21,7 @@ package org.apache.struts2.validator.validators;
 import org.apache.struts2.ActionContext;
 import org.apache.struts2.ObjectFactory;
 import org.apache.struts2.XWorkTestCase;
+import org.apache.struts2.text.StubTextProvider;
 import org.apache.struts2.text.TextProviderFactory;
 import org.apache.struts2.util.ValueStack;
 import org.apache.struts2.util.ValueStackFactory;
@@ -97,6 +98,21 @@ public class LocalDateRangeFieldValidatorTest extends 
XWorkTestCase {
         assertEquals(MAX, validator.getMax());
     }
 
+    public void testReadsConfigurationBoundsAsIsoWhateverTheGlobalDateFormat() 
throws Exception {
+        ActionContext.getContext().getValueStack()
+                .push(new 
StubTextProvider(Map.of(org.apache.struts2.components.Date.DATETAG_PROPERTY, 
"yyyy-dd-MM")));
+        Map<String, Object> context = 
ActionContext.getContext().getContextMap();
+        Map<String, Object> params = new HashMap<>();
+        params.put("min", "2026-01-02");
+        params.put("max", "2026-03-04");
+
+        LocalDateRangeFieldValidator validator = 
(LocalDateRangeFieldValidator) container.getInstance(ObjectFactory.class)
+                .buildValidator(LocalDateRangeFieldValidator.class.getName(), 
params, context);
+
+        assertEquals(LocalDate.of(2026, 1, 2), validator.getMin());
+        assertEquals(LocalDate.of(2026, 3, 4), validator.getMax());
+    }
+
     private ValidatorContext validate(LocalDate birthday) {
         LocalDateAction action = new LocalDateAction(birthday);
         ValidatorContext context = new DummyValidatorContext(action, tpf);

Reply via email to