Skip to content

Commit e6c1cfb

Browse files
Merge branch 'main' into integ-datetime-calculations
2 parents 99c3708 + b919ba0 commit e6c1cfb

File tree

25 files changed

+756
-46
lines changed

25 files changed

+756
-46
lines changed

MAINTAINERS.md

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
# OpenSearch SQL Maintainers
1+
## Overview
22

3-
## Maintainers
3+
This document contains a list of maintainers in this repo. See [opensearch-project/.github/RESPONSIBILITIES.md](https://github.com/opensearch-project/.github/blob/main/RESPONSIBILITIES.md#maintainer-responsibilities) that explains what the role of maintainer means, what maintainers do in this and other repos, and how they should be doing it. If you're interested in contributing, and becoming a maintainer, see [CONTRIBUTING](CONTRIBUTING.md).
44

5-
| Maintainer | GitHub ID | Affiliation |
6-
| --------------- | --------- | ----------- |
7-
| Anirudha (Ani) Jadhav | [anirudha](https://github.com/anirudha) | Amazon |
8-
| Peng Huo | [penghuo](https://github.com/penghuo) | Amazon |
9-
| Chen Dai | [dai-chen](https://github.com/dai-chen) | Amazon |
10-
| Chloe Zhang | [chloe-zh](https://github.com/chloe-zh) | Amazon |
11-
| Nick Knize | [nknize](https://github.com/nknize) | Amazon |
12-
| Charlotte Henkle | [CEHENKLE](https://github.com/CEHENKLE) | Amazon |
13-
| Max Ksyunz | [MaxKsyunz](https://github.com/MaxKsyunz) | BitQuill |
14-
| Yury Fridlyand | [Yury-Fridlyand](https://github.com/Yury-Fridlyand) | BitQuill |
5+
## Current Maintainers
6+
7+
| Maintainer | GitHub ID | Affiliation |
8+
| --------------------- | --------------------------------------------------- | ----------- |
9+
| Anirudha (Ani) Jadhav | [anirudha](https://github.com/anirudha) | Amazon |
10+
| Peng Huo | [penghuo](https://github.com/penghuo) | Amazon |
11+
| Chen Dai | [dai-chen](https://github.com/dai-chen) | Amazon |
12+
| Chloe Zhang | [chloe-zh](https://github.com/chloe-zh) | Amazon |
13+
| Nick Knize | [nknize](https://github.com/nknize) | Amazon |
14+
| Charlotte Henkle | [CEHENKLE](https://github.com/CEHENKLE) | Amazon |
15+
| Max Ksyunz | [MaxKsyunz](https://github.com/MaxKsyunz) | BitQuill |
16+
| Yury Fridlyand | [Yury-Fridlyand](https://github.com/Yury-Fridlyand) | BitQuill |

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

Lines changed: 90 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;
@@ -27,13 +28,15 @@
2728
import static org.opensearch.sql.utils.DateTimeFormatters.DATE_TIME_FORMATTER_LONG_YEAR;
2829
import static org.opensearch.sql.utils.DateTimeFormatters.DATE_TIME_FORMATTER_SHORT_YEAR;
2930
import static org.opensearch.sql.utils.DateTimeFormatters.DATE_TIME_FORMATTER_STRICT_WITH_TZ;
31+
import static org.opensearch.sql.utils.DateTimeUtils.extractDate;
3032
import static org.opensearch.sql.utils.DateTimeUtils.extractDateTime;
3133

3234
import java.math.BigDecimal;
3335
import java.math.RoundingMode;
3436
import java.text.DecimalFormat;
3537
import java.time.Clock;
3638
import java.time.DateTimeException;
39+
import java.time.Duration;
3740
import java.time.Instant;
3841
import java.time.LocalDate;
3942
import java.time.LocalDateTime;
@@ -107,6 +110,7 @@ public void register(BuiltinFunctionRepository repository) {
107110
repository.register(current_time());
108111
repository.register(current_timestamp());
109112
repository.register(date());
113+
repository.register(datediff());
110114
repository.register(datetime());
111115
repository.register(date_add());
112116
repository.register(date_sub());
@@ -138,6 +142,7 @@ public void register(BuiltinFunctionRepository repository) {
138142
repository.register(sysdate());
139143
repository.register(time());
140144
repository.register(time_to_sec());
145+
repository.register(timediff());
141146
repository.register(timestamp());
142147
repository.register(utc_date());
143148
repository.register(utc_time());
@@ -298,6 +303,46 @@ private DefaultFunctionResolver date() {
298303
impl(nullMissingHandling(DateTimeFunction::exprDate), DATE, TIMESTAMP));
299304
}
300305

306+
/*
307+
* Calculates the difference of date part of given values.
308+
* (DATE/DATETIME/TIMESTAMP/TIME, DATE/DATETIME/TIMESTAMP/TIME) -> LONG
309+
*/
310+
private DefaultFunctionResolver datediff() {
311+
return define(BuiltinFunctionName.DATEDIFF.getName(),
312+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
313+
LONG, DATE, DATE),
314+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
315+
LONG, DATETIME, DATE),
316+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
317+
LONG, DATE, DATETIME),
318+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
319+
LONG, DATETIME, DATETIME),
320+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
321+
LONG, DATE, TIME),
322+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
323+
LONG, TIME, DATE),
324+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
325+
LONG, TIME, TIME),
326+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
327+
LONG, TIMESTAMP, DATE),
328+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
329+
LONG, DATE, TIMESTAMP),
330+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
331+
LONG, TIMESTAMP, TIMESTAMP),
332+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
333+
LONG, TIMESTAMP, TIME),
334+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
335+
LONG, TIME, TIMESTAMP),
336+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
337+
LONG, TIMESTAMP, DATETIME),
338+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
339+
LONG, DATETIME, TIMESTAMP),
340+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
341+
LONG, TIME, DATETIME),
342+
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
343+
LONG, DATETIME, TIME));
344+
}
345+
301346
/**
302347
* Specify a datetime with time zone field and a time zone to convert to.
303348
* Returns a local date time.
@@ -557,6 +602,22 @@ private DefaultFunctionResolver time() {
557602
impl(nullMissingHandling(DateTimeFunction::exprTime), TIME, TIMESTAMP));
558603
}
559604

605+
/**
606+
* Returns different between two times as a time.
607+
* (TIME, TIME) -> TIME
608+
* MySQL has these signatures too
609+
* (DATE, DATE) -> TIME // result is > 24 hours
610+
* (DATETIME, DATETIME) -> TIME // result is > 24 hours
611+
* (TIMESTAMP, TIMESTAMP) -> TIME // result is > 24 hours
612+
* (x, x) -> NULL // when args have different types
613+
* (STRING, STRING) -> TIME // argument strings contain same types only
614+
* (STRING, STRING) -> NULL // argument strings are different types
615+
*/
616+
private DefaultFunctionResolver timediff() {
617+
return define(BuiltinFunctionName.TIMEDIFF.getName(),
618+
impl(nullMissingHandling(DateTimeFunction::exprTimeDiff), TIME, TIME, TIME));
619+
}
620+
560621
/**
561622
* TIME_TO_SEC(STRING/TIME/DATETIME/TIMESTAMP). return the time argument, converted to seconds.
562623
*/
@@ -792,6 +853,22 @@ private ExprValue exprDate(ExprValue exprValue) {
792853
}
793854
}
794855

856+
/**
857+
* Calculate the value in days from one date to the other.
858+
* Only the date parts of the values are used in the calculation.
859+
*
860+
* @param first The first value.
861+
* @param second The second value.
862+
* @return The diff.
863+
*/
864+
private ExprValue exprDateDiff(FunctionProperties functionProperties,
865+
ExprValue first, ExprValue second) {
866+
// java inverses the value, so we have to swap 1 and 2
867+
return new ExprLongValue(DAYS.between(
868+
extractDate(second, functionProperties),
869+
extractDate(first, functionProperties)));
870+
}
871+
795872
/**
796873
* DateTime implementation for ExprValue.
797874
*
@@ -1151,6 +1228,19 @@ private ExprValue exprTime(ExprValue exprValue) {
11511228
}
11521229
}
11531230

1231+
/**
1232+
* Calculate the time difference between two times.
1233+
*
1234+
* @param first The first value.
1235+
* @param second The second value.
1236+
* @return The diff.
1237+
*/
1238+
private ExprValue exprTimeDiff(ExprValue first, ExprValue second) {
1239+
// java inverses the value, so we have to swap 1 and 2
1240+
return new ExprTimeValue(LocalTime.MIN.plus(
1241+
Duration.between(second.timeValue(), first.timeValue())));
1242+
}
1243+
11541244
/**
11551245
* Timestamp implementation for ExprValue.
11561246
*

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")),

core/src/main/java/org/opensearch/sql/utils/DateTimeUtils.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package org.opensearch.sql.utils;
77

88
import java.time.Instant;
9+
import java.time.LocalDate;
910
import java.time.LocalDateTime;
1011
import java.time.ZoneId;
1112
import java.time.ZonedDateTime;
@@ -139,4 +140,15 @@ public static LocalDateTime extractDateTime(ExprValue value,
139140
? ((ExprTimeValue) value).datetimeValue(functionProperties)
140141
: value.datetimeValue();
141142
}
143+
144+
/**
145+
* Extracts LocalDate from a datetime ExprValue.
146+
* Uses `FunctionProperties` for `ExprTimeValue`.
147+
*/
148+
public static LocalDate extractDate(ExprValue value,
149+
FunctionProperties functionProperties) {
150+
return value instanceof ExprTimeValue
151+
? ((ExprTimeValue) value).dateValue(functionProperties)
152+
: value.dateValue();
153+
}
142154
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.expression.datetime;
7+
8+
import static java.time.temporal.ChronoUnit.DAYS;
9+
import static org.junit.jupiter.api.Assertions.assertEquals;
10+
11+
import java.time.Instant;
12+
import java.time.LocalDate;
13+
import java.time.LocalDateTime;
14+
import java.time.LocalTime;
15+
import java.time.ZoneId;
16+
import java.time.temporal.Temporal;
17+
import java.util.TimeZone;
18+
import java.util.stream.Stream;
19+
import org.junit.jupiter.params.ParameterizedTest;
20+
import org.junit.jupiter.params.provider.Arguments;
21+
import org.junit.jupiter.params.provider.MethodSource;
22+
23+
public class DateDiffTest extends DateTimeTestBase {
24+
25+
private static final LocalTime timeSample1 = LocalTime.of(12, 42);
26+
private static final LocalTime timeSample2 = LocalTime.of(7, 40);
27+
private static final LocalDate dateSample1 = LocalDate.of(2022, 6, 6);
28+
private static final LocalDate dateSample2 = LocalDate.of(1961, 4, 12);
29+
private static final LocalDate dateSample3 = LocalDate.of(1993, 3, 4);
30+
private static final LocalDate epochStart = LocalDate.of(1970, 1, 1);
31+
private static final LocalDate dateNow = LocalDate.now();
32+
private static final LocalDateTime dateTimeSample1 = LocalDateTime.of(1961, 4, 12, 9, 7);
33+
private static final LocalDateTime dateTimeSample2 = LocalDateTime.of(1993, 3, 4, 5, 6);
34+
35+
// Function signature is:
36+
// (DATE/DATETIME/TIMESTAMP/TIME, DATE/DATETIME/TIMESTAMP/TIME) -> LONG
37+
private static Stream<Arguments> getTestData() {
38+
// Arguments are: first argument for `DATE_DIFF` function, second argument and expected result.
39+
return Stream.of(
40+
Arguments.of(timeSample1, timeSample2, 0L),
41+
Arguments.of(timeSample1, dateNow, 0L),
42+
Arguments.of(timeSample1, LocalDateTime.now(), 0L),
43+
Arguments.of(timeSample1,
44+
Instant.now().plusMillis(TimeZone.getDefault().getRawOffset()), 0L),
45+
Arguments.of(dateSample1, timeSample1,
46+
-DAYS.between(dateSample1, dateNow)),
47+
Arguments.of(dateSample1, dateSample3,
48+
-DAYS.between(dateSample1, dateSample3)),
49+
Arguments.of(dateSample1, dateTimeSample1,
50+
-DAYS.between(dateSample1, dateSample2)),
51+
Arguments.of(dateSample1, Instant.ofEpochSecond(42),
52+
-DAYS.between(dateSample1, epochStart)),
53+
Arguments.of(dateTimeSample1, LocalTime.now(),
54+
-DAYS.between(dateSample2, dateNow)),
55+
Arguments.of(dateTimeSample1, dateSample3,
56+
-DAYS.between(dateSample2, dateSample3)),
57+
Arguments.of(dateTimeSample1, dateTimeSample2,
58+
-DAYS.between(dateSample2, dateSample3)),
59+
Arguments.of(dateTimeSample1, Instant.ofEpochSecond(0),
60+
-DAYS.between(dateSample2, epochStart)),
61+
Arguments.of(Instant.ofEpochSecond(0), LocalTime.MAX,
62+
-DAYS.between(epochStart, dateNow)),
63+
Arguments.of(Instant.ofEpochSecond(0), dateSample3,
64+
-DAYS.between(epochStart, dateSample3)),
65+
Arguments.of(Instant.ofEpochSecond(0), dateTimeSample2,
66+
-DAYS.between(epochStart, dateSample3)),
67+
Arguments.of(Instant.ofEpochSecond(0), Instant.now(),
68+
-DAYS.between(epochStart, LocalDateTime.now(ZoneId.of("UTC"))))
69+
);
70+
}
71+
72+
@ParameterizedTest
73+
@MethodSource("getTestData")
74+
public void try_different_data(Temporal arg1, Temporal arg2, Long expectedResult) {
75+
assertEquals(expectedResult, datediff(arg1, arg2));
76+
}
77+
}

core/src/test/java/org/opensearch/sql/expression/datetime/DateTimeTestBase.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,19 @@
66
package org.opensearch.sql.expression.datetime;
77

88
import static org.opensearch.sql.data.model.ExprValueUtils.fromObjectValue;
9+
import static org.opensearch.sql.data.type.ExprCoreType.DOUBLE;
910

1011
import java.time.Instant;
1112
import java.time.LocalDate;
1213
import java.time.LocalDateTime;
1314
import java.time.LocalTime;
15+
import java.time.temporal.Temporal;
1416
import java.util.List;
1517
import org.opensearch.sql.data.model.ExprDateValue;
1618
import org.opensearch.sql.data.model.ExprDatetimeValue;
1719
import org.opensearch.sql.data.model.ExprMissingValue;
1820
import org.opensearch.sql.data.model.ExprNullValue;
21+
import org.opensearch.sql.data.model.ExprTimeValue;
1922
import org.opensearch.sql.data.model.ExprTimestampValue;
2023
import org.opensearch.sql.data.model.ExprValue;
2124
import org.opensearch.sql.expression.DSL;
@@ -68,6 +71,17 @@ protected ExprValue date_sub(Object first, Object second) {
6871
.valueOf(null);
6972
}
7073

74+
protected FunctionExpression datediff(Expression first, Expression second) {
75+
return (FunctionExpression) functionRepository.compile(
76+
functionProperties,
77+
BuiltinFunctionName.DATEDIFF.getName(), List.of(first, second));
78+
}
79+
80+
protected Long datediff(Temporal first, Temporal second) {
81+
return datediff(DSL.literal(fromObjectValue(first)), DSL.literal(fromObjectValue(second)))
82+
.valueOf(null).longValue();
83+
}
84+
7185
protected LocalDateTime fromUnixTime(Double value) {
7286
return fromUnixTime(DSL.literal(value)).valueOf().datetimeValue();
7387
}
@@ -106,7 +120,6 @@ protected FunctionExpression maketime(Expression hour, Expression minute, Expres
106120
BuiltinFunctionName.MAKETIME.getName(), List.of(hour, minute, second));
107121
}
108122

109-
110123
protected LocalTime maketime(Double hour, Double minute, Double second) {
111124
return maketime(DSL.literal(hour), DSL.literal(minute), DSL.literal(second))
112125
.valueOf().timeValue();
@@ -154,6 +167,17 @@ protected ExprValue subdate(Object first, Object interval) {
154167
.valueOf(null);
155168
}
156169

170+
protected FunctionExpression timediff(Expression first, Expression second) {
171+
return (FunctionExpression) functionRepository.compile(
172+
functionProperties,
173+
BuiltinFunctionName.TIMEDIFF.getName(), List.of(first, second));
174+
}
175+
176+
protected LocalTime timediff(LocalTime first, LocalTime second) {
177+
return timediff(DSL.literal(new ExprTimeValue(first)), DSL.literal(new ExprTimeValue(second)))
178+
.valueOf(null).timeValue();
179+
}
180+
157181
protected FunctionExpression unixTimeStampExpr() {
158182
return (FunctionExpression) functionRepository.compile(
159183
functionProperties, BuiltinFunctionName.UNIX_TIMESTAMP.getName(), List.of());

0 commit comments

Comments
 (0)