This is an automated email from the ASF dual-hosted git repository.
mbudiu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite-avatica.git
The following commit(s) were added to refs/heads/main by this push:
new c0cb4b71f [CALCITE-6248] Illegal dates are accepted by casts
c0cb4b71f is described below
commit c0cb4b71f187e688e3dfd579111c3c646b9c47af
Author: Mihai Budiu <[email protected]>
AuthorDate: Sat Mar 23 18:08:29 2024 -0700
[CALCITE-6248] Illegal dates are accepted by casts
Signed-off-by: Mihai Budiu <[email protected]>
---
.../apache/calcite/avatica/util/DateTimeUtils.java | 40 ++++++++++++++++++++--
.../calcite/avatica/util/DateTimeUtilsTest.java | 22 ++++++++++--
2 files changed, 56 insertions(+), 6 deletions(-)
diff --git
a/core/src/main/java/org/apache/calcite/avatica/util/DateTimeUtils.java
b/core/src/main/java/org/apache/calcite/avatica/util/DateTimeUtils.java
index bb9f233a7..a71f3e52e 100644
--- a/core/src/main/java/org/apache/calcite/avatica/util/DateTimeUtils.java
+++ b/core/src/main/java/org/apache/calcite/avatica/util/DateTimeUtils.java
@@ -63,6 +63,10 @@ public class 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*)*$");
@@ -653,6 +657,7 @@ public class DateTimeUtils {
}
public static int dateStringToUnixDate(String s) {
+ validateLenientDate(s);
int hyphen1 = s.indexOf('-');
int y;
int m;
@@ -739,15 +744,44 @@ public class DateTimeUtils {
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);
}
diff --git
a/core/src/test/java/org/apache/calcite/avatica/util/DateTimeUtilsTest.java
b/core/src/test/java/org/apache/calcite/avatica/util/DateTimeUtilsTest.java
index 2cae4a743..b4e0203fb 100644
--- a/core/src/test/java/org/apache/calcite/avatica/util/DateTimeUtilsTest.java
+++ b/core/src/test/java/org/apache/calcite/avatica/util/DateTimeUtilsTest.java
@@ -790,12 +790,10 @@ public class DateTimeUtilsTest {
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));
@@ -1681,6 +1679,24 @@ public class DateTimeUtilsTest {
* 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"),