diff --git a/core/src/main/java/org/opensearch/sql/data/model/ExprDateValue.java b/core/src/main/java/org/opensearch/sql/data/model/ExprDateValue.java index 09b2e56b44..7617e156ba 100644 --- a/core/src/main/java/org/opensearch/sql/data/model/ExprDateValue.java +++ b/core/src/main/java/org/opensearch/sql/data/model/ExprDateValue.java @@ -6,6 +6,8 @@ package org.opensearch.sql.data.model; +import static org.opensearch.sql.utils.DateTimeFormatters.DATE_TIME_FORMATTER_VARIABLE_NANOS_OPTIONAL; + import com.google.common.base.Objects; import java.time.Instant; import java.time.LocalDate; @@ -33,7 +35,7 @@ public class ExprDateValue extends AbstractExprValue { */ public ExprDateValue(String date) { try { - this.date = LocalDate.parse(date); + this.date = LocalDate.parse(date, DATE_TIME_FORMATTER_VARIABLE_NANOS_OPTIONAL); } catch (DateTimeParseException e) { throw new SemanticCheckException(String.format("date:%s in unsupported format, please use " + "yyyy-MM-dd", date)); diff --git a/core/src/main/java/org/opensearch/sql/data/model/ExprTimeValue.java b/core/src/main/java/org/opensearch/sql/data/model/ExprTimeValue.java index 9c5f5c5d55..6cc4021d2e 100644 --- a/core/src/main/java/org/opensearch/sql/data/model/ExprTimeValue.java +++ b/core/src/main/java/org/opensearch/sql/data/model/ExprTimeValue.java @@ -6,10 +6,15 @@ package org.opensearch.sql.data.model; -import static org.opensearch.sql.utils.DateTimeFormatters.TIME_FORMATTER_VARIABLE_NANOS; +import static java.time.format.DateTimeFormatter.ISO_LOCAL_TIME; +import static org.opensearch.sql.utils.DateTimeFormatters.DATE_TIME_FORMATTER_VARIABLE_NANOS_OPTIONAL; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.LocalTime; -import java.time.format.DateTimeFormatter; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.time.format.DateTimeParseException; import java.util.Objects; import lombok.RequiredArgsConstructor; @@ -22,14 +27,15 @@ */ @RequiredArgsConstructor public class ExprTimeValue extends AbstractExprValue { + private final LocalTime time; /** - * Constructor. + * Constructor of ExprTimeValue. */ public ExprTimeValue(String time) { try { - this.time = LocalTime.parse(time, TIME_FORMATTER_VARIABLE_NANOS); + this.time = LocalTime.parse(time, DATE_TIME_FORMATTER_VARIABLE_NANOS_OPTIONAL); } catch (DateTimeParseException e) { throw new SemanticCheckException(String.format("time:%s in unsupported format, please use " + "HH:mm:ss[.SSSSSSSSS]", time)); @@ -38,7 +44,7 @@ public ExprTimeValue(String time) { @Override public String value() { - return DateTimeFormatter.ISO_LOCAL_TIME.format(time); + return ISO_LOCAL_TIME.format(time); } @Override diff --git a/core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunction.java b/core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunction.java index 84716a425a..f0cf2b98a7 100644 --- a/core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunction.java @@ -55,6 +55,7 @@ import org.opensearch.sql.data.model.ExprValue; import org.opensearch.sql.data.type.ExprCoreType; import org.opensearch.sql.exception.ExpressionEvaluationException; +import org.opensearch.sql.exception.SemanticCheckException; import org.opensearch.sql.expression.function.BuiltinFunctionName; import org.opensearch.sql.expression.function.BuiltinFunctionRepository; import org.opensearch.sql.expression.function.DefaultFunctionResolver; diff --git a/core/src/main/java/org/opensearch/sql/utils/DateTimeFormatters.java b/core/src/main/java/org/opensearch/sql/utils/DateTimeFormatters.java index 2efdbb3755..2556aed8d8 100644 --- a/core/src/main/java/org/opensearch/sql/utils/DateTimeFormatters.java +++ b/core/src/main/java/org/opensearch/sql/utils/DateTimeFormatters.java @@ -113,23 +113,25 @@ public class DateTimeFormatters { public static final DateTimeFormatter DATE_TIME_FORMATTER_VARIABLE_NANOS = new DateTimeFormatterBuilder() - .appendPattern("yyyy-MM-dd HH:mm:ss") + .appendPattern("uuuu-MM-dd HH:mm:ss") .appendFraction( ChronoField.NANO_OF_SECOND, MIN_FRACTION_SECONDS, MAX_FRACTION_SECONDS, true) - .toFormatter(Locale.ROOT); + .toFormatter(Locale.ROOT) + .withResolverStyle(ResolverStyle.STRICT); - public static final DateTimeFormatter TIME_FORMATTER_VARIABLE_NANOS = + public static final DateTimeFormatter DATE_TIME_FORMATTER_VARIABLE_NANOS_OPTIONAL = new DateTimeFormatterBuilder() - .appendPattern("HH:mm:ss") + .appendPattern("[uuuu-MM-dd HH:mm:ss][uuuu-MM-dd HH:mm][HH:mm:ss][HH:mm][uuuu-MM-dd]") .appendFraction( ChronoField.NANO_OF_SECOND, MIN_FRACTION_SECONDS, MAX_FRACTION_SECONDS, true) - .toFormatter(); + .toFormatter(Locale.ROOT) + .withResolverStyle(ResolverStyle.STRICT); // YYMMDD public static final DateTimeFormatter DATE_FORMATTER_SHORT_YEAR = diff --git a/core/src/test/java/org/opensearch/sql/expression/datetime/DateTimeFunctionTest.java b/core/src/test/java/org/opensearch/sql/expression/datetime/DateTimeFunctionTest.java index 79efa2a015..b8c17cf48a 100644 --- a/core/src/test/java/org/opensearch/sql/expression/datetime/DateTimeFunctionTest.java +++ b/core/src/test/java/org/opensearch/sql/expression/datetime/DateTimeFunctionTest.java @@ -24,6 +24,7 @@ import static org.opensearch.sql.data.type.ExprCoreType.TIMESTAMP; import com.google.common.collect.ImmutableList; +import java.time.LocalDate; import java.util.List; import lombok.AllArgsConstructor; import org.junit.jupiter.api.BeforeEach; @@ -240,6 +241,18 @@ public void date() { assertEquals(DATE, expr.type()); assertEquals(new ExprDateValue("2020-08-17"), eval(expr)); assertEquals("date(DATE '2020-08-17')", expr.toString()); + + expr = dsl.date(DSL.literal(new ExprDateValue("2020-08-17 12:12:00"))); + assertEquals(DATE, expr.type()); + assertEquals(new ExprDateValue("2020-08-17 12:12:00"), eval(expr)); + assertEquals("date(DATE '2020-08-17')", expr.toString()); + + expr = dsl.date(DSL.literal(new ExprDateValue("2020-08-17 12:12"))); + assertEquals(DATE, expr.type()); + assertEquals(new ExprDateValue("2020-08-17 12:12"), eval(expr)); + assertEquals("date(DATE '2020-08-17')", expr.toString()); + + } @Test @@ -795,6 +808,30 @@ public void time() { assertEquals(TIME, expr.type()); assertEquals(new ExprTimeValue("01:01:01"), eval(expr)); assertEquals("time(TIME '01:01:01')", expr.toString()); + + expr = dsl.time(DSL.literal(new ExprTimeValue("01:01"))); + assertEquals(TIME, expr.type()); + assertEquals(new ExprTimeValue("01:01"), eval(expr)); + assertEquals("time(TIME '01:01:00')", expr.toString()); + + expr = dsl.time(DSL.literal(new ExprTimeValue("2019-04-19 01:01:01"))); + assertEquals(TIME, expr.type()); + assertEquals(new ExprTimeValue("2019-04-19 01:01:01"), eval(expr)); + assertEquals("time(TIME '01:01:01')", expr.toString()); + + expr = dsl.time(DSL.literal(new ExprTimeValue("2019-04-19 01:01"))); + assertEquals(TIME, expr.type()); + assertEquals(new ExprTimeValue("2019-04-19 01:01"), eval(expr)); + assertEquals("time(TIME '01:01:00')", expr.toString()); + + expr = dsl.time(DSL.literal(new ExprTimeValue("01:01:01.0123"))); + assertEquals(TIME, expr.type()); + assertEquals(new ExprTimeValue("01:01:01.0123"), eval(expr)); + assertEquals("time(TIME '01:01:01.0123')", expr.toString()); + + expr = dsl.time(dsl.date(DSL.literal("2020-01-02"))); + assertEquals(TIME, expr.type()); + assertEquals(new ExprTimeValue("00:00:00"), expr.valueOf(null)); } @Test diff --git a/docs/user/dql/functions.rst b/docs/user/dql/functions.rst index 7f02e947a4..7d003a2262 100644 --- a/docs/user/dql/functions.rst +++ b/docs/user/dql/functions.rst @@ -1076,13 +1076,13 @@ Return type: DATE Example:: - >od SELECT DATE('2020-08-26'), DATE(TIMESTAMP('2020-08-26 13:49:00')) + os> SELECT DATE('2020-08-26'), DATE(TIMESTAMP('2020-08-26 13:49:00')), DATE('2020-08-26 13:49:00'), DATE('2020-08-26 13:49') fetched rows / total rows = 1/1 - +----------------------+------------------------------------------+ - | DATE('2020-08-26') | DATE(TIMESTAMP('2020-08-26 13:49:00')) | - |----------------------+------------------------------------------| - | DATE '2020-08-26' | DATE '2020-08-26' | - +----------------------+------------------------------------------+ + +----------------------+------------------------------------------+-------------------------------+----------------------------+ + | DATE('2020-08-26') | DATE(TIMESTAMP('2020-08-26 13:49:00')) | DATE('2020-08-26 13:49:00') | DATE('2020-08-26 13:49') | + |----------------------+------------------------------------------+-------------------------------+----------------------------| + | 2020-08-26 | 2020-08-26 | 2020-08-26 | 2020-08-26 | + +----------------------+------------------------------------------+-------------------------------+----------------------------+ DATETIME @@ -1880,13 +1880,13 @@ Return type: TIME Example:: - >od SELECT TIME('13:49:00'), TIME(TIMESTAMP('2020-08-26 13:49:00')) + os> SELECT TIME('13:49:00'), TIME('13:49'), TIME(TIMESTAMP('2020-08-26 13:49:00')), TIME('2020-08-26 13:49:00') fetched rows / total rows = 1/1 - +--------------------+------------------------------------------+ - | TIME('13:49:00') | TIME(TIMESTAMP('2020-08-26 13:49:00')) | - |--------------------+------------------------------------------| - | TIME '13:49:00' | TIME '13:49:00' | - +--------------------+------------------------------------------+ + +--------------------+-----------------+------------------------------------------+-------------------------------+ + | TIME('13:49:00') | TIME('13:49') | TIME(TIMESTAMP('2020-08-26 13:49:00')) | TIME('2020-08-26 13:49:00') | + |--------------------+-----------------+------------------------------------------+-------------------------------| + | 13:49:00 | 13:49:00 | 13:49:00 | 13:49:00 | + +--------------------+-----------------+------------------------------------------+-------------------------------+ TIME_TO_SEC diff --git a/docs/user/ppl/functions/datetime.rst b/docs/user/ppl/functions/datetime.rst index e6ff88d2a8..ee25acb32e 100644 --- a/docs/user/ppl/functions/datetime.rst +++ b/docs/user/ppl/functions/datetime.rst @@ -291,13 +291,38 @@ Return type: DATE Example:: - >od source=people | eval `DATE('2020-08-26')` = DATE('2020-08-26'), `DATE(TIMESTAMP('2020-08-26 13:49:00'))` = DATE(TIMESTAMP('2020-08-26 13:49:00')) | fields `DATE('2020-08-26')`, `DATE(TIMESTAMP('2020-08-26 13:49:00'))` + os> source=people | eval `DATE('2020-08-26')` = DATE('2020-08-26') | fields `DATE('2020-08-26')` fetched rows / total rows = 1/1 - +----------------------+------------------------------------------+ - | DATE('2020-08-26') | DATE(TIMESTAMP('2020-08-26 13:49:00')) | - |----------------------+------------------------------------------| - | DATE '2020-08-26' | DATE '2020-08-26' | - +----------------------+------------------------------------------+ + +----------------------+ + | DATE('2020-08-26') | + |----------------------| + | 2020-08-26 | + +----------------------+ + + os> source=people | eval `DATE(TIMESTAMP('2020-08-26 13:49:00'))` = DATE(TIMESTAMP('2020-08-26 13:49:00')) | fields `DATE(TIMESTAMP('2020-08-26 13:49:00'))` + fetched rows / total rows = 1/1 + +------------------------------------------+ + | DATE(TIMESTAMP('2020-08-26 13:49:00')) | + |------------------------------------------| + | 2020-08-26 | + +------------------------------------------+ + + os> source=people | eval `DATE('2020-08-26 13:49')` = DATE('2020-08-26 13:49') | fields `DATE('2020-08-26 13:49')` + fetched rows / total rows = 1/1 + +----------------------------+ + | DATE('2020-08-26 13:49') | + |----------------------------| + | 2020-08-26 | + +----------------------------+ + + os> source=people | eval `DATE('2020-08-26 13:49')` = DATE('2020-08-26 13:49') | fields `DATE('2020-08-26 13:49')` + fetched rows / total rows = 1/1 + +----------------------------+ + | DATE('2020-08-26 13:49') | + |----------------------------| + | 2020-08-26 | + +----------------------------+ + DATE_ADD @@ -1052,13 +1077,37 @@ Return type: TIME Example:: - >od source=people | eval `TIME('13:49:00')` = TIME('13:49:00'), `TIME(TIMESTAMP('2020-08-26 13:49:00'))` = TIME(TIMESTAMP('2020-08-26 13:49:00')) | fields `TIME('13:49:00')`, `TIME(TIMESTAMP('2020-08-26 13:49:00'))` + os> source=people | eval `TIME('13:49:00')` = TIME('13:49:00') | fields `TIME('13:49:00')` fetched rows / total rows = 1/1 - +--------------------+------------------------------------------+ - | TIME('13:49:00') | TIME(TIMESTAMP('2020-08-26 13:49:00')) | - |--------------------+------------------------------------------| - | TIME '13:49:00' | TIME '13:49:00' | - +--------------------+------------------------------------------+ + +--------------------+ + | TIME('13:49:00') | + |--------------------| + | 13:49:00 | + +--------------------+ + + os> source=people | eval `TIME('13:49')` = TIME('13:49') | fields `TIME('13:49')` + fetched rows / total rows = 1/1 + +-----------------+ + | TIME('13:49') | + |-----------------| + | 13:49:00 | + +-----------------+ + + os> source=people | eval `TIME('2020-08-26 13:49:00')` = TIME('2020-08-26 13:49:00') | fields `TIME('2020-08-26 13:49:00')` + fetched rows / total rows = 1/1 + +-------------------------------+ + | TIME('2020-08-26 13:49:00') | + |-------------------------------| + | 13:49:00 | + +-------------------------------+ + + os> source=people | eval `TIME('2020-08-26 13:49')` = TIME('2020-08-26 13:49') | fields `TIME('2020-08-26 13:49')` + fetched rows / total rows = 1/1 + +----------------------------+ + | TIME('2020-08-26 13:49') | + |----------------------------| + | 13:49:00 | + +----------------------------+ TIME_TO_SEC