Implement DATE_PARSE(<date_str>, <pattern_str>) function which allows to parse a date string according to the specified pattern into a date object. The patterns allowed are those of java.time.format.DateTimeFormatter. Closes #54962 Co-authored-by: Marios Trivyzas <matriv@users.noreply.github.com> Co-authored-by: Patrick Jiang(白泽) <dreamlike.sky@foxmail.com> (cherry picked from commit 647a413d9b21bd3938f1716bb19f8407e1334125)
This commit is contained in:
parent
305b46c7cd
commit
c7efbc1b83
|
@ -404,6 +404,50 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[dateDiffDateTimeMinutes]
|
|||
include-tagged::{sql-specs}/docs/docs.csv-spec[dateDiffDateMinutes]
|
||||
--------------------------------------------------
|
||||
|
||||
[[sql-functions-datetime-dateparse]]
|
||||
==== `DATE_PARSE`
|
||||
|
||||
.Synopsis:
|
||||
[source, sql]
|
||||
--------------------------------------------------
|
||||
DATE_PARSE(
|
||||
string_exp, <1>
|
||||
string_exp) <2>
|
||||
--------------------------------------------------
|
||||
|
||||
*Input*:
|
||||
|
||||
<1> date expression as a string
|
||||
<2> parsing pattern
|
||||
|
||||
*Output*: date
|
||||
|
||||
*Description*: Returns a date by parsing the 1st argument using the format specified in the 2nd argument. The parsing
|
||||
format pattern used is the one from
|
||||
https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/time/format/DateTimeFormatter.html[`java.time.format.DateTimeFormatter`].
|
||||
If any of the two arguments is `null` or an empty string, then `null` is returned.
|
||||
|
||||
[NOTE]
|
||||
If the parsing pattern does not contain all valid date units (e.g. 'HH:mm:ss', 'dd-MM HH:mm:ss', etc.) an error is returned
|
||||
as the function needs to return a value of `date` type which will contain date part.
|
||||
|
||||
[source, sql]
|
||||
--------------------------------------------------
|
||||
include-tagged::{sql-specs}/docs/docs.csv-spec[dateParse1]
|
||||
--------------------------------------------------
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
The resulting `date` will have the time zone specified by the user through the
|
||||
<<sql-rest-fields-timezone,`time_zone`>>/<<jdbc-cfg-timezone,`timezone`>> REST/driver parameters
|
||||
with no conversion applied.
|
||||
|
||||
[source, sql]
|
||||
--------------------------------------------------
|
||||
include-tagged::{sql-specs}/docs/docs.csv-spec[dateParse2]
|
||||
--------------------------------------------------
|
||||
====
|
||||
|
||||
[[sql-functions-datetime-datetimeformat]]
|
||||
==== `DATETIME_FORMAT`
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
** <<sql-functions-current-timestamp>>
|
||||
** <<sql-functions-datetime-add>>
|
||||
** <<sql-functions-datetime-diff>>
|
||||
** <<sql-functions-datetime-dateparse>>
|
||||
** <<sql-functions-datetime-datetimeformat>>
|
||||
** <<sql-functions-datetime-datetimeparse>>
|
||||
** <<sql-functions-datetime-timeparse>>
|
||||
|
|
|
@ -51,6 +51,7 @@ DATETIME_PARSE |SCALAR
|
|||
DATETRUNC |SCALAR
|
||||
DATE_ADD |SCALAR
|
||||
DATE_DIFF |SCALAR
|
||||
DATE_PARSE |SCALAR
|
||||
DATE_PART |SCALAR
|
||||
DATE_TRUNC |SCALAR
|
||||
DAY |SCALAR
|
||||
|
|
|
@ -164,3 +164,105 @@ SELECT MAX(salary) FROM test_emp GROUP BY TODAY();
|
|||
---------------
|
||||
74999
|
||||
;
|
||||
|
||||
selectDateParse
|
||||
schema::date1:date
|
||||
SELECT DATE_PARSE('07/04/2020', 'dd/MM/uuuu') AS date1;
|
||||
|
||||
date1
|
||||
------------
|
||||
2020-04-07
|
||||
;
|
||||
|
||||
|
||||
selectDateParseWithField
|
||||
schema::birth_date:ts|dp_birth_date:date
|
||||
SELECT birth_date, DATE_PARSE(DATETIME_FORMAT(birth_date, 'MM/dd/ HH uuuu'), concat(gender, 'M/dd/ HH uuuu')) AS dp_birth_date
|
||||
FROM test_emp WHERE gender = 'M' AND emp_no BETWEEN 10037 AND 10052 ORDER BY emp_no;
|
||||
|
||||
birth_date | dp_birth_date
|
||||
-------------------------+----------------
|
||||
1963-07-22 00:00:00.000Z | 1963-07-22
|
||||
1960-07-20 00:00:00.000Z | 1960-07-20
|
||||
1959-10-01 00:00:00.000Z | 1959-10-01
|
||||
null | null
|
||||
null | null
|
||||
null | null
|
||||
null | null
|
||||
null | null
|
||||
1958-05-21 00:00:00.000Z | 1958-05-21
|
||||
1953-07-28 00:00:00.000Z | 1953-07-28
|
||||
1961-02-26 00:00:00.000Z | 1961-02-26
|
||||
;
|
||||
|
||||
dateParseWhere
|
||||
schema::birth_date:ts|dp_birth_date:date
|
||||
SELECT birth_date, DATE_PARSE(DATETIME_FORMAT(birth_date, 'MM_dd_uuuu'), 'MM_dd_uuuu') AS dp_birth_date
|
||||
FROM test_emp WHERE dp_birth_date > '1963-10-20'::date ORDER BY emp_no;
|
||||
|
||||
birth_date | dp_birth_date
|
||||
-------------------------+----------------
|
||||
1964-06-02 00:00:00.000Z | 1964-06-02
|
||||
1963-11-26 00:00:00.000Z | 1963-11-26
|
||||
1964-04-18 00:00:00.000Z | 1964-04-18
|
||||
1964-10-18 00:00:00.000Z | 1964-10-18
|
||||
1964-06-11 00:00:00.000Z | 1964-06-11
|
||||
1965-01-03 00:00:00.000Z | 1965-01-03
|
||||
;
|
||||
|
||||
dateParseOrderBy
|
||||
schema::birth_date:ts|dp_birth_date:date
|
||||
SELECT birth_date, DATE_PARSE(DATETIME_FORMAT(birth_date, 'MM/dd/uuuu'), 'MM/dd/uuuu') AS dp_birth_date
|
||||
FROM test_emp ORDER BY 2 DESC NULLS LAST LIMIT 10;
|
||||
|
||||
birth_date | dp_birth_date
|
||||
-------------------------+---------------
|
||||
1965-01-03 00:00:00.000Z | 1965-01-03
|
||||
1964-10-18 00:00:00.000Z | 1964-10-18
|
||||
1964-06-11 00:00:00.000Z | 1964-06-11
|
||||
1964-06-02 00:00:00.000Z | 1964-06-02
|
||||
1964-04-18 00:00:00.000Z | 1964-04-18
|
||||
1963-11-26 00:00:00.000Z | 1963-11-26
|
||||
1963-09-09 00:00:00.000Z | 1963-09-09
|
||||
1963-07-22 00:00:00.000Z | 1963-07-22
|
||||
1963-06-07 00:00:00.000Z | 1963-06-07
|
||||
1963-06-01 00:00:00.000Z | 1963-06-01
|
||||
;
|
||||
|
||||
dateParseGroupBy
|
||||
schema::count:l|df_birth_date:s
|
||||
SELECT count(*) AS count, DATETIME_FORMAT(DATE_PARSE(DATETIME_FORMAT(birth_date, 'dd/MM/uuuu'), 'dd/MM/uuuu'), 'MM') AS df_birth_date
|
||||
FROM test_emp GROUP BY df_birth_date ORDER BY 1 DESC, 2 DESC NULLS LAST LIMIT 10;
|
||||
|
||||
count | df_birth_date
|
||||
-------+---------------
|
||||
10 | 09
|
||||
10 | 05
|
||||
10 | null
|
||||
9 | 10
|
||||
9 | 07
|
||||
8 | 11
|
||||
8 | 04
|
||||
8 | 02
|
||||
7 | 12
|
||||
7 | 06
|
||||
;
|
||||
|
||||
dateParseHaving
|
||||
schema::max:ts|df_birth_date:s
|
||||
SELECT MAX(birth_date) AS max, DATETIME_FORMAT(birth_date, 'MM') AS df_birth_date FROM test_emp GROUP BY df_birth_date
|
||||
HAVING DATE_PARSE(DATETIME_FORMAT(MAX(birth_date), 'dd/MM/uuuu'), 'dd/MM/uuuu') > '1961-10-20'::date ORDER BY 1 DESC NULLS LAST;
|
||||
|
||||
max | df_birth_date
|
||||
-------------------------+---------------
|
||||
1965-01-03 00:00:00.000Z | 01
|
||||
1964-10-18 00:00:00.000Z | 10
|
||||
1964-06-11 00:00:00.000Z | 06
|
||||
1964-04-18 00:00:00.000Z | 04
|
||||
1963-11-26 00:00:00.000Z | 11
|
||||
1963-09-09 00:00:00.000Z | 09
|
||||
1963-07-22 00:00:00.000Z | 07
|
||||
1963-03-21 00:00:00.000Z | 03
|
||||
1962-12-29 00:00:00.000Z | 12
|
||||
null | null
|
||||
;
|
|
@ -247,6 +247,7 @@ DATETIME_PARSE |SCALAR
|
|||
DATETRUNC |SCALAR
|
||||
DATE_ADD |SCALAR
|
||||
DATE_DIFF |SCALAR
|
||||
DATE_PARSE |SCALAR
|
||||
DATE_PART |SCALAR
|
||||
DATE_TRUNC |SCALAR
|
||||
DAY |SCALAR
|
||||
|
@ -2906,6 +2907,31 @@ schema::time:time
|
|||
// end::timeParse3
|
||||
;
|
||||
|
||||
dateParse1
|
||||
schema::date:date
|
||||
// tag::dateParse1
|
||||
SELECT DATE_PARSE('07/04/2020', 'dd/MM/uuuu') AS "date";
|
||||
|
||||
date
|
||||
-----------
|
||||
2020-04-07
|
||||
// end::dateParse1
|
||||
;
|
||||
|
||||
dateParse2-Ignore
|
||||
schema::date:date
|
||||
// tag::dateParse2
|
||||
{
|
||||
"query" : "SELECT DATE_PARSE('07/04/2020', 'dd/MM/uuuu') AS \"date\"",
|
||||
"time_zone" : "Europe/Athens"
|
||||
}
|
||||
|
||||
date
|
||||
------------
|
||||
2020-04-07T00:00:00.000+03:00
|
||||
// end::dateParse2
|
||||
;
|
||||
|
||||
datePartDateTimeYears
|
||||
// tag::datePartDateTimeYears
|
||||
SELECT DATE_PART('year', '2019-09-22T11:22:33.123Z'::datetime) AS "years";
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.CurrentTi
|
|||
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateAdd;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateDiff;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DatePart;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateParse;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeFormat;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeParse;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTrunc;
|
||||
|
@ -176,7 +177,8 @@ public class SqlFunctionRegistry extends FunctionRegistry {
|
|||
def(DayOfYear.class, DayOfYear::new, "DAY_OF_YEAR", "DAYOFYEAR", "DOY"),
|
||||
def(DateAdd.class, DateAdd::new, "DATEADD", "DATE_ADD", "TIMESTAMPADD", "TIMESTAMP_ADD"),
|
||||
def(DateDiff.class, DateDiff::new, "DATEDIFF", "DATE_DIFF", "TIMESTAMPDIFF", "TIMESTAMP_DIFF"),
|
||||
def(DatePart.class, DatePart::new, "DATEPART", "DATE_PART"),
|
||||
def(DateParse.class, DateParse::new, "DATE_PARSE"),
|
||||
def(DatePart.class, DatePart::new, "DATEPART", "DATE_PART"),
|
||||
def(DateTimeFormat.class, DateTimeFormat::new, "DATETIME_FORMAT"),
|
||||
def(DateTimeParse.class, DateTimeParse::new, "DATETIME_PARSE"),
|
||||
def(DateTrunc.class, DateTrunc::new, "DATETRUNC", "DATE_TRUNC"),
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.sql.expression.function.scalar.datetime;
|
||||
|
||||
import org.elasticsearch.xpack.ql.expression.Expression;
|
||||
import org.elasticsearch.xpack.ql.expression.function.scalar.BinaryScalarFunction;
|
||||
import org.elasticsearch.xpack.ql.tree.NodeInfo;
|
||||
import org.elasticsearch.xpack.ql.tree.Source;
|
||||
import org.elasticsearch.xpack.ql.type.DataType;
|
||||
import org.elasticsearch.xpack.sql.type.SqlDataTypes;
|
||||
|
||||
import java.time.ZoneId;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeParseProcessor.Parser.DATE;
|
||||
|
||||
public class DateParse extends BaseDateTimeParseFunction {
|
||||
|
||||
public DateParse(Source source, Expression datePart, Expression timestamp, ZoneId zoneId) {
|
||||
super(source, datePart, timestamp, zoneId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DateTimeParseProcessor.Parser parser() {
|
||||
return DATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NodeInfo.NodeCtor3<Expression, Expression, ZoneId, BaseDateTimeParseFunction> ctorForInfo() {
|
||||
return DateParse::new;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BinaryScalarFunction replaceChildren(Expression timestamp, Expression pattern) {
|
||||
return new DateParse(source(), timestamp, pattern, zoneId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType dataType() {
|
||||
return SqlDataTypes.DATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String scriptMethodName() {
|
||||
return "dateParse";
|
||||
}
|
||||
}
|
|
@ -8,11 +8,15 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.datetime;
|
|||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.xpack.ql.expression.gen.processor.Processor;
|
||||
import org.elasticsearch.xpack.ql.type.DataType;
|
||||
import org.elasticsearch.xpack.ql.type.DataTypes;
|
||||
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
|
||||
import org.elasticsearch.xpack.sql.type.SqlDataTypes;
|
||||
import org.elasticsearch.xpack.sql.util.DateUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.DateTimeException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.OffsetTime;
|
||||
|
@ -30,15 +34,16 @@ import static org.elasticsearch.common.logging.LoggerMessageFormat.format;
|
|||
public class DateTimeParseProcessor extends BinaryDateTimeProcessor {
|
||||
|
||||
public enum Parser {
|
||||
DATE_TIME("datetime", ZonedDateTime::from, LocalDateTime::from),
|
||||
TIME("time", OffsetTime::from, LocalTime::from);
|
||||
DATE_TIME(DataTypes.DATETIME, ZonedDateTime::from, LocalDateTime::from),
|
||||
TIME(SqlDataTypes.TIME, OffsetTime::from, LocalTime::from),
|
||||
DATE(SqlDataTypes.DATE, LocalDate::from, (TemporalAccessor ta) -> {throw new DateTimeException("InvalidDate");});
|
||||
|
||||
private final BiFunction<String, String, TemporalAccessor> parser;
|
||||
|
||||
private final String parseType;
|
||||
|
||||
Parser(String parseType, TemporalQuery<?>... queries) {
|
||||
this.parseType = parseType;
|
||||
Parser(DataType parseType, TemporalQuery<?>... queries) {
|
||||
this.parseType = parseType.typeName();
|
||||
this.parser = (timestampStr, pattern) -> DateTimeFormatter.ofPattern(pattern, Locale.ROOT)
|
||||
.parseBest(timestampStr, queries);
|
||||
}
|
||||
|
|
|
@ -280,6 +280,10 @@ public class InternalSqlScriptUtils extends InternalQlScriptUtils {
|
|||
return DateTruncProcessor.process(truncateTo, asDateTime(dateTimeOrInterval), ZoneId.of(tzId));
|
||||
}
|
||||
|
||||
public static Object dateParse(String dateField, String pattern, String tzId) {
|
||||
return Parser.DATE.parse(dateField, pattern, ZoneId.of(tzId));
|
||||
}
|
||||
|
||||
public static Integer datePart(String dateField, Object dateTime, String tzId) {
|
||||
return (Integer) DatePartProcessor.process(dateField, asDateTime(dateTime), ZoneId.of(tzId));
|
||||
}
|
||||
|
|
|
@ -177,7 +177,11 @@ public final class DateUtils {
|
|||
nano = nano - nano % (int) Math.pow(10, (9 - precision));
|
||||
return nano;
|
||||
}
|
||||
|
||||
|
||||
public static ZonedDateTime atTimeZone(LocalDate ld, ZoneId zoneId) {
|
||||
return ld.atStartOfDay(zoneId);
|
||||
}
|
||||
|
||||
public static ZonedDateTime atTimeZone(LocalDateTime ldt, ZoneId zoneId) {
|
||||
return ZonedDateTime.ofInstant(ldt, zoneId.getRules().getValidOffsets(ldt).get(0), zoneId);
|
||||
}
|
||||
|
@ -205,6 +209,8 @@ public final class DateUtils {
|
|||
return atTimeZone((OffsetTime) ta, zoneId);
|
||||
} else if (ta instanceof LocalTime) {
|
||||
return atTimeZone((LocalTime) ta, zoneId);
|
||||
} else if (ta instanceof LocalDate) {
|
||||
return atTimeZone((LocalDate) ta, zoneId);
|
||||
} else {
|
||||
return ta;
|
||||
}
|
||||
|
|
|
@ -132,6 +132,7 @@ class org.elasticsearch.xpack.sql.expression.function.scalar.whitelist.InternalS
|
|||
ZonedDateTime dateAdd(String, Integer, Object, String)
|
||||
Integer dateDiff(String, Object, Object, String)
|
||||
def dateTrunc(String, Object, String)
|
||||
def dateParse(String, String, String)
|
||||
Integer datePart(String, Object, String)
|
||||
String dateTimeFormat(Object, String, String)
|
||||
def dateTimeParse(String, String, String)
|
||||
|
|
|
@ -43,6 +43,12 @@ public class DateTimeParsePipeTests extends AbstractNodeTestCase<DateTimeParsePi
|
|||
randomStringLiteral(),
|
||||
randomZone()
|
||||
).makePipe());
|
||||
functions.add(new DateParse(
|
||||
randomSource(),
|
||||
randomStringLiteral(),
|
||||
randomStringLiteral(),
|
||||
randomZone()
|
||||
).makePipe());
|
||||
return (DateTimeParsePipe) randomFrom(functions);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import static org.elasticsearch.xpack.ql.expression.function.scalar.FunctionTest
|
|||
import static org.elasticsearch.xpack.ql.expression.function.scalar.FunctionTestUtils.randomStringLiteral;
|
||||
import static org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeTestUtils.dateTime;
|
||||
import static org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeTestUtils.time;
|
||||
import static org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeTestUtils.date;
|
||||
|
||||
public class DateTimeParseProcessorTests extends AbstractSqlWireSerializingTestCase<DateTimeParseProcessor> {
|
||||
|
||||
|
@ -87,7 +88,7 @@ public class DateTimeParseProcessorTests extends AbstractSqlWireSerializingTestC
|
|||
() -> new DateTimeParse(Source.EMPTY, l("2020-04-07"), l("MM/dd"), randomZone()).makePipe().asProcessor().process(null)
|
||||
);
|
||||
assertEquals(
|
||||
"Invalid datetime string [2020-04-07] or pattern [MM/dd] is received; Text '2020-04-07' could not be parsed at index 2",
|
||||
"Invalid datetime string [2020-04-07] or pattern [MM/dd] is received; Text '2020-04-07' could not be parsed at index 2",
|
||||
siae.getMessage()
|
||||
);
|
||||
|
||||
|
@ -113,43 +114,97 @@ public class DateTimeParseProcessorTests extends AbstractSqlWireSerializingTestC
|
|||
|
||||
public void testTimeInvalidInputs() {
|
||||
SqlIllegalArgumentException siae = expectThrows(
|
||||
SqlIllegalArgumentException.class,
|
||||
() -> new TimeParse(Source.EMPTY, l(10), randomStringLiteral(), randomZone()).makePipe().asProcessor().process(null)
|
||||
SqlIllegalArgumentException.class,
|
||||
() -> new TimeParse(Source.EMPTY, l(10), randomStringLiteral(), randomZone()).makePipe().asProcessor().process(null)
|
||||
);
|
||||
assertEquals("A string is required; received [10]", siae.getMessage());
|
||||
|
||||
siae = expectThrows(
|
||||
SqlIllegalArgumentException.class,
|
||||
() -> new TimeParse(Source.EMPTY, randomStringLiteral(), l(20), randomZone()).makePipe().asProcessor().process(null)
|
||||
SqlIllegalArgumentException.class,
|
||||
() -> new TimeParse(Source.EMPTY, randomStringLiteral(), l(20), randomZone()).makePipe().asProcessor().process(null)
|
||||
);
|
||||
assertEquals("A string is required; received [20]", siae.getMessage());
|
||||
|
||||
siae = expectThrows(
|
||||
SqlIllegalArgumentException.class,
|
||||
() -> new TimeParse(Source.EMPTY, l("11:04:07"), l("invalid"), randomZone()).makePipe().asProcessor().process(null)
|
||||
SqlIllegalArgumentException.class,
|
||||
() -> new TimeParse(Source.EMPTY, l("11:04:07"), l("invalid"), randomZone()).makePipe().asProcessor().process(null)
|
||||
);
|
||||
assertEquals(
|
||||
"Invalid time string [11:04:07] or pattern [invalid] is received; Unknown pattern letter: i",
|
||||
"Invalid time string [11:04:07] or pattern [invalid] is received; Unknown pattern letter: i",
|
||||
siae.getMessage()
|
||||
);
|
||||
|
||||
siae = expectThrows(
|
||||
SqlIllegalArgumentException.class,
|
||||
() -> new TimeParse(Source.EMPTY, l("11:04:07"), l("HH:mm"), randomZone()).makePipe().asProcessor().process(null)
|
||||
);
|
||||
assertEquals(
|
||||
"Invalid time string [11:04:07] or pattern [HH:mm] is received; " +
|
||||
"Text '11:04:07' could not be parsed, unparsed text found at index 5",
|
||||
siae.getMessage()
|
||||
);
|
||||
|
||||
siae = expectThrows(
|
||||
SqlIllegalArgumentException.class,
|
||||
() -> new TimeParse(Source.EMPTY, l("07/05/2020"), l("dd/MM/uuuu"), randomZone()).makePipe().asProcessor().process(null)
|
||||
);
|
||||
assertEquals(
|
||||
"Invalid time string [07/05/2020] or pattern [dd/MM/uuuu] is received; Unable to convert parsed text into [time]",
|
||||
siae.getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
public void testDateInvalidInputs() {
|
||||
SqlIllegalArgumentException siae = expectThrows(
|
||||
SqlIllegalArgumentException.class,
|
||||
() -> new DateParse(Source.EMPTY, l(10), randomStringLiteral(), randomZone()).makePipe().asProcessor().process(null)
|
||||
);
|
||||
assertEquals("A string is required; received [10]", siae.getMessage());
|
||||
|
||||
siae = expectThrows(
|
||||
SqlIllegalArgumentException.class,
|
||||
() -> new DateParse(Source.EMPTY, randomStringLiteral(), l(20), randomZone()).makePipe().asProcessor().process(null)
|
||||
);
|
||||
assertEquals("A string is required; received [20]", siae.getMessage());
|
||||
|
||||
siae = expectThrows(
|
||||
SqlIllegalArgumentException.class,
|
||||
() -> new DateParse(Source.EMPTY, l("07/05/2020"), l("invalid"), randomZone()).makePipe().asProcessor().process(null)
|
||||
);
|
||||
assertEquals(
|
||||
"Invalid date string [07/05/2020] or pattern [invalid] is received; Unknown pattern letter: i",
|
||||
siae.getMessage()
|
||||
);
|
||||
|
||||
siae = expectThrows(
|
||||
SqlIllegalArgumentException.class,
|
||||
() -> new TimeParse(Source.EMPTY, l("11:04:07"), l("HH:mm"), randomZone()).makePipe().asProcessor().process(null)
|
||||
SqlIllegalArgumentException.class,
|
||||
() -> new DateParse(Source.EMPTY, l("07/05/2020"), l("dd/MM"), randomZone()).makePipe().asProcessor().process(null)
|
||||
);
|
||||
assertEquals(
|
||||
"Invalid time string [11:04:07] or pattern [HH:mm] is received; " +
|
||||
"Text '11:04:07' could not be parsed, unparsed text found at index 5",
|
||||
siae.getMessage()
|
||||
"Invalid date string [07/05/2020] or pattern [dd/MM] is received; " +
|
||||
"Text '07/05/2020' could not be parsed, unparsed text found at index 5",
|
||||
siae.getMessage()
|
||||
);
|
||||
|
||||
|
||||
siae = expectThrows(
|
||||
SqlIllegalArgumentException.class,
|
||||
() -> new TimeParse(Source.EMPTY, l("07/05/2020"), l("dd/MM/uuuu"), randomZone()).makePipe().asProcessor().process(null)
|
||||
SqlIllegalArgumentException.class,
|
||||
() -> new DateParse(Source.EMPTY, l("11:04:07"), l("HH:mm:ss"), randomZone()).makePipe().asProcessor().process(null)
|
||||
);
|
||||
assertEquals(
|
||||
"Invalid time string [07/05/2020] or pattern [dd/MM/uuuu] is received; Unable to convert parsed text into [time]",
|
||||
siae.getMessage()
|
||||
"Invalid date string [11:04:07] or pattern [HH:mm:ss] is received; Unable to convert parsed text into [date]",
|
||||
siae.getMessage()
|
||||
);
|
||||
|
||||
siae = expectThrows(
|
||||
SqlIllegalArgumentException.class,
|
||||
() -> new DateParse(Source.EMPTY, l("05/2020 11:04:07"), l("MM/uuuu HH:mm:ss"), randomZone())
|
||||
.makePipe()
|
||||
.asProcessor()
|
||||
.process(null)
|
||||
);
|
||||
assertEquals(
|
||||
"Invalid date string [05/2020 11:04:07] or pattern [MM/uuuu HH:mm:ss] is received; Unable to convert parsed text into [date]",
|
||||
siae.getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -164,6 +219,11 @@ public class DateTimeParseProcessorTests extends AbstractSqlWireSerializingTestC
|
|||
assertNull(new TimeParse(Source.EMPTY, randomStringLiteral(), l(""), randomZone()).makePipe().asProcessor().process(null));
|
||||
assertNull(new TimeParse(Source.EMPTY, NULL, randomStringLiteral(), randomZone()).makePipe().asProcessor().process(null));
|
||||
assertNull(new TimeParse(Source.EMPTY, l(""), randomStringLiteral(), randomZone()).makePipe().asProcessor().process(null));
|
||||
// DateParse
|
||||
assertNull(new DateParse(Source.EMPTY, randomStringLiteral(), NULL, randomZone()).makePipe().asProcessor().process(null));
|
||||
assertNull(new DateParse(Source.EMPTY, randomStringLiteral(), l(""), randomZone()).makePipe().asProcessor().process(null));
|
||||
assertNull(new DateParse(Source.EMPTY, NULL, randomStringLiteral(), randomZone()).makePipe().asProcessor().process(null));
|
||||
assertNull(new DateParse(Source.EMPTY, l(""), randomStringLiteral(), randomZone()).makePipe().asProcessor().process(null));
|
||||
}
|
||||
|
||||
public void testParsing() {
|
||||
|
@ -203,6 +263,19 @@ public class DateTimeParseProcessorTests extends AbstractSqlWireSerializingTestC
|
|||
.asProcessor()
|
||||
.process(null)
|
||||
);
|
||||
// DateParse
|
||||
assertEquals(
|
||||
date(2020, 4, 7, zoneId),
|
||||
new DateParse(Source.EMPTY, l("07/04/2020"), l("dd/MM/uuuu"), zoneId).makePipe()
|
||||
.asProcessor()
|
||||
.process(null)
|
||||
);
|
||||
assertEquals(
|
||||
date(2020, 4, 7, zoneId),
|
||||
new DateParse(Source.EMPTY, l("07/04/2020 12:12:00"), l("dd/MM/uuuu HH:mm:ss"), zoneId).makePipe()
|
||||
.asProcessor()
|
||||
.process(null)
|
||||
);
|
||||
assertEquals(
|
||||
time(10, 20, 30, 123456789, ZoneOffset.of("+05:30"), zoneId),
|
||||
new TimeParse(Source.EMPTY, l("16/06/2020 10:20:30.123456789 +0530"), l("dd/MM/uuuu HH:mm:ss.SSSSSSSSS xx"), zoneId).makePipe()
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.elasticsearch.xpack.sql.util.DateUtils;
|
|||
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDate;
|
||||
import java.time.OffsetTime;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
|
@ -64,6 +65,10 @@ public class DateTimeTestUtils {
|
|||
return OffsetTime.of(lt, zoneId.getRules().getValidOffsets(ldt).get(0));
|
||||
}
|
||||
|
||||
public static ZonedDateTime date(int year, int month, int day, ZoneId zoneId) {
|
||||
return LocalDate.of(year, month, day).atStartOfDay(zoneId);
|
||||
}
|
||||
|
||||
static ZonedDateTime nowWithMillisResolution() {
|
||||
Clock millisResolutionClock = Clock.tick(Clock.systemUTC(), Duration.ofMillis(1));
|
||||
return ZonedDateTime.now(millisResolutionClock);
|
||||
|
|
Loading…
Reference in New Issue