Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,16 @@
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;
Expand Down Expand Up @@ -85,6 +91,23 @@
*/
public abstract class DateTimeConverter<D> extends AbstractConverter<D> {

/** Strict validator for the JDBC {@code java.sql.Date} escape format, rejecting out-of-range fields that {@code valueOf} would roll over. */
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);

/** Strict validator for the JDBC {@code java.sql.Time} escape format, rejecting out-of-range fields that {@code valueOf} would roll over. */
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);

/** Strict validator for the JDBC {@code java.sql.Timestamp} escape format, rejecting out-of-range fields that {@code valueOf} would roll over. */
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);

private String[] patterns;
private String displayPatterns;
private Locale locale;
Expand Down Expand Up @@ -579,6 +602,9 @@ private <T> T toDate(final Class<T> type, final long value) {
* <li>{@link java.time.Instant}</li>
* </ul>
* <p>
* 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>
* <strong>N.B.</strong> No default String conversion mechanism is provided for {@link java.util.Date} and {@link java.util.Calendar} type.
*
* @param <T> The target type
Expand All @@ -590,25 +616,31 @@ private <T> T toDate(final Class<T> type, final String value) {
// java.sql.Date
if (type.equals(java.sql.Date.class)) {
try {
LocalDate.parse(value, SQL_DATE_FORMAT);
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");
} 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 {
LocalTime.parse(value, SQL_TIME_FORMAT);
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");
} 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 {
LocalDateTime.parse(value, SQL_TIMESTAMP_FORMAT);
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");
} 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ public void testDefaultStringToTypeConvert() {

// 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");
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ public void testDefaultStringToTypeConvert() {

// 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");
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ public void testDefaultStringToTypeConvert() {
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");
}

/**
Expand Down
Loading