-
Notifications
You must be signed in to change notification settings - Fork 24.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SQL: Make parsing of date more lenient #52137
Changes from 1 commit
32850d4
381741f
6e6ef0b
89bf5e8
7241b91
62f5919
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,6 +42,30 @@ public final class DateUtils { | |
.appendLiteral('T') | ||
.append(ISO_LOCAL_TIME) | ||
.toFormatter().withZone(UTC); | ||
private static final DateTimeFormatter DATE_ESCAPED_LITERAL_FORMATTER_WHITESPACE = new DateTimeFormatterBuilder() | ||
.append(ISO_LOCAL_DATE) | ||
.optionalStart() | ||
.appendLiteral(' ') | ||
.append(ISO_LOCAL_TIME) | ||
.toFormatter().withZone(UTC); | ||
private static final DateTimeFormatter DATE_ESCAPED_LITERAL_FORMATTER_T_LITERAL = new DateTimeFormatterBuilder() | ||
.append(ISO_LOCAL_DATE) | ||
.optionalStart() | ||
.appendLiteral('T') | ||
.append(ISO_LOCAL_TIME) | ||
.toFormatter().withZone(UTC); | ||
private static final DateTimeFormatter ISO_LOCAL_DATE_OPTIONAL_TIME_FORMATTER_WHITESPACE = new DateTimeFormatterBuilder() | ||
.append(DATE_ESCAPED_LITERAL_FORMATTER_WHITESPACE) | ||
.optionalStart() | ||
.appendZoneOrOffsetId() | ||
.optionalEnd() | ||
.toFormatter().withZone(UTC); | ||
private static final DateTimeFormatter ISO_LOCAL_DATE_OPTIONAL_TIME_FORMATTER_T_LITERAL = new DateTimeFormatterBuilder() | ||
.append(DATE_ESCAPED_LITERAL_FORMATTER_T_LITERAL) | ||
.optionalStart() | ||
.appendZoneOrOffsetId() | ||
.optionalEnd() | ||
.toFormatter().withZone(UTC); | ||
|
||
private static final DateFormatter UTC_DATE_TIME_FORMATTER = DateFormatter.forPattern("date_optional_time").withZone(UTC); | ||
private static final int DEFAULT_PRECISION_FOR_CURRENT_FUNCTIONS = 3; | ||
|
@@ -91,7 +115,17 @@ public static ZonedDateTime asDateTime(long millis, ZoneId id) { | |
* Parses the given string into a Date (SQL DATE type) using UTC as a default timezone. | ||
*/ | ||
public static ZonedDateTime asDateOnly(String dateFormat) { | ||
return LocalDate.parse(dateFormat, ISO_LOCAL_DATE).atStartOfDay(UTC); | ||
int separatorIdx = dateFormat.indexOf('-'); | ||
if (separatorIdx == 0) { // negative year | ||
separatorIdx = dateFormat.indexOf('-', separatorIdx + 1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since |
||
} | ||
separatorIdx = dateFormat.indexOf('-', separatorIdx + 1) + 3; | ||
// Avoid index out of bounds - it will lead to DateTimeParseException anyways | ||
if (separatorIdx >= dateFormat.length() || dateFormat.charAt(separatorIdx) == 'T') { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In which situation is the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 2 lines above we do I forgot to add a test for that, will do. |
||
return LocalDate.parse(dateFormat, ISO_LOCAL_DATE_OPTIONAL_TIME_FORMATTER_T_LITERAL).atStartOfDay(UTC); | ||
} else { | ||
return LocalDate.parse(dateFormat, ISO_LOCAL_DATE_OPTIONAL_TIME_FORMATTER_WHITESPACE).atStartOfDay(UTC); | ||
} | ||
} | ||
|
||
public static ZonedDateTime asDateOnly(ZonedDateTime zdt) { | ||
|
@@ -109,7 +143,17 @@ public static ZonedDateTime asDateTime(String dateFormat) { | |
return DateFormatters.from(UTC_DATE_TIME_FORMATTER.parse(dateFormat)).withZoneSameInstant(UTC); | ||
} | ||
|
||
public static ZonedDateTime ofEscapedLiteral(String dateFormat) { | ||
public static ZonedDateTime dateOfEscapedLiteral(String dateFormat) { | ||
int separatorIdx = dateFormat.lastIndexOf('-') + 3; | ||
// Avoid index out of bounds - it will lead to DateTimeParseException anyways | ||
if (separatorIdx >= dateFormat.length() || dateFormat.charAt(separatorIdx) == 'T') { | ||
return LocalDate.parse(dateFormat, DATE_ESCAPED_LITERAL_FORMATTER_T_LITERAL).atStartOfDay(UTC); | ||
} else { | ||
return LocalDate.parse(dateFormat, DATE_TIME_ESCAPED_LITERAL_FORMATTER_WHITESPACE).atStartOfDay(UTC); | ||
} | ||
} | ||
|
||
public static ZonedDateTime dateTimeOfEscapedLiteral(String dateFormat) { | ||
int separatorIdx = dateFormat.lastIndexOf('-') + 3; | ||
// Avoid index out of bounds - it will lead to DateTimeParseException anyways | ||
if (separatorIdx >= dateFormat.length() || dateFormat.charAt(separatorIdx) == 'T') { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -172,13 +172,19 @@ public void testConversionToDate() { | |
Converter conversion = converterFor(KEYWORD, to); | ||
assertNull(conversion.convert(null)); | ||
|
||
assertEquals(date(0L), conversion.convert("1970-01-01")); | ||
assertEquals(date(1483228800000L), conversion.convert("2017-01-01")); | ||
assertEquals(date(-1672531200000L), conversion.convert("1917-01-01")); | ||
assertEquals(date(18000000L), conversion.convert("1970-01-01")); | ||
assertEquals(date(1581292800000L), conversion.convert("2020-02-10T10:20")); | ||
assertEquals(date(-125908819200000L), conversion.convert("-2020-02-10T10:20:30.123")); | ||
assertEquals(date(1581292800000L), conversion.convert("2020-02-10T10:20:30.123456789")); | ||
|
||
// double check back and forth conversion | ||
assertEquals(date(1581292800000L), conversion.convert("2020-02-10 10:20")); | ||
assertEquals(date(-125908819200000L), conversion.convert("-2020-02-10 10:20:30.123")); | ||
assertEquals(date(1581292800000L), conversion.convert("2020-02-10 10:20:30.123456789")); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For sake of completeness, I'd suggest a test without the duration separator, but with a timezone (or a |
||
|
||
assertEquals(date(1581292800000L), conversion.convert("2020-02-10T10:20+05:00")); | ||
assertEquals(date(-125908819200000L), conversion.convert("-2020-02-10T10:20:30.123-06:00")); | ||
assertEquals(date(1581292800000L), conversion.convert("2020-02-10T10:20:30.123456789+03:00")); | ||
|
||
// double check back and forth conversion | ||
ZonedDateTime zdt = org.elasticsearch.common.time.DateUtils.nowWithMillisResolution(); | ||
Converter forward = converterFor(DATE, KEYWORD); | ||
Converter back = converterFor(KEYWORD, DATE); | ||
|
@@ -285,7 +291,6 @@ public void testConversionToDateTime() { | |
assertEquals(dateTime(18000000L), conversion.convert("1970-01-01T00:00:00-05:00")); | ||
|
||
// double check back and forth conversion | ||
|
||
ZonedDateTime dt = org.elasticsearch.common.time.DateUtils.nowWithMillisResolution(); | ||
Converter forward = converterFor(DATETIME, KEYWORD); | ||
Converter back = converterFor(KEYWORD, DATETIME); | ||
|
@@ -692,4 +697,4 @@ static ZonedDateTime date(long millisSinceEpoch) { | |
static OffsetTime time(long millisSinceEpoch) { | ||
return DateUtils.asTimeOnly(millisSinceEpoch); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the name of the formatters could be more descriptive? The fact that time can be added, but it's optional, imo should be reflected in the formatter's name. Also, not to abuse the length of the names, how about DATE_OPTIONAL_TIME_FORMATTER_WHITESPACE? (ie get rid of
ESCAPED_LITERAL
keep the FORMATTER and mention the whitespace/'T') Same idea could be applied to the rest of the formatters: `DATE_OPTIONAL_OPTIONAL_TIME_FORMATTER_T, ISO_DATE_OPTIONAL_TIME_FORMATTER_WHITESPACE, ISO_DATE_OPTIONAL_TIME_FORMATTER_T.....