Skip to content

Commit

Permalink
[CALCITE-6248] Illegal dates are accepted by casts
Browse files Browse the repository at this point in the history
Signed-off-by: Mihai Budiu <[email protected]>
  • Loading branch information
mihaibudiu committed Mar 24, 2024
1 parent c12c3a3 commit 55813e8
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ private DateTimeUtils() {}
private static final Pattern ISO_DATE_PATTERN =
Pattern.compile("^(\\d{4})-([0]\\d|1[0-2])-([0-2]\\d|3[01])$");

/** Regex for lenient date patterns. */
private static final Pattern LENIENT_DATE_PATTERN =
Pattern.compile("^\\s*(\\d{1,4})-(\\d{1,2})-(\\d{1,2})\\s*$");

/** Regex for time, HH:MM:SS. */
private static final Pattern ISO_TIME_PATTERN =
Pattern.compile("^([0-2]\\d):[0-5]\\d:[0-5]\\d(\\.\\d*)*$");
Expand Down Expand Up @@ -653,6 +657,7 @@ private static void fraction(StringBuilder buf, int scale, long ms) {
}

public static int dateStringToUnixDate(String s) {
validateLenientDate(s);
int hyphen1 = s.indexOf('-');
int y;
int m;
Expand Down Expand Up @@ -739,15 +744,44 @@ private static int parseFraction(String v, int multiplier) {
return r;
}

/** Check that the combination year, month, date forms a legal date. */
static void checkLegalDate(int year, int month, int day, String full) {
if (day > daysInMonth(year, month)) {
throw fieldOutOfRange("DAY", full);
}
if (month < 1 || month > 12) {
throw fieldOutOfRange("MONTH", full);
}
if (year <= 0) {
// Year 0 is not really a legal value.
throw fieldOutOfRange("YEAR", full);
}
}

/** Lenient date validation. This accepts more date strings
* than validateDate: it does not insist on having two-digit
* values for days and months, and accepts spaces around the value.
* @param s A string representing a date.
*/
private static void validateLenientDate(String s) {
Matcher matcher = LENIENT_DATE_PATTERN.matcher(s);
if (matcher.find()) {
int year = Integer.parseInt(matcher.group(1));
int month = Integer.parseInt(matcher.group(2));
int day = Integer.parseInt(matcher.group(3));
checkLegalDate(year, month, day, s);
} else {
throw invalidType("DATE", s);
}
}

private static void validateDate(String s, String full) {
Matcher matcher = ISO_DATE_PATTERN.matcher(s);
if (matcher.find()) {
int year = Integer.parseInt(matcher.group(1));
int month = Integer.parseInt(matcher.group(2));
int day = Integer.parseInt(matcher.group(3));
if (day > daysInMonth(year, month)) {
throw fieldOutOfRange("DAY", full);
}
checkLegalDate(year, month, day, full);
} else {
throw invalidType("DATE", full);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -790,12 +790,10 @@ private void checkTimestampString(String s, int p, long d) {
checkDateString("0200-01-01", d200);
final int d100 = d200 - century + 1;
checkDateString("0100-01-01", d100);
final int d000 = d100 - century;
checkDateString("0000-01-01", d000);
}

@Test public void testDateConversion() {
for (int i = 0; i < 4000; ++i) {
for (int i = 1; i < 4000; ++i) {
for (int j = 1; j <= 12; ++j) {
String date = String.format(Locale.ENGLISH, "%04d-%02d-28", i, j);
assertThat(unixDateToString(ymdToUnixDate(i, j, 28)), is(date));
Expand Down Expand Up @@ -1681,6 +1679,24 @@ private static void assertThrows(Runnable runnable,
* Test exception is raised if date in inappropriate meaning.
*/
@Test public void testBrokenDate() {
// Test case for https://issues.apache.org/jira/browse/CALCITE-6248
// [CALCITE-6248] Illegal dates are accepted by casts
assertThrows(() -> DateTimeUtils.dateStringToUnixDate("2023-02-29"),
IllegalArgumentException.class,
is("Value of DAY field is out of range in '2023-02-29'"));

// Test case for https://issues.apache.org/jira/browse/CALCITE-6248
// [CALCITE-6248] Illegal dates are accepted by casts
assertThrows(() -> DateTimeUtils.dateStringToUnixDate("2023-13-1"),
IllegalArgumentException.class,
is("Value of MONTH field is out of range in '2023-13-1'"));

// Test case for https://issues.apache.org/jira/browse/CALCITE-6248
// [CALCITE-6248] Illegal dates are accepted by casts
assertThrows(() -> DateTimeUtils.dateStringToUnixDate("0-1-1"),
IllegalArgumentException.class,
is("Value of YEAR field is out of range in '0-1-1'"));

// 2023 is not a leap year
assertThrows(() ->
DateTimeUtils.timestampStringToUnixDate("2023-02-29 12:00:00.123"),
Expand Down

0 comments on commit 55813e8

Please sign in to comment.