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]
|
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]]
|
[[sql-functions-datetime-datetimeformat]]
|
||||||
==== `DATETIME_FORMAT`
|
==== `DATETIME_FORMAT`
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
** <<sql-functions-current-timestamp>>
|
** <<sql-functions-current-timestamp>>
|
||||||
** <<sql-functions-datetime-add>>
|
** <<sql-functions-datetime-add>>
|
||||||
** <<sql-functions-datetime-diff>>
|
** <<sql-functions-datetime-diff>>
|
||||||
|
** <<sql-functions-datetime-dateparse>>
|
||||||
** <<sql-functions-datetime-datetimeformat>>
|
** <<sql-functions-datetime-datetimeformat>>
|
||||||
** <<sql-functions-datetime-datetimeparse>>
|
** <<sql-functions-datetime-datetimeparse>>
|
||||||
** <<sql-functions-datetime-timeparse>>
|
** <<sql-functions-datetime-timeparse>>
|
||||||
|
|
|
@ -51,6 +51,7 @@ DATETIME_PARSE |SCALAR
|
||||||
DATETRUNC |SCALAR
|
DATETRUNC |SCALAR
|
||||||
DATE_ADD |SCALAR
|
DATE_ADD |SCALAR
|
||||||
DATE_DIFF |SCALAR
|
DATE_DIFF |SCALAR
|
||||||
|
DATE_PARSE |SCALAR
|
||||||
DATE_PART |SCALAR
|
DATE_PART |SCALAR
|
||||||
DATE_TRUNC |SCALAR
|
DATE_TRUNC |SCALAR
|
||||||
DAY |SCALAR
|
DAY |SCALAR
|
||||||
|
|
|
@ -164,3 +164,105 @@ SELECT MAX(salary) FROM test_emp GROUP BY TODAY();
|
||||||
---------------
|
---------------
|
||||||
74999
|
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
|
DATETRUNC |SCALAR
|
||||||
DATE_ADD |SCALAR
|
DATE_ADD |SCALAR
|
||||||
DATE_DIFF |SCALAR
|
DATE_DIFF |SCALAR
|
||||||
|
DATE_PARSE |SCALAR
|
||||||
DATE_PART |SCALAR
|
DATE_PART |SCALAR
|
||||||
DATE_TRUNC |SCALAR
|
DATE_TRUNC |SCALAR
|
||||||
DAY |SCALAR
|
DAY |SCALAR
|
||||||
|
@ -2906,6 +2907,31 @@ schema::time:time
|
||||||
// end::timeParse3
|
// 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
|
datePartDateTimeYears
|
||||||
// tag::datePartDateTimeYears
|
// tag::datePartDateTimeYears
|
||||||
SELECT DATE_PART('year', '2019-09-22T11:22:33.123Z'::datetime) AS "years";
|
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.DateAdd;
|
||||||
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateDiff;
|
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.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.DateTimeFormat;
|
||||||
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeParse;
|
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeParse;
|
||||||
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTrunc;
|
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTrunc;
|
||||||
|
@ -176,6 +177,7 @@ public class SqlFunctionRegistry extends FunctionRegistry {
|
||||||
def(DayOfYear.class, DayOfYear::new, "DAY_OF_YEAR", "DAYOFYEAR", "DOY"),
|
def(DayOfYear.class, DayOfYear::new, "DAY_OF_YEAR", "DAYOFYEAR", "DOY"),
|
||||||
def(DateAdd.class, DateAdd::new, "DATEADD", "DATE_ADD", "TIMESTAMPADD", "TIMESTAMP_ADD"),
|
def(DateAdd.class, DateAdd::new, "DATEADD", "DATE_ADD", "TIMESTAMPADD", "TIMESTAMP_ADD"),
|
||||||
def(DateDiff.class, DateDiff::new, "DATEDIFF", "DATE_DIFF", "TIMESTAMPDIFF", "TIMESTAMP_DIFF"),
|
def(DateDiff.class, DateDiff::new, "DATEDIFF", "DATE_DIFF", "TIMESTAMPDIFF", "TIMESTAMP_DIFF"),
|
||||||
|
def(DateParse.class, DateParse::new, "DATE_PARSE"),
|
||||||
def(DatePart.class, DatePart::new, "DATEPART", "DATE_PART"),
|
def(DatePart.class, DatePart::new, "DATEPART", "DATE_PART"),
|
||||||
def(DateTimeFormat.class, DateTimeFormat::new, "DATETIME_FORMAT"),
|
def(DateTimeFormat.class, DateTimeFormat::new, "DATETIME_FORMAT"),
|
||||||
def(DateTimeParse.class, DateTimeParse::new, "DATETIME_PARSE"),
|
def(DateTimeParse.class, DateTimeParse::new, "DATETIME_PARSE"),
|
||||||
|
|
|
@ -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.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
import org.elasticsearch.xpack.ql.expression.gen.processor.Processor;
|
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.SqlIllegalArgumentException;
|
||||||
|
import org.elasticsearch.xpack.sql.type.SqlDataTypes;
|
||||||
import org.elasticsearch.xpack.sql.util.DateUtils;
|
import org.elasticsearch.xpack.sql.util.DateUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.DateTimeException;
|
import java.time.DateTimeException;
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.time.OffsetTime;
|
import java.time.OffsetTime;
|
||||||
|
@ -30,15 +34,16 @@ import static org.elasticsearch.common.logging.LoggerMessageFormat.format;
|
||||||
public class DateTimeParseProcessor extends BinaryDateTimeProcessor {
|
public class DateTimeParseProcessor extends BinaryDateTimeProcessor {
|
||||||
|
|
||||||
public enum Parser {
|
public enum Parser {
|
||||||
DATE_TIME("datetime", ZonedDateTime::from, LocalDateTime::from),
|
DATE_TIME(DataTypes.DATETIME, ZonedDateTime::from, LocalDateTime::from),
|
||||||
TIME("time", OffsetTime::from, LocalTime::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 BiFunction<String, String, TemporalAccessor> parser;
|
||||||
|
|
||||||
private final String parseType;
|
private final String parseType;
|
||||||
|
|
||||||
Parser(String parseType, TemporalQuery<?>... queries) {
|
Parser(DataType parseType, TemporalQuery<?>... queries) {
|
||||||
this.parseType = parseType;
|
this.parseType = parseType.typeName();
|
||||||
this.parser = (timestampStr, pattern) -> DateTimeFormatter.ofPattern(pattern, Locale.ROOT)
|
this.parser = (timestampStr, pattern) -> DateTimeFormatter.ofPattern(pattern, Locale.ROOT)
|
||||||
.parseBest(timestampStr, queries);
|
.parseBest(timestampStr, queries);
|
||||||
}
|
}
|
||||||
|
|
|
@ -280,6 +280,10 @@ public class InternalSqlScriptUtils extends InternalQlScriptUtils {
|
||||||
return DateTruncProcessor.process(truncateTo, asDateTime(dateTimeOrInterval), ZoneId.of(tzId));
|
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) {
|
public static Integer datePart(String dateField, Object dateTime, String tzId) {
|
||||||
return (Integer) DatePartProcessor.process(dateField, asDateTime(dateTime), ZoneId.of(tzId));
|
return (Integer) DatePartProcessor.process(dateField, asDateTime(dateTime), ZoneId.of(tzId));
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,6 +178,10 @@ public final class DateUtils {
|
||||||
return nano;
|
return nano;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ZonedDateTime atTimeZone(LocalDate ld, ZoneId zoneId) {
|
||||||
|
return ld.atStartOfDay(zoneId);
|
||||||
|
}
|
||||||
|
|
||||||
public static ZonedDateTime atTimeZone(LocalDateTime ldt, ZoneId zoneId) {
|
public static ZonedDateTime atTimeZone(LocalDateTime ldt, ZoneId zoneId) {
|
||||||
return ZonedDateTime.ofInstant(ldt, zoneId.getRules().getValidOffsets(ldt).get(0), 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);
|
return atTimeZone((OffsetTime) ta, zoneId);
|
||||||
} else if (ta instanceof LocalTime) {
|
} else if (ta instanceof LocalTime) {
|
||||||
return atTimeZone((LocalTime) ta, zoneId);
|
return atTimeZone((LocalTime) ta, zoneId);
|
||||||
|
} else if (ta instanceof LocalDate) {
|
||||||
|
return atTimeZone((LocalDate) ta, zoneId);
|
||||||
} else {
|
} else {
|
||||||
return ta;
|
return ta;
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,6 +132,7 @@ class org.elasticsearch.xpack.sql.expression.function.scalar.whitelist.InternalS
|
||||||
ZonedDateTime dateAdd(String, Integer, Object, String)
|
ZonedDateTime dateAdd(String, Integer, Object, String)
|
||||||
Integer dateDiff(String, Object, Object, String)
|
Integer dateDiff(String, Object, Object, String)
|
||||||
def dateTrunc(String, Object, String)
|
def dateTrunc(String, Object, String)
|
||||||
|
def dateParse(String, String, String)
|
||||||
Integer datePart(String, Object, String)
|
Integer datePart(String, Object, String)
|
||||||
String dateTimeFormat(Object, String, String)
|
String dateTimeFormat(Object, String, String)
|
||||||
def dateTimeParse(String, String, String)
|
def dateTimeParse(String, String, String)
|
||||||
|
|
|
@ -43,6 +43,12 @@ public class DateTimeParsePipeTests extends AbstractNodeTestCase<DateTimeParsePi
|
||||||
randomStringLiteral(),
|
randomStringLiteral(),
|
||||||
randomZone()
|
randomZone()
|
||||||
).makePipe());
|
).makePipe());
|
||||||
|
functions.add(new DateParse(
|
||||||
|
randomSource(),
|
||||||
|
randomStringLiteral(),
|
||||||
|
randomStringLiteral(),
|
||||||
|
randomZone()
|
||||||
|
).makePipe());
|
||||||
return (DateTimeParsePipe) randomFrom(functions);
|
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.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.dateTime;
|
||||||
import static org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeTestUtils.time;
|
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> {
|
public class DateTimeParseProcessorTests extends AbstractSqlWireSerializingTestCase<DateTimeParseProcessor> {
|
||||||
|
|
||||||
|
@ -153,6 +154,60 @@ public class DateTimeParseProcessorTests extends AbstractSqlWireSerializingTestC
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 DateParse(Source.EMPTY, l("07/05/2020"), l("dd/MM"), randomZone()).makePipe().asProcessor().process(null)
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
"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 DateParse(Source.EMPTY, l("11:04:07"), l("HH:mm:ss"), randomZone()).makePipe().asProcessor().process(null)
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
"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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public void testWithNulls() {
|
public void testWithNulls() {
|
||||||
// DateTimeParse
|
// DateTimeParse
|
||||||
assertNull(new DateTimeParse(Source.EMPTY, randomStringLiteral(), NULL, randomZone()).makePipe().asProcessor().process(null));
|
assertNull(new DateTimeParse(Source.EMPTY, randomStringLiteral(), NULL, randomZone()).makePipe().asProcessor().process(null));
|
||||||
|
@ -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, randomStringLiteral(), l(""), randomZone()).makePipe().asProcessor().process(null));
|
||||||
assertNull(new TimeParse(Source.EMPTY, NULL, randomStringLiteral(), 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));
|
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() {
|
public void testParsing() {
|
||||||
|
@ -203,6 +263,19 @@ public class DateTimeParseProcessorTests extends AbstractSqlWireSerializingTestC
|
||||||
.asProcessor()
|
.asProcessor()
|
||||||
.process(null)
|
.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(
|
assertEquals(
|
||||||
time(10, 20, 30, 123456789, ZoneOffset.of("+05:30"), zoneId),
|
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()
|
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.Clock;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.time.OffsetTime;
|
import java.time.OffsetTime;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
|
@ -64,6 +65,10 @@ public class DateTimeTestUtils {
|
||||||
return OffsetTime.of(lt, zoneId.getRules().getValidOffsets(ldt).get(0));
|
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() {
|
static ZonedDateTime nowWithMillisResolution() {
|
||||||
Clock millisResolutionClock = Clock.tick(Clock.systemUTC(), Duration.ofMillis(1));
|
Clock millisResolutionClock = Clock.tick(Clock.systemUTC(), Duration.ofMillis(1));
|
||||||
return ZonedDateTime.now(millisResolutionClock);
|
return ZonedDateTime.now(millisResolutionClock);
|
||||||
|
|
Loading…
Reference in New Issue