Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport 2.x] Add TIMEDIFF and DATEDIFF functions. (#131) #1234

Merged
merged 1 commit into from
Jan 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
Expand Down Expand Up @@ -69,7 +68,7 @@ public LocalDateTime datetimeValue() {

@Override
public Instant timestampValue() {
return ZonedDateTime.of(date, timeValue(), ZoneId.systemDefault()).toInstant();
return ZonedDateTime.of(date, timeValue(), ExprTimestampValue.ZONE).toInstant();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
Expand Down Expand Up @@ -71,7 +70,7 @@ public LocalTime timeValue() {

@Override
public Instant timestampValue() {
return ZonedDateTime.of(datetime, ZoneId.of("UTC")).toInstant();
return ZonedDateTime.of(datetime, ExprTimestampValue.ZONE).toInstant();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeParseException;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.opensearch.sql.data.type.ExprCoreType;
import org.opensearch.sql.data.type.ExprType;
import org.opensearch.sql.exception.SemanticCheckException;
import org.opensearch.sql.expression.function.FunctionProperties;

/**
* Expression Time Value.
Expand Down Expand Up @@ -57,6 +57,19 @@ public LocalTime timeValue() {
return time;
}

public LocalDate dateValue(FunctionProperties functionProperties) {
return LocalDate.now(functionProperties.getQueryStartClock());
}

public LocalDateTime datetimeValue(FunctionProperties functionProperties) {
return LocalDateTime.of(dateValue(functionProperties), timeValue());
}

public Instant timestampValue(FunctionProperties functionProperties) {
return ZonedDateTime.of(dateValue(functionProperties), timeValue(), ExprTimestampValue.ZONE)
.toInstant();
}

@Override
public String toString() {
return String.format("TIME '%s'", value());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class ExprTimestampValue extends AbstractExprValue {
/**
* todo. only support UTC now.
*/
private static final ZoneId ZONE = ZoneId.of("UTC");
public static final ZoneId ZONE = ZoneId.of("UTC");

private final Instant timestamp;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

package org.opensearch.sql.data.model;

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.TemporalAmount;
import java.util.ArrayList;
import java.util.LinkedHashMap;
Expand Down Expand Up @@ -61,6 +65,22 @@ public static ExprValue intervalValue(TemporalAmount value) {
return new ExprIntervalValue(value);
}

public static ExprValue dateValue(LocalDate value) {
return new ExprDateValue(value);
}

public static ExprValue datetimeValue(LocalDateTime value) {
return new ExprDatetimeValue(value);
}

public static ExprValue timeValue(LocalTime value) {
return new ExprTimeValue(value);
}

public static ExprValue timestampValue(Instant value) {
return new ExprTimestampValue(value);
}

/**
* {@link ExprTupleValue} constructor.
*/
Expand Down Expand Up @@ -115,6 +135,14 @@ public static ExprValue fromObjectValue(Object o) {
return stringValue((String) o);
} else if (o instanceof Float) {
return floatValue((Float) o);
} else if (o instanceof LocalDate) {
return dateValue((LocalDate) o);
} else if (o instanceof LocalDateTime) {
return datetimeValue((LocalDateTime) o);
} else if (o instanceof LocalTime) {
return timeValue((LocalTime) o);
} else if (o instanceof Instant) {
return timestampValue((Instant) o);
} else {
throw new ExpressionEvaluationException("unsupported object " + o.getClass());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

package org.opensearch.sql.expression.datetime;

import static java.time.temporal.ChronoUnit.DAYS;
import static java.time.temporal.ChronoUnit.MINUTES;
import static java.time.temporal.ChronoUnit.MONTHS;
import static org.opensearch.sql.data.type.ExprCoreType.DATE;
Expand All @@ -21,17 +22,20 @@
import static org.opensearch.sql.expression.function.FunctionDSL.impl;
import static org.opensearch.sql.expression.function.FunctionDSL.implWithProperties;
import static org.opensearch.sql.expression.function.FunctionDSL.nullMissingHandling;
import static org.opensearch.sql.expression.function.FunctionDSL.nullMissingHandlingWithProperties;
import static org.opensearch.sql.utils.DateTimeFormatters.DATE_FORMATTER_LONG_YEAR;
import static org.opensearch.sql.utils.DateTimeFormatters.DATE_FORMATTER_SHORT_YEAR;
import static org.opensearch.sql.utils.DateTimeFormatters.DATE_TIME_FORMATTER_LONG_YEAR;
import static org.opensearch.sql.utils.DateTimeFormatters.DATE_TIME_FORMATTER_SHORT_YEAR;
import static org.opensearch.sql.utils.DateTimeFormatters.DATE_TIME_FORMATTER_STRICT_WITH_TZ;
import static org.opensearch.sql.utils.DateTimeUtils.extractDate;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
Expand Down Expand Up @@ -97,6 +101,7 @@ public void register(BuiltinFunctionRepository repository) {
repository.register(current_time());
repository.register(current_timestamp());
repository.register(date());
repository.register(datediff());
repository.register(datetime());
repository.register(date_add());
repository.register(date_sub());
Expand Down Expand Up @@ -128,6 +133,7 @@ public void register(BuiltinFunctionRepository repository) {
repository.register(sysdate());
repository.register(time());
repository.register(time_to_sec());
repository.register(timediff());
repository.register(timestamp());
repository.register(utc_date());
repository.register(utc_time());
Expand Down Expand Up @@ -267,6 +273,46 @@ private DefaultFunctionResolver date() {
impl(nullMissingHandling(DateTimeFunction::exprDate), DATE, TIMESTAMP));
}

/*
* Calculates the difference of date part of given values.
* (DATE/DATETIME/TIMESTAMP/TIME, DATE/DATETIME/TIMESTAMP/TIME) -> LONG
*/
private DefaultFunctionResolver datediff() {
return define(BuiltinFunctionName.DATEDIFF.getName(),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
LONG, DATE, DATE),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
LONG, DATETIME, DATE),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
LONG, DATE, DATETIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
LONG, DATETIME, DATETIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
LONG, DATE, TIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
LONG, TIME, DATE),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
LONG, TIME, TIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
LONG, TIMESTAMP, DATE),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
LONG, DATE, TIMESTAMP),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
LONG, TIMESTAMP, TIMESTAMP),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
LONG, TIMESTAMP, TIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
LONG, TIME, TIMESTAMP),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
LONG, TIMESTAMP, DATETIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
LONG, DATETIME, TIMESTAMP),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
LONG, TIME, DATETIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff),
LONG, DATETIME, TIME));
}

/**
* Specify a datetime with time zone field and a time zone to convert to.
* Returns a local date time.
Expand Down Expand Up @@ -538,6 +584,22 @@ private DefaultFunctionResolver time() {
impl(nullMissingHandling(DateTimeFunction::exprTime), TIME, TIMESTAMP));
}

/**
* Returns different between two times as a time.
* (TIME, TIME) -> TIME
* MySQL has these signatures too
* (DATE, DATE) -> TIME // result is > 24 hours
* (DATETIME, DATETIME) -> TIME // result is > 24 hours
* (TIMESTAMP, TIMESTAMP) -> TIME // result is > 24 hours
* (x, x) -> NULL // when args have different types
* (STRING, STRING) -> TIME // argument strings contain same types only
* (STRING, STRING) -> NULL // argument strings are different types
*/
private DefaultFunctionResolver timediff() {
return define(BuiltinFunctionName.TIMEDIFF.getName(),
impl(nullMissingHandling(DateTimeFunction::exprTimeDiff), TIME, TIME, TIME));
}

/**
* TIME_TO_SEC(STRING/TIME/DATETIME/TIMESTAMP). return the time argument, converted to seconds.
*/
Expand Down Expand Up @@ -737,6 +799,22 @@ private ExprValue exprDate(ExprValue exprValue) {
}
}

/**
* Calculate the value in days from one date to the other.
* Only the date parts of the values are used in the calculation.
*
* @param first The first value.
* @param second The second value.
* @return The diff.
*/
private ExprValue exprDateDiff(FunctionProperties functionProperties,
ExprValue first, ExprValue second) {
// java inverses the value, so we have to swap 1 and 2
return new ExprLongValue(DAYS.between(
extractDate(second, functionProperties),
extractDate(first, functionProperties)));
}

/**
* DateTime implementation for ExprValue.
*
Expand Down Expand Up @@ -1096,6 +1174,19 @@ private ExprValue exprTime(ExprValue exprValue) {
}
}

/**
* Calculate the time difference between two times.
*
* @param first The first value.
* @param second The second value.
* @return The diff.
*/
private ExprValue exprTimeDiff(ExprValue first, ExprValue second) {
// java inverses the value, so we have to swap 1 and 2
return new ExprTimeValue(LocalTime.MIN.plus(
Duration.between(second.timeValue(), first.timeValue())));
}

/**
* Timestamp implementation for ExprValue.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public enum BuiltinFunctionName {
ADDDATE(FunctionName.of("adddate")),
CONVERT_TZ(FunctionName.of("convert_tz")),
DATE(FunctionName.of("date")),
DATEDIFF(FunctionName.of("datediff")),
DATETIME(FunctionName.of("datetime")),
DATE_ADD(FunctionName.of("date_add")),
DATE_SUB(FunctionName.of("date_sub")),
Expand All @@ -87,6 +88,7 @@ public enum BuiltinFunctionName {
SECOND(FunctionName.of("second")),
SUBDATE(FunctionName.of("subdate")),
TIME(FunctionName.of("time")),
TIMEDIFF(FunctionName.of("timediff")),
TIME_TO_SEC(FunctionName.of("time_to_sec")),
TIMESTAMP(FunctionName.of("timestamp")),
DATE_FORMAT(FunctionName.of("date_format")),
Expand Down
Loading