Skip to content

Commit b235550

Browse files
Yury-FridlyandMax Ksyunz
andauthored
Add TIMEDIFF and DATEDIFF functions. (#131)
* 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]>
1 parent 64a3794 commit b235550

24 files changed

+600
-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: 92 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.MONTHS;
1011
import static org.opensearch.sql.data.type.ExprCoreType.DATE;
1112
import static org.opensearch.sql.data.type.ExprCoreType.DATETIME;
@@ -20,17 +21,20 @@
2021
import static org.opensearch.sql.expression.function.FunctionDSL.impl;
2122
import static org.opensearch.sql.expression.function.FunctionDSL.implWithProperties;
2223
import static org.opensearch.sql.expression.function.FunctionDSL.nullMissingHandling;
24+
import static org.opensearch.sql.expression.function.FunctionDSL.nullMissingHandlingWithProperties;
2325
import static org.opensearch.sql.utils.DateTimeFormatters.DATE_FORMATTER_LONG_YEAR;
2426
import static org.opensearch.sql.utils.DateTimeFormatters.DATE_FORMATTER_SHORT_YEAR;
2527
import static org.opensearch.sql.utils.DateTimeFormatters.DATE_TIME_FORMATTER_LONG_YEAR;
2628
import static org.opensearch.sql.utils.DateTimeFormatters.DATE_TIME_FORMATTER_SHORT_YEAR;
2729
import static org.opensearch.sql.utils.DateTimeFormatters.DATE_TIME_FORMATTER_STRICT_WITH_TZ;
30+
import static org.opensearch.sql.utils.DateTimeUtils.extractDate;
2831

2932
import java.math.BigDecimal;
3033
import java.math.RoundingMode;
3134
import java.text.DecimalFormat;
3235
import java.time.Clock;
3336
import java.time.DateTimeException;
37+
import java.time.Duration;
3438
import java.time.Instant;
3539
import java.time.LocalDate;
3640
import java.time.LocalDateTime;
@@ -63,6 +67,7 @@
6367
import org.opensearch.sql.expression.function.DefaultFunctionResolver;
6468
import org.opensearch.sql.expression.function.FunctionDSL;
6569
import org.opensearch.sql.expression.function.FunctionName;
70+
import org.opensearch.sql.expression.function.FunctionProperties;
6671
import org.opensearch.sql.expression.function.FunctionResolver;
6772
import org.opensearch.sql.utils.DateTimeUtils;
6873

@@ -95,6 +100,7 @@ public void register(BuiltinFunctionRepository repository) {
95100
repository.register(current_time());
96101
repository.register(current_timestamp());
97102
repository.register(date());
103+
repository.register(datediff());
98104
repository.register(datetime());
99105
repository.register(date_add());
100106
repository.register(date_sub());
@@ -125,6 +131,7 @@ public void register(BuiltinFunctionRepository repository) {
125131
repository.register(sysdate());
126132
repository.register(time());
127133
repository.register(time_to_sec());
134+
repository.register(timediff());
128135
repository.register(timestamp());
129136
repository.register(date_format());
130137
repository.register(to_days());
@@ -261,6 +268,46 @@ private DefaultFunctionResolver date() {
261268
impl(nullMissingHandling(DateTimeFunction::exprDate), DATE, TIMESTAMP));
262269
}
263270

271+
/*
272+
* Calculates the difference of date part of given values.
273+
* (DATE/DATETIME/TIMESTAMP/TIME, DATE/DATETIME/TIMESTAMP/TIME) -> LONG
274+
*/
275+
private DefaultFunctionResolver datediff() {
276+
return define(BuiltinFunctionName.DATEDIFF.getName(),
277+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
278+
LONG, DATE, DATE),
279+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
280+
LONG, DATETIME, DATE),
281+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
282+
LONG, DATE, DATETIME),
283+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
284+
LONG, DATETIME, DATETIME),
285+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
286+
LONG, DATE, TIME),
287+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
288+
LONG, TIME, DATE),
289+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
290+
LONG, TIME, TIME),
291+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
292+
LONG, TIMESTAMP, DATE),
293+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
294+
LONG, DATE, TIMESTAMP),
295+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
296+
LONG, TIMESTAMP, TIMESTAMP),
297+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
298+
LONG, TIMESTAMP, TIME),
299+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
300+
LONG, TIME, TIMESTAMP),
301+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
302+
LONG, TIMESTAMP, DATETIME),
303+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
304+
LONG, DATETIME, TIMESTAMP),
305+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
306+
LONG, TIME, DATETIME),
307+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
308+
LONG, DATETIME, TIME));
309+
}
310+
264311
/**
265312
* Specify a datetime with time zone field and a time zone to convert to.
266313
* Returns a local date time.
@@ -519,6 +566,22 @@ private DefaultFunctionResolver time() {
519566
impl(nullMissingHandling(DateTimeFunction::exprTime), TIME, TIMESTAMP));
520567
}
521568

569+
/**
570+
* Returns different between two times as a time.
571+
* (TIME, TIME) -> TIME
572+
* MySQL has these signatures too
573+
* (DATE, DATE) -> TIME // result is > 24 hours
574+
* (DATETIME, DATETIME) -> TIME // result is > 24 hours
575+
* (TIMESTAMP, TIMESTAMP) -> TIME // result is > 24 hours
576+
* (x, x) -> NULL // when args have different types
577+
* (STRING, STRING) -> TIME // argument strings contain same types only
578+
* (STRING, STRING) -> NULL // argument strings are different types
579+
*/
580+
private DefaultFunctionResolver timediff() {
581+
return define(BuiltinFunctionName.TIMEDIFF.getName(),
582+
impl(nullMissingHandling(DateTimeFunction::exprTimeDiff), TIME, TIME, TIME));
583+
}
584+
522585
/**
523586
* TIME_TO_SEC(STRING/TIME/DATETIME/TIMESTAMP). return the time argument, converted to seconds.
524587
*/
@@ -691,6 +754,22 @@ private ExprValue exprDate(ExprValue exprValue) {
691754
}
692755
}
693756

757+
/**
758+
* Calculate the value in days from one date to the other.
759+
* Only the date parts of the values are used in the calculation.
760+
*
761+
* @param first The first value.
762+
* @param second The second value.
763+
* @return The diff.
764+
*/
765+
private ExprValue exprDateDiff(FunctionProperties functionProperties,
766+
ExprValue first, ExprValue second) {
767+
// java inverses the value, so we have to swap 1 and 2
768+
return new ExprLongValue(DAYS.between(
769+
extractDate(second, functionProperties),
770+
extractDate(first, functionProperties)));
771+
}
772+
694773
/**
695774
* DateTime implementation for ExprValue.
696775
*
@@ -1039,6 +1118,19 @@ private ExprValue exprTime(ExprValue exprValue) {
10391118
}
10401119
}
10411120

1121+
/**
1122+
* Calculate the time difference between two times.
1123+
*
1124+
* @param first The first value.
1125+
* @param second The second value.
1126+
* @return The diff.
1127+
*/
1128+
private ExprValue exprTimeDiff(ExprValue first, ExprValue second) {
1129+
// java inverses the value, so we have to swap 1 and 2
1130+
return new ExprTimeValue(LocalTime.MIN.plus(
1131+
Duration.between(second.timeValue(), first.timeValue())));
1132+
}
1133+
10421134
/**
10431135
* Timestamp implementation for ExprValue.
10441136
*

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")),
@@ -86,6 +87,7 @@ public enum BuiltinFunctionName {
8687
SECOND(FunctionName.of("second")),
8788
SUBDATE(FunctionName.of("subdate")),
8889
TIME(FunctionName.of("time")),
90+
TIMEDIFF(FunctionName.of("timediff")),
8991
TIME_TO_SEC(FunctionName.of("time_to_sec")),
9092
TIMESTAMP(FunctionName.of("timestamp")),
9193
DATE_FORMAT(FunctionName.of("date_format")),

0 commit comments

Comments
 (0)