This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-beanutils.git
The following commit(s) were added to refs/heads/master by this push:
new 148cad5d Patched up version of PR #436: Reject out-of-range sql
date/time fields in DateTimeConverter.toDate
148cad5d is described below
commit 148cad5d3105ccad82830edad3454a17148e0e59
Author: Gary Gregory <[email protected]>
AuthorDate: Sun Aug 23 08:03:08 2026 -0400
Patched up version of PR #436: Reject out-of-range sql date/time fields
in DateTimeConverter.toDate
The problematic bug I fixed is three instances of double parsing.
Instead of going round and round, I applied the diff locally, made the
following changes, and pushed to master.
Double parsing for the `java.sql.Date` case.
Validation parses with the strict formatter and valueOf parses again.
Functionally correct, but wasteful. For example:
LocalDate.parse(value, SQL_DATE_FORMAT);
return type.cast(java.sql.Date.valueOf(value));
The parsed LocalDate is discarded. A more efficient form would be
Date.valueOf(LocalDate.parse(...)). Same for Time / Timestamp.
Double parsing of for the `java.sql.Time` case. Same as above.
Double parsing of for the `java.sql.Timestamp` case. Same as above.
I added details to Javadocs to clarify intent.
I added the follow unit tests:
In SqlDateConverterTest
testDefaultStringToTypeConvertValidLeapYear
testDefaultStringToTypeConvertInvalidNonLeapYearFeb29
testDefaultStringToTypeConvertValidBoundaryDates
testDefaultStringToTypeConvertStrictValidationMessage
In SqlTimeConverterTest
testDefaultStringToTypeConvertValidBoundaryTimes
testDefaultStringToTypeConvertInvalidTimeHourMinuteSecond
testDefaultStringToTypeConvertStrictValidationMessage
In SqlTimestampConverterTest
testDefaultStringToTypeConvertValidTimestampWithFraction
testDefaultStringToTypeConvertInvalidTimestampOutOfRangeDate
testDefaultStringToTypeConvertInvalidTimestampOutOfRangeTime
testDefaultStringToTypeConvertStrictValidationMessage
These tests cover valid leap-year and boundary values, invalid
out-of-range values for all three SQL types, timestamp fraction handling
and verification that the new ConversionException messages mention
strict validation.
---
.../beanutils2/converters/DateTimeConverter.java | 122 ++++++++++++++++++---
.../beanutils2/converters/DateConverterTest.java | 32 +++---
.../sql/converters/SqlDateConverterTest.java | 41 +++++++
.../sql/converters/SqlTimeConverterTest.java | 34 +++++-
.../sql/converters/SqlTimestampConverterTest.java | 50 ++++++++-
5 files changed, 243 insertions(+), 36 deletions(-)
diff --git
a/src/main/java/org/apache/commons/beanutils2/converters/DateTimeConverter.java
b/src/main/java/org/apache/commons/beanutils2/converters/DateTimeConverter.java
index 3e7952df..444d42b1 100644
---
a/src/main/java/org/apache/commons/beanutils2/converters/DateTimeConverter.java
+++
b/src/main/java/org/apache/commons/beanutils2/converters/DateTimeConverter.java
@@ -22,10 +22,16 @@ import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
+import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
+import java.time.format.ResolverStyle;
+import java.time.format.SignStyle;
+import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Date;
@@ -49,7 +55,6 @@ import org.apache.commons.beanutils2.ConversionException;
* <li>{@link java.sql.Time}</li>
* <li>{@link java.sql.Timestamp}</li>
* </ul>
- *
* <h2>String Conversions (to and from)</h2> This class provides a number of
ways in which date/time conversions to/from Strings can be achieved:
* <ul>
* <li>Using the SHORT date format for the default Locale, configure using:
@@ -76,15 +81,81 @@ import org.apache.commons.beanutils2.ConversionException;
* <li>If none of the above are configured the {@code toDate(String)} method
is used to convert from String to Date and the Dates's {@code toString()} method
* used to convert from Date to String.</li>
* </ul>
- *
+ * <p>
+ * For {@link java.sql.Date}, {@link java.sql.Time} and {@link
java.sql.Timestamp} the default String conversion, that is when {@code
useLocaleFormat} is
+ * {@code false} and no patterns are configured, requires the JDBC escape
format and validation is strict: out-of-range fields are rejected with a
+ * {@link ConversionException} instead of being rolled over.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> This strict JDBC validation applies only to the
default String conversion path. When the converter is configured with patterns
via
+ * {@link #setPattern(String)}/{@link #setPatterns(String[])} or with {@link
#setUseLocaleFormat(boolean)} = {@code true}, conversion is performed through
+ * {@link java.text.DateFormat}/{@link java.util.Calendar} and the strict JDBC
validation is bypassed.
+ * </p>
* <p>
* The <strong>Time Zone</strong> to use with the date format can be specified
using the {@link #setTimeZone(TimeZone)} method.
+ * </p>
*
* @param <D> The default value type.
* @since 1.8.0
*/
public abstract class DateTimeConverter<D> extends AbstractConverter<D> {
+ /**
+ * Strict DateTimeFormatter for the JDBC {@code java.sql.Date} escape
format, rejecting out-of-range fields that {@code valueOf} would roll over.
+ * <p>
+ * Values follow this STRICT format:
+ * <pre>
+ * YEAR 4-10 + '-' + MONTH + '-' + DAY
+ * </pre>
+ */
+ // @formatter:off
+ private static final DateTimeFormatter SQL_DATE_FORMAT = new
DateTimeFormatterBuilder()
+ .appendValue(ChronoField.YEAR, 4, 10,
SignStyle.EXCEEDS_PAD).appendLiteral('-')
+ .appendValue(ChronoField.MONTH_OF_YEAR).appendLiteral('-')
+ .appendValue(ChronoField.DAY_OF_MONTH)
+ .toFormatter()
+ .withResolverStyle(ResolverStyle.STRICT);
+ // @formatter:on
+
+ /**
+ * Strict DateTimeFormatter for the JDBC {@code java.sql.Time} escape
format, rejecting out-of-range fields that {@code valueOf} would roll over.
+ * <p>
+ * Values follow this STRICT format:
+ * <pre>
+ * HOUR_OF_DAY ':' MINUTE ':' SECOND
+ * </pre>
+ */
+ // @formatter:off
+ private static final DateTimeFormatter SQL_TIME_FORMAT = new
DateTimeFormatterBuilder()
+ .appendValue(ChronoField.HOUR_OF_DAY).appendLiteral(':')
+ .appendValue(ChronoField.MINUTE_OF_HOUR).appendLiteral(':')
+ .appendValue(ChronoField.SECOND_OF_MINUTE)
+ .toFormatter()
+ .withResolverStyle(ResolverStyle.STRICT);
+ // @formatter:on
+
+ /**
+ * Strict DateTimeFormatter for the JDBC {@code java.sql.Timestamp} escape
format, rejecting out-of-range fields that {@code valueOf} would roll over.
+ * <p>
+ * Values follow this STRICT format:
+ * <pre>
+ * YEAR '-' MONTH '-' DAY ' ' HOUR ':' MINUTE ':' SECOND [fraction]
+ * </pre>
+ */
+ // @formatter:off
+ private static final DateTimeFormatter SQL_TIMESTAMP_FORMAT = new
DateTimeFormatterBuilder()
+ .appendValue(ChronoField.YEAR, 4, 10,
SignStyle.EXCEEDS_PAD).appendLiteral('-')
+ .appendValue(ChronoField.MONTH_OF_YEAR).appendLiteral('-')
+ .appendValue(ChronoField.DAY_OF_MONTH).appendLiteral(' ')
+ .appendValue(ChronoField.HOUR_OF_DAY).appendLiteral(':')
+ .appendValue(ChronoField.MINUTE_OF_HOUR).appendLiteral(':')
+ .appendValue(ChronoField.SECOND_OF_MINUTE)
+ .optionalStart()
+ .appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true)
+ .optionalEnd().toFormatter()
+ .withResolverStyle(ResolverStyle.STRICT);
+ // @formatter:on
+
private String[] patterns;
private String displayPatterns;
private Locale locale;
@@ -569,9 +640,10 @@ public abstract class DateTimeConverter<D> extends
AbstractConverter<D> {
}
/**
- * Default String to Date conversion.
+ * Default String to date/time conversion.
* <p>
- * This method handles conversion from a String to the following types:
+ * This method handles conversion from a {@code String} to the following
types:
+ * </p>
* <ul>
* <li>{@link java.sql.Date}</li>
* <li>{@link java.sql.Time}</li>
@@ -579,36 +651,50 @@ public abstract class DateTimeConverter<D> extends
AbstractConverter<D> {
* <li>{@link java.time.Instant}</li>
* </ul>
* <p>
- * <strong>N.B.</strong> No default String conversion mechanism is
provided for {@link java.util.Date} and {@link java.util.Calendar} type.
+ * For the {@code java.sql} types the String must be in the JDBC escape
format and validation is strict: out-of-range fields, for example {@code
2006-02-31}
+ * or {@code 25:70:90} are rejected with a {@link ConversionException}
instead of being rolled over.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> This strict JDBC validation applies only to the
default String conversion path when no patterns are configured and
+ * {@code useLocaleFormat} is {@code false}. When the converter is
configured with patterns or {@link #setUseLocaleFormat(boolean)} = {@code true},
+ * conversion is performed through {@link java.text.DateFormat}/{@link
java.util.Calendar} and the strict JDBC validation is bypassed.
+ * </p>
+ * <p>
+ * No default String conversion mechanism is provided for {@link
java.util.Date} and {@link java.util.Calendar}.
+ * </p>
*
- * @param <T> The target type
- * @param type The date type to convert to
- * @param value The String value to convert.
- * @return The converted Number value.
+ * @param <T> the target type
+ * @param type the date/time type to convert to
+ * @param value the String value to convert
+ * @return the converted date/time value
+ * @throws ConversionException if the String cannot be converted to the
target type
*/
private <T> T toDate(final Class<T> type, final String value) {
// java.sql.Date
if (type.equals(java.sql.Date.class)) {
try {
- return type.cast(java.sql.Date.valueOf(value));
- } catch (final IllegalArgumentException e) {
- throw new ConversionException("String must be in JDBC format
[yyyy-MM-dd] to create a java.sql.Date");
+ return type.cast(java.sql.Date.valueOf(LocalDate.parse(value,
SQL_DATE_FORMAT)));
+ } catch (final IllegalArgumentException | DateTimeParseException
e) {
+ throw new ConversionException(
+ "String must be in JDBC format [yyyy-MM-dd] to create
a java.sql.Date; validation is strict, out-of-range fields are rejected");
}
}
// java.sql.Time
if (type.equals(java.sql.Time.class)) {
try {
- return type.cast(java.sql.Time.valueOf(value));
- } catch (final IllegalArgumentException e) {
- throw new ConversionException("String must be in JDBC format
[HH:mm:ss] to create a java.sql.Time");
+ return type.cast(java.sql.Time.valueOf(LocalTime.parse(value,
SQL_TIME_FORMAT)));
+ } catch (final IllegalArgumentException | DateTimeParseException
e) {
+ throw new ConversionException(
+ "String must be in JDBC format [HH:mm:ss] to create a
java.sql.Time; validation is strict, out-of-range fields are rejected");
}
}
// java.sql.Timestamp
if (type.equals(java.sql.Timestamp.class)) {
try {
- return type.cast(java.sql.Timestamp.valueOf(value));
- } catch (final IllegalArgumentException e) {
- throw new ConversionException("String must be in JDBC format
[yyyy-MM-dd HH:mm:ss.fffffffff] to create a java.sql.Timestamp");
+ return
type.cast(java.sql.Timestamp.valueOf(LocalDateTime.parse(value,
SQL_TIMESTAMP_FORMAT)));
+ } catch (final IllegalArgumentException | DateTimeParseException
e) {
+ throw new ConversionException("String must be in JDBC format
[yyyy-MM-dd HH:mm:ss.fffffffff] to create a java.sql.Timestamp; "
+ + "validation is strict, out-of-range fields are
rejected");
}
}
// java.time.Instant
diff --git
a/src/test/java/org/apache/commons/beanutils2/converters/DateConverterTest.java
b/src/test/java/org/apache/commons/beanutils2/converters/DateConverterTest.java
index 7bb5380a..ed154453 100644
---
a/src/test/java/org/apache/commons/beanutils2/converters/DateConverterTest.java
+++
b/src/test/java/org/apache/commons/beanutils2/converters/DateConverterTest.java
@@ -62,14 +62,16 @@ class DateConverterTest extends
AbstractDateConverterTest<Date> {
}
/**
- * Convert from a Calendar to the appropriate Date type
- *
- * @param value The Calendar value to convert
- * @return The converted value
+ * For {@code getTime()} in {@code [Long.MIN_VALUE, Long.MIN_VALUE + 807]}
the whole-second term
+ * {@code Math.floorDiv(getTime(), 1000) * 1000} wraps around {@link
Long#MIN_VALUE}, but adding the non-negative
+ * {@code getNanos() / 1_000_000} wraps it back: the two terms reconstruct
{@code getTime()} exactly in
+ * two's-complement arithmetic, so no overflow guard is needed.
*/
- @Override
- protected Date toType(final Calendar value) {
- return value.getTime();
+ @Test
+ void testConvertExtremePreEpochSqlTimestamp() {
+ assertEquals(Long.MIN_VALUE,
makeConverter().convert(getExpectedType(), new
Timestamp(Long.MIN_VALUE)).getTime());
+ // last value whose whole-second term still wraps
+ assertEquals(Long.MIN_VALUE + 807,
makeConverter().convert(getExpectedType(), new Timestamp(Long.MIN_VALUE +
807)).getTime());
}
/**
@@ -85,15 +87,13 @@ class DateConverterTest extends
AbstractDateConverterTest<Date> {
}
/**
- * For {@code getTime()} in {@code [Long.MIN_VALUE, Long.MIN_VALUE + 807]}
the whole-second term
- * {@code Math.floorDiv(getTime(), 1000) * 1000} wraps around {@link
Long#MIN_VALUE}, but adding the non-negative
- * {@code getNanos() / 1_000_000} wraps it back: the two terms reconstruct
{@code getTime()} exactly in
- * two's-complement arithmetic, so no overflow guard is needed.
+ * Convert from a Calendar to the appropriate Date type
+ *
+ * @param value The Calendar value to convert
+ * @return The converted value
*/
- @Test
- void testConvertExtremePreEpochSqlTimestamp() {
- assertEquals(Long.MIN_VALUE,
makeConverter().convert(getExpectedType(), new
Timestamp(Long.MIN_VALUE)).getTime());
- // last value whose whole-second term still wraps
- assertEquals(Long.MIN_VALUE + 807,
makeConverter().convert(getExpectedType(), new Timestamp(Long.MIN_VALUE +
807)).getTime());
+ @Override
+ protected Date toType(final Calendar value) {
+ return value.getTime();
}
}
diff --git
a/src/test/java/org/apache/commons/beanutils2/sql/converters/SqlDateConverterTest.java
b/src/test/java/org/apache/commons/beanutils2/sql/converters/SqlDateConverterTest.java
index 18cbe3eb..821f03ef 100644
---
a/src/test/java/org/apache/commons/beanutils2/sql/converters/SqlDateConverterTest.java
+++
b/src/test/java/org/apache/commons/beanutils2/sql/converters/SqlDateConverterTest.java
@@ -18,10 +18,13 @@
package org.apache.commons.beanutils2.sql.converters;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.sql.Date;
import java.util.Calendar;
+import org.apache.commons.beanutils2.ConversionException;
import org.apache.commons.beanutils2.converters.AbstractDateConverterTest;
import org.apache.commons.beanutils2.converters.DateTimeConverter;
import org.junit.jupiter.api.Test;
@@ -62,6 +65,7 @@ class SqlDateConverterTest extends
AbstractDateConverterTest<Date> {
return new SqlDateConverter(defaultValue);
}
+
/**
* Test default String to java.sql.Date conversion
*/
@@ -80,6 +84,43 @@ class SqlDateConverterTest extends
AbstractDateConverterTest<Date> {
// Invalid String --> java.sql.Date Conversion
invalidConversion(converter, "01/01/2006");
+
+ // Out-of-range fields must be rejected, not silently rolled over
(2006-02-31 -> 2006-03-03)
+ invalidConversion(converter, "2006-02-31");
+ invalidConversion(converter, "2006-13-01");
+ }
+
+ @Test
+ void testDefaultStringToTypeConvertInvalidNonLeapYearFeb29() {
+ final SqlDateConverter converter = makeConverter();
+ converter.setUseLocaleFormat(false);
+ invalidConversion(converter, "2005-02-29");
+ invalidConversion(converter, "1900-02-29");
+ }
+
+ @Test
+ void testDefaultStringToTypeConvertStrictValidationMessage() {
+ final SqlDateConverter converter = makeConverter();
+ converter.setUseLocaleFormat(false);
+ final ConversionException ex = assertThrows(ConversionException.class,
() -> converter.convert(getExpectedType(), "2006-02-31"));
+ assertTrue(ex.getMessage().contains("validation is strict"), "Message
must mention strict validation");
+ }
+
+ @Test
+ void testDefaultStringToTypeConvertValidBoundaryDates() {
+ final SqlDateConverter converter = makeConverter();
+ converter.setUseLocaleFormat(false);
+ validConversion(converter, toType("0001-01-01", "yyyy-MM-dd", null),
"0001-01-01");
+ validConversion(converter, toType("9999-12-31", "yyyy-MM-dd", null),
"9999-12-31");
+ }
+
+ @Test
+ void testDefaultStringToTypeConvertValidLeapYear() {
+ final SqlDateConverter converter = makeConverter();
+ converter.setUseLocaleFormat(false);
+ final String testString = "2004-02-29";
+ final Object expected = toType(testString, "yyyy-MM-dd", null);
+ validConversion(converter, expected, testString);
}
/**
diff --git
a/src/test/java/org/apache/commons/beanutils2/sql/converters/SqlTimeConverterTest.java
b/src/test/java/org/apache/commons/beanutils2/sql/converters/SqlTimeConverterTest.java
index 918290c7..4fe4a8c7 100644
---
a/src/test/java/org/apache/commons/beanutils2/sql/converters/SqlTimeConverterTest.java
+++
b/src/test/java/org/apache/commons/beanutils2/sql/converters/SqlTimeConverterTest.java
@@ -17,10 +17,14 @@
package org.apache.commons.beanutils2.sql.converters;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
import java.sql.Time;
import java.util.Calendar;
import java.util.Locale;
+import org.apache.commons.beanutils2.ConversionException;
import org.apache.commons.beanutils2.converters.AbstractDateConverterTest;
import org.junit.jupiter.api.Test;
@@ -77,6 +81,35 @@ class SqlTimeConverterTest extends
AbstractDateConverterTest<Time> {
// Invalid String --> java.sql.Time Conversion
invalidConversion(converter, "15:36");
+
+ // Out-of-range fields must be rejected, not silently rolled over
(25:70:90 -> 02:11:30)
+ invalidConversion(converter, "25:70:90");
+ invalidConversion(converter, "-1:-1:-1");
+ }
+
+ @Test
+ void testDefaultStringToTypeConvertInvalidTimeHourMinuteSecond() {
+ final SqlTimeConverter converter = makeConverter();
+ converter.setUseLocaleFormat(false);
+ invalidConversion(converter, "24:00:00");
+ invalidConversion(converter, "12:60:00");
+ invalidConversion(converter, "12:00:60");
+ }
+
+ @Test
+ void testDefaultStringToTypeConvertStrictValidationMessage() {
+ final SqlTimeConverter converter = makeConverter();
+ converter.setUseLocaleFormat(false);
+ final ConversionException ex = assertThrows(ConversionException.class,
() -> converter.convert(getExpectedType(), "25:70:90"));
+ assertTrue(ex.getMessage().contains("validation is strict"));
+ }
+
+ @Test
+ void testDefaultStringToTypeConvertValidBoundaryTimes() {
+ final SqlTimeConverter converter = makeConverter();
+ converter.setUseLocaleFormat(false);
+ validConversion(converter, toType("00:00:00", "HH:mm:ss", null),
"00:00:00");
+ validConversion(converter, toType("23:59:59", "HH:mm:ss", null),
"23:59:59");
}
/**
@@ -127,5 +160,4 @@ class SqlTimeConverterTest extends
AbstractDateConverterTest<Time> {
protected Time toType(final Calendar value) {
return new Time(getTimeInMillis(value));
}
-
}
diff --git
a/src/test/java/org/apache/commons/beanutils2/sql/converters/SqlTimestampConverterTest.java
b/src/test/java/org/apache/commons/beanutils2/sql/converters/SqlTimestampConverterTest.java
index 902735e4..72415c6d 100644
---
a/src/test/java/org/apache/commons/beanutils2/sql/converters/SqlTimestampConverterTest.java
+++
b/src/test/java/org/apache/commons/beanutils2/sql/converters/SqlTimestampConverterTest.java
@@ -88,6 +88,55 @@ class SqlTimestampConverterTest extends
AbstractDateConverterTest<Timestamp> {
invalidConversion(converter, "2006/09/21 15:36:01.0");
invalidConversion(converter, "2006-10-22");
invalidConversion(converter, "15:36:01");
+
+ // Out-of-range fields must be rejected, not silently rolled over
(2006-02-31 25:70:90 -> 2006-03-04 02:11:30)
+ invalidConversion(converter, "2006-02-31 15:36:01.0");
+ invalidConversion(converter, "2006-10-23 25:70:90.0");
+ }
+
+ @Test
+ void testDefaultStringToTypeConvertInvalidTimestampOutOfRangeDate() {
+ final SqlTimestampConverter converter = makeConverter();
+ converter.setUseLocaleFormat(false);
+ invalidConversion(converter, "2006-02-31 15:36:01");
+ invalidConversion(converter, "2006-13-01 00:00:00");
+ }
+
+ @Test
+ void testDefaultStringToTypeConvertInvalidTimestampOutOfRangeTime() {
+ final SqlTimestampConverter converter = makeConverter();
+ converter.setUseLocaleFormat(false);
+ invalidConversion(converter, "2006-10-23 25:00:00");
+ invalidConversion(converter, "2006-10-23 12:60:00");
+ }
+
+ /**
+ * Test default String to {@code java.sql.Timestamp} conversion with
fractional seconds.
+ * <p>
+ * The strict {@link DateTimeConverter#SQL_TIMESTAMP_FORMAT} accepts an
optional fractional second part. The helper
+ * {@link AbstractDateConverterTest#toType(String,String,Locale)} builds
the expected value via {@link SimpleDateFormat}, which only supports millisecond
+ * precision {@code S} = 1-3 digits. Therefore, the test uses a 3-digit
fraction {@code .123} for the {@code toType} comparison and additionally
verifies
+ * that {@code Timestamp.valueOf} correctly handles the full 9-digit
nanosecond fraction that the strict formatter accepts. This avoids the
+ * {@code ParseException} that occurs with {@code SSSSSSSSS} in {@code
SimpleDateFormat}.
+ * </p>
+ */
+ @Test
+ void testDefaultStringToTypeConvertValidTimestampWithFraction() {
+ final SqlTimestampConverter converter = makeConverter();
+ converter.setUseLocaleFormat(false);
+ // 3-digit fraction – SimpleDateFormat can create the expected value
+ final String testString1 = "2006-10-23 15:36:01.123";
+ final Object expected1 = toType(testString1, "yyyy-MM-dd
HH:mm:ss.SSS", null);
+ validConversion(converter, expected1, testString1);
+ // No fraction – also valid
+ final String testString2 = "2006-10-23 15:36:01";
+ final Object expected2 = toType(testString2, "yyyy-MM-dd HH:mm:ss",
null);
+ validConversion(converter, expected2, testString2);
+ // Full nanosecond fraction – expected built directly with valueOf
+ // valueOf handles up to 9 digits, toType cannot, so build expected
manually
+ final String testString3 = "2006-10-23 15:36:01.123456789";
+ final java.sql.Timestamp expected3 =
java.sql.Timestamp.valueOf("2006-10-23 15:36:01.123456789");
+ validConversion(converter, expected3, testString3);
}
/**
@@ -142,5 +191,4 @@ class SqlTimestampConverterTest extends
AbstractDateConverterTest<Timestamp> {
protected Timestamp toType(final Calendar value) {
return new Timestamp(getTimeInMillis(value));
}
-
}