Skip to content

Commit 9d7a6eb

Browse files
Add TIMEDIFF and DATEDIFF functions. (#131) (#1195) (#1234)
* Add `TIMEDIFF` and `DATEDIFF` functions. Signed-off-by: Yury-Fridlyand <[email protected]> Signed-off-by: Yury-Fridlyand <[email protected]> Co-authored-by: Max Ksyunz <[email protected]> Signed-off-by: Yury-Fridlyand <[email protected]> Signed-off-by: Yury-Fridlyand <[email protected]> Co-authored-by: Max Ksyunz <[email protected]> (cherry picked from commit 438c44d) Co-authored-by: Yury-Fridlyand <[email protected]>
1 parent de79815 commit 9d7a6eb

24 files changed

+599
-47
lines changed

core/src/main/java/org/opensearch/sql/data/model/ExprDateValue.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import java.time.LocalDate;
1414
import java.time.LocalDateTime;
1515
import java.time.LocalTime;
16-
import java.time.ZoneId;
1716
import java.time.ZonedDateTime;
1817
import java.time.format.DateTimeFormatter;
1918
import java.time.format.DateTimeParseException;
@@ -69,7 +68,7 @@ public LocalDateTime datetimeValue() {
6968

7069
@Override
7170
public Instant timestampValue() {
72-
return ZonedDateTime.of(date, timeValue(), ZoneId.systemDefault()).toInstant();
71+
return ZonedDateTime.of(date, timeValue(), ExprTimestampValue.ZONE).toInstant();
7372
}
7473

7574
@Override

core/src/main/java/org/opensearch/sql/data/model/ExprDatetimeValue.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import java.time.LocalDate;
1212
import java.time.LocalDateTime;
1313
import java.time.LocalTime;
14-
import java.time.ZoneId;
1514
import java.time.ZonedDateTime;
1615
import java.time.format.DateTimeFormatter;
1716
import java.time.format.DateTimeFormatterBuilder;
@@ -71,7 +70,7 @@ public LocalTime timeValue() {
7170

7271
@Override
7372
public Instant timestampValue() {
74-
return ZonedDateTime.of(datetime, ZoneId.of("UTC")).toInstant();
73+
return ZonedDateTime.of(datetime, ExprTimestampValue.ZONE).toInstant();
7574
}
7675

7776
@Override

core/src/main/java/org/opensearch/sql/data/model/ExprTimeValue.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@
1313
import java.time.LocalDate;
1414
import java.time.LocalDateTime;
1515
import java.time.LocalTime;
16-
import java.time.ZoneId;
1716
import java.time.ZonedDateTime;
1817
import java.time.format.DateTimeParseException;
1918
import java.util.Objects;
2019
import lombok.RequiredArgsConstructor;
2120
import org.opensearch.sql.data.type.ExprCoreType;
2221
import org.opensearch.sql.data.type.ExprType;
2322
import org.opensearch.sql.exception.SemanticCheckException;
23+
import org.opensearch.sql.expression.function.FunctionProperties;
2424

2525
/**
2626
* Expression Time Value.
@@ -57,6 +57,19 @@ public LocalTime timeValue() {
5757
return time;
5858
}
5959

60+
public LocalDate dateValue(FunctionProperties functionProperties) {
61+
return LocalDate.now(functionProperties.getQueryStartClock());
62+
}
63+
64+
public LocalDateTime datetimeValue(FunctionProperties functionProperties) {
65+
return LocalDateTime.of(dateValue(functionProperties), timeValue());
66+
}
67+
68+
public Instant timestampValue(FunctionProperties functionProperties) {
69+
return ZonedDateTime.of(dateValue(functionProperties), timeValue(), ExprTimestampValue.ZONE)
70+
.toInstant();
71+
}
72+
6073
@Override
6174
public String toString() {
6275
return String.format("TIME '%s'", value());

core/src/main/java/org/opensearch/sql/data/model/ExprTimestampValue.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public class ExprTimestampValue extends AbstractExprValue {
3030
/**
3131
* todo. only support UTC now.
3232
*/
33-
private static final ZoneId ZONE = ZoneId.of("UTC");
33+
public static final ZoneId ZONE = ZoneId.of("UTC");
3434

3535
private final Instant timestamp;
3636

core/src/main/java/org/opensearch/sql/data/model/ExprValueUtils.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
package org.opensearch.sql.data.model;
88

9+
import java.time.Instant;
10+
import java.time.LocalDate;
11+
import java.time.LocalDateTime;
12+
import java.time.LocalTime;
913
import java.time.temporal.TemporalAmount;
1014
import java.util.ArrayList;
1115
import java.util.LinkedHashMap;
@@ -61,6 +65,22 @@ public static ExprValue intervalValue(TemporalAmount value) {
6165
return new ExprIntervalValue(value);
6266
}
6367

68+
public static ExprValue dateValue(LocalDate value) {
69+
return new ExprDateValue(value);
70+
}
71+
72+
public static ExprValue datetimeValue(LocalDateTime value) {
73+
return new ExprDatetimeValue(value);
74+
}
75+
76+
public static ExprValue timeValue(LocalTime value) {
77+
return new ExprTimeValue(value);
78+
}
79+
80+
public static ExprValue timestampValue(Instant value) {
81+
return new ExprTimestampValue(value);
82+
}
83+
6484
/**
6585
* {@link ExprTupleValue} constructor.
6686
*/
@@ -115,6 +135,14 @@ public static ExprValue fromObjectValue(Object o) {
115135
return stringValue((String) o);
116136
} else if (o instanceof Float) {
117137
return floatValue((Float) o);
138+
} else if (o instanceof LocalDate) {
139+
return dateValue((LocalDate) o);
140+
} else if (o instanceof LocalDateTime) {
141+
return datetimeValue((LocalDateTime) o);
142+
} else if (o instanceof LocalTime) {
143+
return timeValue((LocalTime) o);
144+
} else if (o instanceof Instant) {
145+
return timestampValue((Instant) o);
118146
} else {
119147
throw new ExpressionEvaluationException("unsupported object " + o.getClass());
120148
}

core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunction.java

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
package org.opensearch.sql.expression.datetime;
88

9+
import static java.time.temporal.ChronoUnit.DAYS;
910
import static java.time.temporal.ChronoUnit.MINUTES;
1011
import static java.time.temporal.ChronoUnit.MONTHS;
1112
import static org.opensearch.sql.data.type.ExprCoreType.DATE;
@@ -21,17 +22,20 @@
2122
import static org.opensearch.sql.expression.function.FunctionDSL.impl;
2223
import static org.opensearch.sql.expression.function.FunctionDSL.implWithProperties;
2324
import static org.opensearch.sql.expression.function.FunctionDSL.nullMissingHandling;
25+
import static org.opensearch.sql.expression.function.FunctionDSL.nullMissingHandlingWithProperties;
2426
import static org.opensearch.sql.utils.DateTimeFormatters.DATE_FORMATTER_LONG_YEAR;
2527
import static org.opensearch.sql.utils.DateTimeFormatters.DATE_FORMATTER_SHORT_YEAR;
2628
import static org.opensearch.sql.utils.DateTimeFormatters.DATE_TIME_FORMATTER_LONG_YEAR;
2729
import static org.opensearch.sql.utils.DateTimeFormatters.DATE_TIME_FORMATTER_SHORT_YEAR;
2830
import static org.opensearch.sql.utils.DateTimeFormatters.DATE_TIME_FORMATTER_STRICT_WITH_TZ;
31+
import static org.opensearch.sql.utils.DateTimeUtils.extractDate;
2932

3033
import java.math.BigDecimal;
3134
import java.math.RoundingMode;
3235
import java.text.DecimalFormat;
3336
import java.time.Clock;
3437
import java.time.DateTimeException;
38+
import java.time.Duration;
3539
import java.time.Instant;
3640
import java.time.LocalDate;
3741
import java.time.LocalDateTime;
@@ -97,6 +101,7 @@ public void register(BuiltinFunctionRepository repository) {
97101
repository.register(current_time());
98102
repository.register(current_timestamp());
99103
repository.register(date());
104+
repository.register(datediff());
100105
repository.register(datetime());
101106
repository.register(date_add());
102107
repository.register(date_sub());
@@ -128,6 +133,7 @@ public void register(BuiltinFunctionRepository repository) {
128133
repository.register(sysdate());
129134
repository.register(time());
130135
repository.register(time_to_sec());
136+
repository.register(timediff());
131137
repository.register(timestamp());
132138
repository.register(utc_date());
133139
repository.register(utc_time());
@@ -267,6 +273,46 @@ private DefaultFunctionResolver date() {
267273
impl(nullMissingHandling(DateTimeFunction::exprDate), DATE, TIMESTAMP));
268274
}
269275

276+
/*
277+
* Calculates the difference of date part of given values.
278+
* (DATE/DATETIME/TIMESTAMP/TIME, DATE/DATETIME/TIMESTAMP/TIME) -> LONG
279+
*/
280+
private DefaultFunctionResolver datediff() {
281+
return define(BuiltinFunctionName.DATEDIFF.getName(),
282+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
283+
LONG, DATE, DATE),
284+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
285+
LONG, DATETIME, DATE),
286+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
287+
LONG, DATE, DATETIME),
288+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
289+
LONG, DATETIME, DATETIME),
290+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
291+
LONG, DATE, TIME),
292+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
293+
LONG, TIME, DATE),
294+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
295+
LONG, TIME, TIME),
296+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
297+
LONG, TIMESTAMP, DATE),
298+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
299+
LONG, DATE, TIMESTAMP),
300+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
301+
LONG, TIMESTAMP, TIMESTAMP),
302+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
303+
LONG, TIMESTAMP, TIME),
304+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
305+
LONG, TIME, TIMESTAMP),
306+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
307+
LONG, TIMESTAMP, DATETIME),
308+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
309+
LONG, DATETIME, TIMESTAMP),
310+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
311+
LONG, TIME, DATETIME),
312+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
313+
LONG, DATETIME, TIME));
314+
}
315+
270316
/**
271317
* Specify a datetime with time zone field and a time zone to convert to.
272318
* Returns a local date time.
@@ -538,6 +584,22 @@ private DefaultFunctionResolver time() {
538584
impl(nullMissingHandling(DateTimeFunction::exprTime), TIME, TIMESTAMP));
539585
}
540586

587+
/**
588+
* Returns different between two times as a time.
589+
* (TIME, TIME) -> TIME
590+
* MySQL has these signatures too
591+
* (DATE, DATE) -> TIME // result is > 24 hours
592+
* (DATETIME, DATETIME) -> TIME // result is > 24 hours
593+
* (TIMESTAMP, TIMESTAMP) -> TIME // result is > 24 hours
594+
* (x, x) -> NULL // when args have different types
595+
* (STRING, STRING) -> TIME // argument strings contain same types only
596+
* (STRING, STRING) -> NULL // argument strings are different types
597+
*/
598+
private DefaultFunctionResolver timediff() {
599+
return define(BuiltinFunctionName.TIMEDIFF.getName(),
600+
impl(nullMissingHandling(DateTimeFunction::exprTimeDiff), TIME, TIME, TIME));
601+
}
602+
541603
/**
542604
* TIME_TO_SEC(STRING/TIME/DATETIME/TIMESTAMP). return the time argument, converted to seconds.
543605
*/
@@ -737,6 +799,22 @@ private ExprValue exprDate(ExprValue exprValue) {
737799
}
738800
}
739801

802+
/**
803+
* Calculate the value in days from one date to the other.
804+
* Only the date parts of the values are used in the calculation.
805+
*
806+
* @param first The first value.
807+
* @param second The second value.
808+
* @return The diff.
809+
*/
810+
private ExprValue exprDateDiff(FunctionProperties functionProperties,
811+
ExprValue first, ExprValue second) {
812+
// java inverses the value, so we have to swap 1 and 2
813+
return new ExprLongValue(DAYS.between(
814+
extractDate(second, functionProperties),
815+
extractDate(first, functionProperties)));
816+
}
817+
740818
/**
741819
* DateTime implementation for ExprValue.
742820
*
@@ -1096,6 +1174,19 @@ private ExprValue exprTime(ExprValue exprValue) {
10961174
}
10971175
}
10981176

1177+
/**
1178+
* Calculate the time difference between two times.
1179+
*
1180+
* @param first The first value.
1181+
* @param second The second value.
1182+
* @return The diff.
1183+
*/
1184+
private ExprValue exprTimeDiff(ExprValue first, ExprValue second) {
1185+
// java inverses the value, so we have to swap 1 and 2
1186+
return new ExprTimeValue(LocalTime.MIN.plus(
1187+
Duration.between(second.timeValue(), first.timeValue())));
1188+
}
1189+
10991190
/**
11001191
* Timestamp implementation for ExprValue.
11011192
*

core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionName.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public enum BuiltinFunctionName {
6161
ADDDATE(FunctionName.of("adddate")),
6262
CONVERT_TZ(FunctionName.of("convert_tz")),
6363
DATE(FunctionName.of("date")),
64+
DATEDIFF(FunctionName.of("datediff")),
6465
DATETIME(FunctionName.of("datetime")),
6566
DATE_ADD(FunctionName.of("date_add")),
6667
DATE_SUB(FunctionName.of("date_sub")),
@@ -87,6 +88,7 @@ public enum BuiltinFunctionName {
8788
SECOND(FunctionName.of("second")),
8889
SUBDATE(FunctionName.of("subdate")),
8990
TIME(FunctionName.of("time")),
91+
TIMEDIFF(FunctionName.of("timediff")),
9092
TIME_TO_SEC(FunctionName.of("time_to_sec")),
9193
TIMESTAMP(FunctionName.of("timestamp")),
9294
DATE_FORMAT(FunctionName.of("date_format")),

0 commit comments

Comments
 (0)