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

Add functions ADDTIME and SUBTIME. #132

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.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any chance you know about this TODO?

*/
private static final ZoneId ZONE = ZoneId.of("UTC");
public static final ZoneId ZONE = ZoneId.of("UTC");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps specify this is UTC_ZONE?


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 @@ -20,17 +20,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.extractDateTime;

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 @@ -63,6 +66,7 @@
import org.opensearch.sql.expression.function.DefaultFunctionResolver;
import org.opensearch.sql.expression.function.FunctionDSL;
import org.opensearch.sql.expression.function.FunctionName;
import org.opensearch.sql.expression.function.FunctionProperties;
import org.opensearch.sql.expression.function.FunctionResolver;
import org.opensearch.sql.utils.DateTimeUtils;

Expand All @@ -88,6 +92,7 @@ public class DateTimeFunction {
*/
public void register(BuiltinFunctionRepository repository) {
repository.register(adddate());
repository.register(addtime());
repository.register(convert_tz());
repository.register(curtime());
repository.register(curdate());
Expand Down Expand Up @@ -122,6 +127,7 @@ public void register(BuiltinFunctionRepository repository) {
repository.register(quarter());
repository.register(second());
repository.register(subdate());
repository.register(subtime());
repository.register(sysdate());
repository.register(time());
repository.register(time_to_sec());
Expand Down Expand Up @@ -233,6 +239,52 @@ private DefaultFunctionResolver adddate() {
return add_date(BuiltinFunctionName.ADDDATE.getName());
}

/**
* Adds expr2 to expr1 and returns the result.
* (TIME, TIME/DATE/DATETIME/TIMESTAMP) -> TIME
* (DATE/DATETIME/TIMESTAMP, TIME/DATE/DATETIME/TIMESTAMP) -> DATETIME
* TODO: MySQL has these signatures too

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we going to implement these other signatures in a separate PR or were they meant to be done here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In another PR

* (STRING, STRING/TIME) -> STRING // second arg - string with time only
* (x, STRING) -> NULL // second arg - string with timestamp
* (x, STRING/DATE) -> x // second arg - string with date only
*/
private DefaultFunctionResolver addtime() {
return define(BuiltinFunctionName.ADDTIME.getName(),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime),
TIME, TIME, TIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime),
TIME, TIME, DATE),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime),
TIME, TIME, DATETIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime),
TIME, TIME, TIMESTAMP),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime),
DATETIME, DATETIME, TIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime),
DATETIME, DATETIME, DATE),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime),
DATETIME, DATETIME, DATETIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime),
DATETIME, DATETIME, TIMESTAMP),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime),
DATETIME, DATE, TIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime),
DATETIME, DATE, DATE),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime),
DATETIME, DATE, DATETIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime),
DATETIME, DATE, TIMESTAMP),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime),
DATETIME, TIMESTAMP, TIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime),
DATETIME, TIMESTAMP, DATE),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime),
DATETIME, TIMESTAMP, DATETIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime),
DATETIME, TIMESTAMP, TIMESTAMP)
);
}

/**
* Converts date/time from a specified timezone to another specified timezone.
* The supported signatures:
Expand Down Expand Up @@ -505,6 +557,52 @@ private DefaultFunctionResolver subdate() {
return sub_date(BuiltinFunctionName.SUBDATE.getName());
}

/**
* Subtracts expr2 from expr1 and returns the result.
* (TIME, TIME/DATE/DATETIME/TIMESTAMP) -> TIME
* (DATE/DATETIME/TIMESTAMP, TIME/DATE/DATETIME/TIMESTAMP) -> DATETIME
* TODO: MySQL has these signatures too
* (STRING, STRING/TIME) -> STRING // second arg - string with time only
* (x, STRING) -> NULL // second arg - string with timestamp
* (x, STRING/DATE) -> x // second arg - string with date only
*/
private DefaultFunctionResolver subtime() {
return define(BuiltinFunctionName.SUBTIME.getName(),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime),
TIME, TIME, TIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime),
TIME, TIME, DATE),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime),
TIME, TIME, DATETIME),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this case match with SQL?

implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime),
TIME, TIME, TIMESTAMP),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime),
DATETIME, DATETIME, TIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime),
DATETIME, DATETIME, DATE),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime),
DATETIME, DATETIME, DATETIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime),
DATETIME, DATETIME, TIMESTAMP),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime),
DATETIME, DATE, TIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime),
DATETIME, DATE, DATE),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime),
DATETIME, DATE, DATETIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime),
DATETIME, DATE, TIMESTAMP),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime),
DATETIME, TIMESTAMP, TIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime),
DATETIME, TIMESTAMP, DATE),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime),
DATETIME, TIMESTAMP, DATETIME),
implWithProperties(nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime),
DATETIME, TIMESTAMP, TIMESTAMP)
);
}

/**
* Extracts the time part of a date and time value.
* Also to construct a time type. The supported signatures:
Expand Down Expand Up @@ -641,6 +739,39 @@ private ExprValue exprAddDateDays(ExprValue date, ExprValue days) {
: exprValue);
}

/**
* Adds or subtracts time to/from date and returns the result.
*
* @param functionProperties A FunctionProperties object.
* @param temporal A Date/Time/Datetime/Timestamp value to change.
* @param temporalDelta A Date/Time/Datetime/Timestamp object to add/subtract time from.
* @param isAdd A flag: true to add, false to subtract.
* @return A value calculated.
*/
private ExprValue exprApplyTime(FunctionProperties functionProperties,
ExprValue temporal, ExprValue temporalDelta, Boolean isAdd) {
var interval = Duration.between(LocalTime.MIN, temporalDelta.timeValue());
var result = isAdd
? extractDateTime(temporal, functionProperties).plus(interval)
: extractDateTime(temporal, functionProperties).minus(interval);
return temporal.type() == TIME
? new ExprTimeValue(result.toLocalTime())
: new ExprDatetimeValue(result);
}

/**
* Adds time to date and returns the result.
*
* @param functionProperties A FunctionProperties object.
* @param temporal A Date/Time/Datetime/Timestamp value to change.
* @param temporalDelta A Date/Time/Datetime/Timestamp object to add time from.
* @return A value calculated.
*/
private ExprValue exprAddTime(FunctionProperties functionProperties,
ExprValue temporal, ExprValue temporalDelta) {
return exprApplyTime(functionProperties, temporal, temporalDelta, true);
}

/**
* CONVERT_TZ function implementation for ExprValue.
* Returns null for time zones outside of +13:00 and -12:00.
Expand Down Expand Up @@ -1025,6 +1156,18 @@ private ExprValue exprSubDateInterval(ExprValue date, ExprValue expr) {
: exprValue);
}

/**
* Subtracts expr2 from expr1 and returns the result.
*
* @param temporal A Date/Time/Datetime/Timestamp value to change.
* @param temporalDelta A Date/Time/Datetime/Timestamp to subtract time from.
* @return A value calculated.
*/
private ExprValue exprSubTime(FunctionProperties functionProperties,
ExprValue temporal, ExprValue temporalDelta) {
return exprApplyTime(functionProperties, temporal, temporalDelta, false);
}

/**
* Time implementation for ExprValue.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public enum BuiltinFunctionName {
* Date and Time Functions.
*/
ADDDATE(FunctionName.of("adddate")),
ADDTIME(FunctionName.of("addtime")),
CONVERT_TZ(FunctionName.of("convert_tz")),
DATE(FunctionName.of("date")),
DATETIME(FunctionName.of("datetime")),
Expand All @@ -85,6 +86,7 @@ public enum BuiltinFunctionName {
QUARTER(FunctionName.of("quarter")),
SECOND(FunctionName.of("second")),
SUBDATE(FunctionName.of("subdate")),
SUBTIME(FunctionName.of("subtime")),
TIME(FunctionName.of("time")),
TIME_TO_SEC(FunctionName.of("time_to_sec")),
TIMESTAMP(FunctionName.of("timestamp")),
Expand Down
Loading