This is an automated email from the ASF dual-hosted git repository.
apupier pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new aeaf7d9db100 CAMEL-25151: camel-bindy - a java.util.Date value longer
than its pattern is parsed
aeaf7d9db100 is described below
commit aeaf7d9db100a66fe056acab7cffcffda381c8a6
Author: smjain <[email protected]>
AuthorDate: Wed Sep 30 07:51:13 2026 +0530
CAMEL-25151: camel-bindy - a java.util.Date value longer than its pattern
is parsed
DateFormatFactory rejected every java.util.Date value with more characters
than the pattern, to reject a date followed by other characters (such as
20090901-10:32:30 for yyyyMMdd). But a pattern letter count is a minimum
width: M/d/yyyy formats 12/25/2026, h:mm a formats 11:45 PM and MMMM
formats September, so valid dates, including the ones Bindy writes itself,
failed with "Date provided does not fit the pattern defined".
CAMEL-11620 removed the same check from the java.time factories.
A value longer than the pattern is now parsed with a ParsePosition and
accepted when the whole value is used; otherwise the same FormatException
is thrown as before. A value no longer than the pattern is parsed exactly
as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../bindy/format/factories/DateFormatFactory.java | 13 +-
...dyCsvDatePatternValueLongerThanPatternTest.java | 148 +++++++++++++++++++++
2 files changed, 160 insertions(+), 1 deletion(-)
diff --git
a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/format/factories/DateFormatFactory.java
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/format/factories/DateFormatFactory.java
index d63e7da90540..1861a4fe97e5 100644
---
a/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/format/factories/DateFormatFactory.java
+++
b/components/camel-bindy/src/main/java/org/apache/camel/dataformat/bindy/format/factories/DateFormatFactory.java
@@ -17,6 +17,7 @@
package org.apache.camel.dataformat.bindy.format.factories;
import java.text.DateFormat;
+import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
@@ -84,7 +85,17 @@ public class DateFormatFactory extends AbstractFormatFactory
{
return date;
} else {
- throw new FormatException("Date provided does not fit the
pattern defined");
+ // A formatted date can be longer than its pattern (M, d, H or
h with two digits, MMMM, EEEE, a),
+ // so a longer string is accepted when it is parsed
completely. A date followed by other
+ // characters, such as 20090901-10:32:30 with the pattern
yyyyMMdd, is still rejected
+ df.setLenient(false);
+ ParsePosition position = new ParsePosition(0);
+ date = df.parse(string, position);
+ if (date == null || position.getIndex() != string.length()) {
+ throw new FormatException("Date provided does not fit the
pattern defined");
+ }
+
+ return date;
}
}
diff --git
a/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/csv/BindyCsvDatePatternValueLongerThanPatternTest.java
b/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/csv/BindyCsvDatePatternValueLongerThanPatternTest.java
new file mode 100644
index 000000000000..f126abcfb61b
--- /dev/null
+++
b/components/camel-bindy/src/test/java/org/apache/camel/dataformat/bindy/csv/BindyCsvDatePatternValueLongerThanPatternTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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.camel.dataformat.bindy.csv;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.dataformat.bindy.annotation.CsvRecord;
+import org.apache.camel.dataformat.bindy.annotation.DataField;
+import org.apache.camel.dataformat.bindy.format.FormatException;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * A java.util.Date field is formatted with its pattern, and the formatted
text can be longer than the pattern (M, d, h
+ * with two digits, MMMM, a). Such a value must be read back.
+ */
+public class BindyCsvDatePatternValueLongerThanPatternTest extends
CamelTestSupport {
+
+ private static Date date(String text) throws Exception {
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm",
Locale.ENGLISH);
+ return df.parse(text);
+ }
+
+ @Test
+ public void testUnmarshalValuesLongerThanPattern() throws Exception {
+ String body = "12/25/2026;25.12.2026;December 25 2026;11:45 PM\r\n";
+ Row row = template.requestBody("direct:unmarshal", body, Row.class);
+ assertEquals(date("2026-12-25 00:00"), row.getUs());
+ assertEquals(date("2026-12-25 00:00"), row.getDe());
+ assertEquals(date("2026-12-25 00:00"), row.getLongMonth());
+ assertEquals(date("1970-01-01 23:45"), row.getTime());
+ }
+
+ @Test
+ public void testMarshalUnmarshalRoundTrip() throws Exception {
+ Row row = new Row();
+ row.setUs(date("2026-11-30 00:00"));
+ row.setDe(date("2026-10-15 00:00"));
+ row.setLongMonth(date("2026-09-30 00:00"));
+ row.setTime(date("1970-01-01 10:05"));
+
+ String csv = template.requestBody("direct:marshal", row, String.class);
+ assertEquals("11/30/2026;15.10.2026;September 30 2026;10:05 AM\r\n",
csv);
+
+ Row back = template.requestBody("direct:unmarshal", csv, Row.class);
+ assertEquals(row.getUs(), back.getUs());
+ assertEquals(row.getDe(), back.getDe());
+ assertEquals(row.getLongMonth(), back.getLongMonth());
+ assertEquals(row.getTime(), back.getTime());
+ }
+
+ @Test
+ public void testUnmarshalLongerValueThatIsNotTheDateIsRejected() {
+ // a date followed by other characters, and a value that is not a
valid date
+ for (String value : new String[] { "12/25/2026-01", "13/45/2026" }) {
+ String body = value + ";25.12.2026;December 25 2026;11:45 PM\r\n";
+ CamelExecutionException e =
assertThrows(CamelExecutionException.class,
+ () -> template.requestBody("direct:unmarshal", body,
Row.class));
+ assertInstanceOf(FormatException.class, e.getCause().getCause(),
value);
+ assertEquals("Date provided does not fit the pattern defined,
position: 1, line: 1", e.getCause().getMessage(),
+ value);
+ }
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ BindyCsvDataFormat format = new BindyCsvDataFormat(Row.class);
+ format.setLocale("en");
+ from("direct:marshal").marshal(format);
+ from("direct:unmarshal").unmarshal(format);
+ }
+ };
+ }
+
+ @CsvRecord(separator = ";")
+ public static class Row {
+
+ @DataField(pos = 1, pattern = "M/d/yyyy")
+ private Date us;
+
+ @DataField(pos = 2, pattern = "d.M.yyyy")
+ private Date de;
+
+ @DataField(pos = 3, pattern = "MMMM d yyyy")
+ private Date longMonth;
+
+ @DataField(pos = 4, pattern = "h:mm a")
+ private Date time;
+
+ public Date getUs() {
+ return us;
+ }
+
+ public void setUs(Date us) {
+ this.us = us;
+ }
+
+ public Date getDe() {
+ return de;
+ }
+
+ public void setDe(Date de) {
+ this.de = de;
+ }
+
+ public Date getLongMonth() {
+ return longMonth;
+ }
+
+ public void setLongMonth(Date longMonth) {
+ this.longMonth = longMonth;
+ }
+
+ public Date getTime() {
+ return time;
+ }
+
+ public void setTime(Date time) {
+ this.time = time;
+ }
+ }
+}