DateTimeExtractAndTransform.java
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.doris.nereids.trees.expressions.functions.executable;
import org.apache.doris.catalog.ScalarType;
import org.apache.doris.catalog.Type;
import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.rules.expression.rules.SupportJavaDateFormatter;
import org.apache.doris.nereids.trees.expressions.ExecFunction;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral;
import org.apache.doris.nereids.trees.expressions.literal.DateLiteral;
import org.apache.doris.nereids.trees.expressions.literal.DateTimeLiteral;
import org.apache.doris.nereids.trees.expressions.literal.DateTimeV2Literal;
import org.apache.doris.nereids.trees.expressions.literal.DateV2Literal;
import org.apache.doris.nereids.trees.expressions.literal.DecimalV3Literal;
import org.apache.doris.nereids.trees.expressions.literal.DoubleLiteral;
import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
import org.apache.doris.nereids.trees.expressions.literal.SmallIntLiteral;
import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
import org.apache.doris.nereids.trees.expressions.literal.StringLiteral;
import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral;
import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral;
import org.apache.doris.nereids.types.DataType;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.DateType;
import org.apache.doris.nereids.types.DateV2Type;
import org.apache.doris.nereids.types.DecimalV3Type;
import org.apache.doris.nereids.types.VarcharType;
import org.apache.doris.nereids.util.DateUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.DayOfWeek;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
import java.time.format.TextStyle;
import java.time.temporal.ChronoUnit;
import java.time.temporal.WeekFields;
import java.util.HashMap;
import java.util.Locale;
/**
* executable function:
* year, quarter, month, week, dayOfYear, dayOfweek, dayOfMonth, hour, minute, second, microsecond
*/
public class DateTimeExtractAndTransform {
private static final HashMap<String, Integer> DAY_OF_WEEK = new HashMap<>();
static {
DAY_OF_WEEK.put("MO", 1);
DAY_OF_WEEK.put("MON", 1);
DAY_OF_WEEK.put("MONDAY", 1);
DAY_OF_WEEK.put("TU", 2);
DAY_OF_WEEK.put("TUE", 2);
DAY_OF_WEEK.put("TUESDAY", 2);
DAY_OF_WEEK.put("WE", 3);
DAY_OF_WEEK.put("WED", 3);
DAY_OF_WEEK.put("WEDNESDAY", 3);
DAY_OF_WEEK.put("TH", 4);
DAY_OF_WEEK.put("THU", 4);
DAY_OF_WEEK.put("THURSDAY", 4);
DAY_OF_WEEK.put("FR", 5);
DAY_OF_WEEK.put("FRI", 5);
DAY_OF_WEEK.put("FRIDAY", 5);
DAY_OF_WEEK.put("SA", 6);
DAY_OF_WEEK.put("SAT", 6);
DAY_OF_WEEK.put("SATURDAY", 6);
DAY_OF_WEEK.put("SU", 7);
DAY_OF_WEEK.put("SUN", 7);
DAY_OF_WEEK.put("SUNDAY", 7);
}
/**
* datetime arithmetic function date-v2
*/
@ExecFunction(name = "datev2")
public static Expression dateV2(DateTimeV2Literal dateTime) {
return new DateV2Literal(dateTime.getYear(), dateTime.getMonth(), dateTime.getDay());
}
/**
* Executable datetime extract year
*/
@ExecFunction(name = "year")
public static Expression year(DateLiteral date) {
return new SmallIntLiteral(((short) date.getYear()));
}
@ExecFunction(name = "year")
public static Expression year(DateTimeLiteral date) {
return new SmallIntLiteral(((short) date.getYear()));
}
@ExecFunction(name = "year")
public static Expression year(DateV2Literal date) {
return new SmallIntLiteral(((short) date.getYear()));
}
@ExecFunction(name = "year")
public static Expression year(DateTimeV2Literal date) {
return new SmallIntLiteral(((short) date.getYear()));
}
/**
* Executable datetime extract quarter
*/
@ExecFunction(name = "quarter")
public static Expression quarter(DateLiteral date) {
return new TinyIntLiteral((byte) (((byte) date.getMonth() - 1) / 3 + 1));
}
@ExecFunction(name = "quarter")
public static Expression quarter(DateTimeLiteral date) {
return new TinyIntLiteral((byte) ((date.getMonth() - 1) / 3 + 1));
}
@ExecFunction(name = "quarter")
public static Expression quarter(DateV2Literal date) {
return new TinyIntLiteral((byte) ((date.getMonth() - 1) / 3 + 1));
}
@ExecFunction(name = "quarter")
public static Expression quarter(DateTimeV2Literal date) {
return new TinyIntLiteral((byte) ((date.getMonth() - 1) / 3 + 1));
}
/**
* Executable datetime extract month
*/
@ExecFunction(name = "month")
public static Expression month(DateLiteral date) {
return new TinyIntLiteral((byte) date.getMonth());
}
@ExecFunction(name = "month")
public static Expression month(DateTimeLiteral date) {
return new TinyIntLiteral((byte) date.getMonth());
}
@ExecFunction(name = "month")
public static Expression month(DateV2Literal date) {
return new TinyIntLiteral((byte) date.getMonth());
}
@ExecFunction(name = "month")
public static Expression month(DateTimeV2Literal date) {
return new TinyIntLiteral((byte) date.getMonth());
}
/**
* Executable datetime extract day
*/
@ExecFunction(name = "day")
public static Expression day(DateLiteral date) {
return new TinyIntLiteral((byte) date.getDay());
}
@ExecFunction(name = "day")
public static Expression day(DateTimeLiteral date) {
return new TinyIntLiteral((byte) date.getDay());
}
@ExecFunction(name = "day")
public static Expression day(DateV2Literal date) {
return new TinyIntLiteral((byte) date.getDay());
}
@ExecFunction(name = "day")
public static Expression day(DateTimeV2Literal date) {
return new TinyIntLiteral((byte) date.getDay());
}
/**
* Executable datetime extract hour
*/
@ExecFunction(name = "hour")
public static Expression hour(DateTimeLiteral date) {
return new TinyIntLiteral(((byte) date.getHour()));
}
@ExecFunction(name = "hour")
public static Expression hour(DateTimeV2Literal date) {
return new TinyIntLiteral(((byte) date.getHour()));
}
/**
* Executable datetime extract hour
*/
@ExecFunction(name = "minute")
public static Expression minute(DateTimeLiteral date) {
return new TinyIntLiteral(((byte) date.getMinute()));
}
@ExecFunction(name = "minute")
public static Expression minute(DateTimeV2Literal date) {
return new TinyIntLiteral(((byte) date.getMinute()));
}
/**
* Executable datetime extract second
*/
@ExecFunction(name = "second")
public static Expression second(DateTimeLiteral date) {
return new TinyIntLiteral(((byte) date.getSecond()));
}
@ExecFunction(name = "second")
public static Expression second(DateTimeV2Literal date) {
return new TinyIntLiteral(((byte) date.getSecond()));
}
/**
* Executable datetime extract microsecond
*/
@ExecFunction(name = "microsecond")
public static Expression microsecond(DateTimeV2Literal date) {
return new IntegerLiteral(((int) date.getMicroSecond()));
}
/**
* Executable datetime extract dayofyear
*/
@ExecFunction(name = "dayofyear")
public static Expression dayOfYear(DateLiteral date) {
return new SmallIntLiteral((short) date.getDayOfYear());
}
@ExecFunction(name = "dayofyear")
public static Expression dayOfYear(DateTimeLiteral date) {
return new SmallIntLiteral((short) date.getDayOfYear());
}
@ExecFunction(name = "dayofyear")
public static Expression dayOfYear(DateV2Literal date) {
return new SmallIntLiteral((short) date.getDayOfYear());
}
@ExecFunction(name = "dayofyear")
public static Expression dayOfYear(DateTimeV2Literal date) {
return new SmallIntLiteral((short) date.getDayOfYear());
}
/**
* Executable datetime extract dayofmonth
*/
@ExecFunction(name = "dayofmonth")
public static Expression dayOfMonth(DateLiteral date) {
return new TinyIntLiteral((byte) date.toJavaDateType().getDayOfMonth());
}
@ExecFunction(name = "dayofmonth")
public static Expression dayOfMonth(DateTimeLiteral date) {
return new TinyIntLiteral((byte) date.toJavaDateType().getDayOfMonth());
}
@ExecFunction(name = "dayofmonth")
public static Expression dayOfMonth(DateV2Literal date) {
return new TinyIntLiteral((byte) date.toJavaDateType().getDayOfMonth());
}
@ExecFunction(name = "dayofmonth")
public static Expression dayOfMonth(DateTimeV2Literal date) {
return new TinyIntLiteral((byte) date.toJavaDateType().getDayOfMonth());
}
/**
* Executable datetime extract dayofweek
*/
@ExecFunction(name = "dayofweek")
public static Expression dayOfWeek(DateLiteral date) {
return new TinyIntLiteral((byte) (date.getDayOfWeek() % 7 + 1));
}
@ExecFunction(name = "dayofweek")
public static Expression dayOfWeek(DateTimeLiteral date) {
return new TinyIntLiteral((byte) (date.getDayOfWeek() % 7 + 1));
}
@ExecFunction(name = "dayofweek")
public static Expression dayOfWeek(DateV2Literal date) {
return new TinyIntLiteral((byte) (date.getDayOfWeek() % 7 + 1));
}
@ExecFunction(name = "dayofweek")
public static Expression dayOfWeek(DateTimeV2Literal date) {
return new TinyIntLiteral((byte) (date.getDayOfWeek() % 7 + 1));
}
private static int distanceToFirstDayOfWeek(LocalDateTime dateTime) {
return dateTime.getDayOfWeek().getValue() - 1;
}
private static LocalDateTime firstDayOfWeek(LocalDateTime dateTime) {
return dateTime.plusDays(-distanceToFirstDayOfWeek(dateTime));
}
/**
* datetime arithmetic function date-format
*/
@ExecFunction(name = "date_format")
public static Expression dateFormat(DateLiteral date, StringLikeLiteral format) {
format = (StringLikeLiteral) SupportJavaDateFormatter.translateJavaFormatter(format);
return new VarcharLiteral(DateUtils.dateTimeFormatter(format.getValue()).format(
java.time.LocalDate.of(((int) date.getYear()), ((int) date.getMonth()), ((int) date.getDay()))));
}
@ExecFunction(name = "date_format")
public static Expression dateFormat(DateTimeLiteral date, StringLikeLiteral format) {
format = (StringLikeLiteral) SupportJavaDateFormatter.translateJavaFormatter(format);
return new VarcharLiteral(DateUtils.dateTimeFormatter(format.getValue()).format(
java.time.LocalDateTime.of(((int) date.getYear()), ((int) date.getMonth()), ((int) date.getDay()),
((int) date.getHour()), ((int) date.getMinute()), ((int) date.getSecond()))));
}
@ExecFunction(name = "date_format")
public static Expression dateFormat(DateV2Literal date, StringLikeLiteral format) {
format = (StringLikeLiteral) SupportJavaDateFormatter.translateJavaFormatter(format);
return new VarcharLiteral(DateUtils.dateTimeFormatter(format.getValue()).format(
java.time.LocalDate.of(((int) date.getYear()), ((int) date.getMonth()), ((int) date.getDay()))));
}
@ExecFunction(name = "date_format")
public static Expression dateFormat(DateTimeV2Literal date, StringLikeLiteral format) {
format = (StringLikeLiteral) SupportJavaDateFormatter.translateJavaFormatter(format);
return new VarcharLiteral(DateUtils.dateTimeFormatter(format.getValue()).format(
java.time.LocalDateTime.of(((int) date.getYear()), ((int) date.getMonth()), ((int) date.getDay()),
((int) date.getHour()), ((int) date.getMinute()), ((int) date.getSecond()))));
}
/**
* datetime arithmetic function date
*/
@ExecFunction(name = "date")
public static Expression date(DateTimeLiteral dateTime) throws AnalysisException {
return new DateLiteral(dateTime.getYear(), dateTime.getMonth(), dateTime.getDay());
}
@ExecFunction(name = "date")
public static Expression date(DateTimeV2Literal dateTime) throws AnalysisException {
return new DateV2Literal(dateTime.getYear(), dateTime.getMonth(), dateTime.getDay());
}
/**
* datetime arithmetic function date-trunc
*/
@ExecFunction(name = "date_trunc")
public static Expression dateTrunc(DateTimeLiteral date, StringLikeLiteral trunc) {
return DateTimeLiteral.fromJavaDateType(dateTruncHelper(date.toJavaDateType(), trunc.getValue()));
}
@ExecFunction(name = "date_trunc")
public static Expression dateTrunc(DateTimeV2Literal date, StringLikeLiteral trunc) {
return DateTimeV2Literal.fromJavaDateType(dateTruncHelper(date.toJavaDateType(), trunc.getValue()));
}
@ExecFunction(name = "date_trunc")
public static Expression dateTrunc(DateLiteral date, StringLikeLiteral trunc) {
return DateLiteral.fromJavaDateType(dateTruncHelper(date.toJavaDateType(), trunc.getValue()));
}
@ExecFunction(name = "date_trunc")
public static Expression dateTrunc(DateV2Literal date, StringLikeLiteral trunc) {
return DateV2Literal.fromJavaDateType(dateTruncHelper(date.toJavaDateType(), trunc.getValue()));
}
@ExecFunction(name = "date_trunc")
public static Expression dateTrunc(StringLikeLiteral trunc, DateTimeLiteral date) {
return DateTimeLiteral.fromJavaDateType(dateTruncHelper(date.toJavaDateType(), trunc.getValue()));
}
@ExecFunction(name = "date_trunc")
public static Expression dateTrunc(StringLikeLiteral trunc, DateTimeV2Literal date) {
return DateTimeV2Literal.fromJavaDateType(dateTruncHelper(date.toJavaDateType(), trunc.getValue()));
}
@ExecFunction(name = "date_trunc")
public static Expression dateTrunc(StringLikeLiteral trunc, DateLiteral date) {
return DateLiteral.fromJavaDateType(dateTruncHelper(date.toJavaDateType(), trunc.getValue()));
}
@ExecFunction(name = "date_trunc")
public static Expression dateTrunc(StringLikeLiteral trunc, DateV2Literal date) {
return DateV2Literal.fromJavaDateType(dateTruncHelper(date.toJavaDateType(), trunc.getValue()));
}
private static LocalDateTime dateTruncHelper(LocalDateTime dateTime, String trunc) {
int year = dateTime.getYear();
int month = dateTime.getMonthValue();
int day = dateTime.getDayOfMonth();
int hour = dateTime.getHour();
int minute = dateTime.getMinute();
int second = dateTime.getSecond();
switch (trunc.toLowerCase()) {
case "year":
month = 0;
case "quarter": // CHECKSTYLE IGNORE THIS LINE
month = ((month - 1) / 3) * 3 + 1;
case "month": // CHECKSTYLE IGNORE THIS LINE
day = 1;
break;
case "week":
LocalDateTime firstDayOfWeek = firstDayOfWeek(dateTime);
year = firstDayOfWeek.getYear();
month = firstDayOfWeek.getMonthValue();
day = firstDayOfWeek.getDayOfMonth();
default: // CHECKSTYLE IGNORE THIS LINE
break;
}
switch (trunc.toLowerCase()) {
case "year":
case "quarter":
case "month":
case "week":
case "day": // CHECKSTYLE IGNORE THIS LINE
hour = 0;
case "hour": // CHECKSTYLE IGNORE THIS LINE
minute = 0;
case "minute": // CHECKSTYLE IGNORE THIS LINE
second = 0;
default: // CHECKSTYLE IGNORE THIS LINE
}
return LocalDateTime.of(year, month, day, hour, minute, second);
}
/**
* from_days.
*/
@ExecFunction(name = "from_days")
public static Expression fromDays(IntegerLiteral n) {
// doris treat 0000AD as ordinary year but java LocalDateTime treat it as lunar year.
LocalDateTime res = LocalDateTime.of(0, 1, 1, 0, 0, 0)
.plusDays(n.getValue());
if (res.isBefore(LocalDateTime.of(0, 3, 1, 0, 0, 0))) {
res = res.plusDays(-1);
}
return DateV2Literal.fromJavaDateType(res);
}
@ExecFunction(name = "last_day")
public static Expression lastDay(DateLiteral date) {
LocalDateTime nextMonthFirstDay = LocalDateTime.of((int) date.getYear(), (int) date.getMonth(), 1,
0, 0, 0).plusMonths(1);
return DateLiteral.fromJavaDateType(nextMonthFirstDay.minusDays(1));
}
@ExecFunction(name = "last_day")
public static Expression lastDay(DateTimeLiteral date) {
LocalDateTime nextMonthFirstDay = LocalDateTime.of((int) date.getYear(), (int) date.getMonth(), 1,
0, 0, 0).plusMonths(1);
return DateLiteral.fromJavaDateType(nextMonthFirstDay.minusDays(1));
}
@ExecFunction(name = "last_day")
public static Expression lastDay(DateV2Literal date) {
LocalDateTime nextMonthFirstDay = LocalDateTime.of((int) date.getYear(), (int) date.getMonth(), 1,
0, 0, 0).plusMonths(1);
return DateV2Literal.fromJavaDateType(nextMonthFirstDay.minusDays(1));
}
@ExecFunction(name = "last_day")
public static Expression lastDay(DateTimeV2Literal date) {
LocalDateTime nextMonthFirstDay = LocalDateTime.of((int) date.getYear(), (int) date.getMonth(), 1,
0, 0, 0).plusMonths(1);
return DateV2Literal.fromJavaDateType(nextMonthFirstDay.minusDays(1));
}
/**
* datetime transformation function: to_monday
*/
@ExecFunction(name = "to_monday")
public static Expression toMonday(DateLiteral date) {
return DateLiteral.fromJavaDateType(toMonday(date.toJavaDateType()));
}
@ExecFunction(name = "to_monday")
public static Expression toMonday(DateTimeLiteral date) {
return DateLiteral.fromJavaDateType(toMonday(date.toJavaDateType()));
}
@ExecFunction(name = "to_monday")
public static Expression toMonday(DateV2Literal date) {
return DateV2Literal.fromJavaDateType(toMonday(date.toJavaDateType()));
}
@ExecFunction(name = "to_monday")
public static Expression toMonday(DateTimeV2Literal date) {
return DateV2Literal.fromJavaDateType(toMonday(date.toJavaDateType()));
}
private static LocalDateTime toMonday(LocalDateTime dateTime) {
LocalDateTime specialUpperBound = LocalDateTime.of(1970, 1, 4, 23, 59, 59, 999_999_999);
LocalDateTime specialLowerBound = LocalDateTime.of(1970, 1, 1, 0, 0, 0);
if (dateTime.isAfter(specialUpperBound) || dateTime.isBefore(specialLowerBound)) {
return dateTime.plusDays(-dateTime.getDayOfWeek().getValue() + 1);
}
return specialLowerBound;
}
/**
* date transformation function: from_unixtime
*/
@ExecFunction(name = "from_unixtime")
public static Expression fromUnixTime(BigIntLiteral second) {
return fromUnixTime(second, new VarcharLiteral("%Y-%m-%d %H:%i:%s"));
}
/**
* date transformation function: from_unixtime
*/
@ExecFunction(name = "from_unixtime")
public static Expression fromUnixTime(BigIntLiteral second, StringLikeLiteral format) {
format = (StringLikeLiteral) SupportJavaDateFormatter.translateJavaFormatter(format);
// 32536771199L is max valid timestamp of mysql from_unix_time
if (second.getValue() < 0 || second.getValue() > 32536771199L) {
return new NullLiteral(VarcharType.SYSTEM_DEFAULT);
}
ZonedDateTime dateTime = LocalDateTime.of(1970, 1, 1, 0, 0, 0)
.plusSeconds(second.getValue())
.atZone(ZoneId.of("UTC+0"))
.toOffsetDateTime()
.atZoneSameInstant(DateUtils.getTimeZone());
return dateFormat(new DateTimeLiteral(dateTime.getYear(), dateTime.getMonthValue(),
dateTime.getDayOfMonth(), dateTime.getHour(), dateTime.getMinute(), dateTime.getSecond()),
format);
}
/**
* date transformation function: unix_timestamp
*/
@ExecFunction(name = "unix_timestamp")
public static Expression unixTimestamp(DateLiteral date) {
return new IntegerLiteral(Integer.parseInt(getTimestamp(date.toJavaDateType())));
}
@ExecFunction(name = "unix_timestamp")
public static Expression unixTimestamp(DateTimeLiteral date) {
return new IntegerLiteral(Integer.parseInt(getTimestamp(date.toJavaDateType())));
}
@ExecFunction(name = "unix_timestamp")
public static Expression unixTimestamp(DateV2Literal date) {
return new IntegerLiteral(Integer.parseInt(getTimestamp(date.toJavaDateType())));
}
/**
* date transformation function: unix_timestamp
*/
@ExecFunction(name = "unix_timestamp")
public static Expression unixTimestamp(DateTimeV2Literal date) {
if (date.getMicroSecond() == 0) {
return new DecimalV3Literal(DecimalV3Type.createDecimalV3TypeLooseCheck(10, 0),
new BigDecimal(getTimestamp(date.toJavaDateType())));
}
int scale = date.getDataType().getScale();
return new DecimalV3Literal(DecimalV3Type.createDecimalV3TypeLooseCheck(10 + scale, scale),
new BigDecimal(getTimestamp(date.toJavaDateType())));
}
/**
* date transformation function: unix_timestamp
*/
@ExecFunction(name = "unix_timestamp")
public static Expression unixTimestamp(StringLikeLiteral date, StringLikeLiteral format) {
format = (StringLikeLiteral) SupportJavaDateFormatter.translateJavaFormatter(format);
DateTimeFormatter formatter = DateUtils.dateTimeFormatter(format.getValue());
LocalDateTime dateObj;
try {
dateObj = LocalDateTime.parse(date.getValue(), formatter);
} catch (DateTimeParseException e) {
// means the date string doesn't contain time fields.
dateObj = LocalDate.parse(date.getValue(), formatter).atStartOfDay();
}
return new DecimalV3Literal(DecimalV3Type.createDecimalV3TypeLooseCheck(16, 6),
new BigDecimal(getTimestamp(dateObj)));
}
private static String getTimestamp(LocalDateTime dateTime) {
LocalDateTime specialUpperBound = LocalDateTime.of(2038, 1, 19, 3, 14, 7);
LocalDateTime specialLowerBound = LocalDateTime.of(1970, 1, 1, 0, 0, 0);
dateTime = dateTime.atZone(DateUtils.getTimeZone())
.toOffsetDateTime().atZoneSameInstant(ZoneId.of("UTC+0"))
.toLocalDateTime();
if (dateTime.isBefore(specialLowerBound) || dateTime.isAfter(specialUpperBound)) {
return "0";
}
Duration duration = Duration.between(
specialLowerBound,
dateTime
);
if (duration.getNano() == 0) {
return String.valueOf(duration.getSeconds());
} else {
return duration.getSeconds() + "." + (duration.getNano() / 1000);
}
}
/**
* date transformation function: to_date
*/
@ExecFunction(name = "to_date")
public static Expression toDate(DateTimeLiteral date) {
return new DateLiteral(date.getYear(), date.getMonth(), date.getDay());
}
@ExecFunction(name = "to_date")
public static Expression toDate(DateTimeV2Literal date) {
return new DateV2Literal(date.getYear(), date.getMonth(), date.getDay());
}
/**
* date transformation function: to_days
*/
@ExecFunction(name = "to_days")
public static Expression toDays(DateLiteral date) {
return new IntegerLiteral(((int) Duration.between(
LocalDateTime.of(0, 1, 1, 0, 0, 0), date.toJavaDateType()).toDays()));
}
@ExecFunction(name = "to_days")
public static Expression toDays(DateTimeLiteral date) {
return new IntegerLiteral(((int) Duration.between(
LocalDateTime.of(0, 1, 1, 0, 0, 0), date.toJavaDateType()).toDays()));
}
@ExecFunction(name = "to_days")
public static Expression toDays(DateV2Literal date) {
return new IntegerLiteral(((int) Duration.between(
LocalDateTime.of(0, 1, 1, 0, 0, 0), date.toJavaDateType()).toDays()));
}
@ExecFunction(name = "to_days")
public static Expression toDays(DateTimeV2Literal date) {
return new IntegerLiteral(((int) Duration.between(
LocalDateTime.of(0, 1, 1, 0, 0, 0), date.toJavaDateType()).toDays()));
}
/**
* date transformation function: makedate
*/
@ExecFunction(name = "makedate")
public static Expression makeDate(IntegerLiteral year, IntegerLiteral dayOfYear) {
int day = dayOfYear.getValue();
return day > 0 ? DateLiteral.fromJavaDateType(LocalDateTime.of(year.getValue(), 1, 1, 0, 0, 0)
.plusDays(day - 1)) : new NullLiteral(DateType.INSTANCE);
}
/**
* date transformation function: str_to_date
*/
@ExecFunction(name = "str_to_date")
public static Expression strToDate(StringLikeLiteral str, StringLikeLiteral format) {
format = (StringLikeLiteral) SupportJavaDateFormatter.translateJavaFormatter(format);
if (org.apache.doris.analysis.DateLiteral.hasTimePart(format.getStringValue())) {
DataType returnType = DataType.fromCatalogType(ScalarType.getDefaultDateType(Type.DATETIME));
if (returnType instanceof DateTimeV2Type) {
boolean hasMicroPart = org.apache.doris.analysis.DateLiteral
.hasMicroSecondPart(format.getStringValue());
return DateTimeV2Literal.fromJavaDateType(DateUtils.getTime(DateUtils
.dateTimeFormatter(format.getValue()), str.getValue()), hasMicroPart ? 6 : 0);
} else {
return DateTimeLiteral.fromJavaDateType(DateUtils.getTime(DateUtils
.dateTimeFormatter(format.getValue()), str.getValue()));
}
} else {
DataType returnType = DataType.fromCatalogType(ScalarType.getDefaultDateType(Type.DATE));
if (returnType instanceof DateV2Type) {
return DateV2Literal.fromJavaDateType(DateUtils.getTime(DateUtils.dateTimeFormatter(format.getValue()),
str.getValue()));
} else {
return DateLiteral.fromJavaDateType(DateUtils.getTime(DateUtils.dateTimeFormatter(format.getValue()),
str.getValue()));
}
}
}
@ExecFunction(name = "timestamp")
public static Expression timestamp(DateTimeLiteral datetime) {
return datetime;
}
@ExecFunction(name = "timestamp")
public static Expression timestamp(DateTimeV2Literal datetime) {
return datetime;
}
/**
* convert_tz
*/
@ExecFunction(name = "convert_tz")
public static Expression convertTz(DateTimeV2Literal datetime, StringLikeLiteral fromTz, StringLikeLiteral toTz) {
DateTimeFormatter zoneFormatter = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendZoneOrOffsetId()
.toFormatter()
.withResolverStyle(ResolverStyle.STRICT);
ZoneId fromZone = ZoneId.from(zoneFormatter.parse(fromTz.getStringValue()));
ZoneId toZone = ZoneId.from(zoneFormatter.parse(toTz.getStringValue()));
LocalDateTime localDateTime = datetime.toJavaDateType();
ZonedDateTime resultDateTime = localDateTime.atZone(fromZone).withZoneSameInstant(toZone);
return DateTimeV2Literal.fromJavaDateType(resultDateTime.toLocalDateTime(), datetime.getDataType().getScale());
}
@ExecFunction(name = "weekday")
public static Expression weekDay(DateLiteral date) {
return new TinyIntLiteral((byte) ((date.toJavaDateType().getDayOfWeek().getValue() + 6) % 7));
}
@ExecFunction(name = "weekday")
public static Expression weekDay(DateTimeLiteral date) {
return new TinyIntLiteral((byte) ((date.toJavaDateType().getDayOfWeek().getValue() + 6) % 7));
}
@ExecFunction(name = "weekday")
public static Expression weekDay(DateV2Literal date) {
return new TinyIntLiteral((byte) ((date.toJavaDateType().getDayOfWeek().getValue() + 6) % 7));
}
@ExecFunction(name = "weekday")
public static Expression weekDay(DateTimeV2Literal date) {
return new TinyIntLiteral((byte) ((date.toJavaDateType().getDayOfWeek().getValue() + 6) % 7));
}
@ExecFunction(name = "week")
public static Expression week(DateTimeV2Literal dateTime) {
return week(dateTime.toJavaDateType(), 0);
}
@ExecFunction(name = "week")
public static Expression week(DateTimeV2Literal dateTime, IntegerLiteral mode) {
return week(dateTime.toJavaDateType(), mode.getIntValue());
}
@ExecFunction(name = "week")
public static Expression week(DateTimeLiteral dateTime) {
return week(dateTime.toJavaDateType(), 0);
}
@ExecFunction(name = "week")
public static Expression week(DateTimeLiteral dateTime, IntegerLiteral mode) {
return week(dateTime.toJavaDateType(), mode.getIntValue());
}
@ExecFunction(name = "week")
public static Expression week(DateV2Literal date) {
return week(date.toJavaDateType(), 0);
}
@ExecFunction(name = "week")
public static Expression week(DateV2Literal date, IntegerLiteral mode) {
return week(date.toJavaDateType(), mode.getIntValue());
}
/**
* the impl of function week(date/datetime, mode)
*/
public static Expression week(LocalDateTime localDateTime, int mode) {
final byte[] resultOfFirstDayBC1 = new byte[] { 1, 0, 1, 52, 1, 0, 1, 52 };
if (isSpecificDate(localDateTime) && mode >= 0 && mode <= 7) { // 0000-01-01/02
if (localDateTime.getDayOfMonth() == 1) {
return new TinyIntLiteral(resultOfFirstDayBC1[mode]);
} else { // 0001-01-02
return new TinyIntLiteral((byte) 1);
}
}
switch (mode) {
case 0: {
return new TinyIntLiteral(
(byte) localDateTime.get(WeekFields.of(DayOfWeek.SUNDAY, 7).weekOfYear()));
}
case 1: {
return new TinyIntLiteral((byte) localDateTime.get(WeekFields.ISO.weekOfYear()));
}
case 2: {
return new TinyIntLiteral(
(byte) localDateTime.get(WeekFields.of(DayOfWeek.SUNDAY, 7).weekOfWeekBasedYear()));
}
case 3: {
return new TinyIntLiteral(
(byte) localDateTime.get(WeekFields.ISO.weekOfWeekBasedYear()));
}
case 4: {
return new TinyIntLiteral((byte) localDateTime
.get(WeekFields.of(DayOfWeek.SUNDAY, 4).weekOfYear()));
}
case 5: {
return new TinyIntLiteral((byte) localDateTime
.get(WeekFields.of(DayOfWeek.MONDAY, 7).weekOfYear()));
}
case 6: {
return new TinyIntLiteral((byte) localDateTime
.get(WeekFields.of(DayOfWeek.SUNDAY, 4).weekOfWeekBasedYear()));
}
case 7: {
return new TinyIntLiteral((byte) localDateTime
.get(WeekFields.of(DayOfWeek.MONDAY, 7).weekOfWeekBasedYear()));
}
default: {
throw new AnalysisException(
String.format("unknown mode %d in week function", mode));
}
}
}
/**
* 0000-01-01/02 are specific dates, sometime need handle them alone.
*/
private static boolean isSpecificDate(LocalDateTime localDateTime) {
return localDateTime.getYear() == 0 && localDateTime.getMonthValue() == 1
&& (localDateTime.getDayOfMonth() == 1 || localDateTime.getDayOfMonth() == 2);
}
@ExecFunction(name = "yearweek")
public static Expression yearWeek(DateV2Literal date, IntegerLiteral mode) {
return yearWeek(date.toJavaDateType(), mode.getIntValue());
}
@ExecFunction(name = "yearweek")
public static Expression yearWeek(DateTimeV2Literal dateTime, IntegerLiteral mode) {
return yearWeek(dateTime.toJavaDateType(), mode.getIntValue());
}
@ExecFunction(name = "yearweek")
public static Expression yearWeek(DateTimeLiteral dateTime, IntegerLiteral mode) {
return yearWeek(dateTime.toJavaDateType(), mode.getIntValue());
}
@ExecFunction(name = "yearweek")
public static Expression yearWeek(DateV2Literal date) {
return yearWeek(date.toJavaDateType(), 0);
}
@ExecFunction(name = "yearweek")
public static Expression yearWeek(DateTimeV2Literal dateTime) {
return yearWeek(dateTime.toJavaDateType(), 0);
}
@ExecFunction(name = "yearweek")
public static Expression yearWeek(DateTimeLiteral dateTime) {
return yearWeek(dateTime.toJavaDateType(), 0);
}
/**
* the impl of function yearWeek(date/datetime, mode)
*/
public static Expression yearWeek(LocalDateTime localDateTime, int mode) {
if (localDateTime.getYear() == 0) {
return week(localDateTime, mode);
}
switch (mode) {
case 0: {
return new IntegerLiteral(
localDateTime.get(WeekFields.of(DayOfWeek.SUNDAY, 7).weekBasedYear()) * 100
+ localDateTime.get(
WeekFields.of(DayOfWeek.SUNDAY, 7).weekOfWeekBasedYear()));
}
case 1: {
return new IntegerLiteral(localDateTime.get(WeekFields.ISO.weekBasedYear()) * 100
+ localDateTime.get(WeekFields.ISO.weekOfWeekBasedYear()));
}
case 2: {
return new IntegerLiteral(
localDateTime.get(WeekFields.of(DayOfWeek.SUNDAY, 7).weekBasedYear()) * 100
+ localDateTime.get(
WeekFields.of(DayOfWeek.SUNDAY, 7).weekOfWeekBasedYear()));
}
case 3: {
return new IntegerLiteral(localDateTime.get(WeekFields.ISO.weekBasedYear()) * 100
+ localDateTime.get(WeekFields.ISO.weekOfWeekBasedYear()));
}
case 4: {
return new IntegerLiteral(
localDateTime.get(WeekFields.of(DayOfWeek.SUNDAY, 4).weekBasedYear()) * 100
+ localDateTime
.get(WeekFields.of(DayOfWeek.SUNDAY, 4).weekOfWeekBasedYear()));
}
case 5: {
return new IntegerLiteral(
localDateTime.get(WeekFields.of(DayOfWeek.MONDAY, 7).weekBasedYear()) * 100
+ localDateTime
.get(WeekFields.of(DayOfWeek.MONDAY, 7).weekOfWeekBasedYear()));
}
case 6: {
return new IntegerLiteral(
localDateTime.get(WeekFields.of(DayOfWeek.SUNDAY, 4).weekBasedYear()) * 100
+ localDateTime.get(
WeekFields.of(DayOfWeek.SUNDAY, 4).weekOfWeekBasedYear()));
}
case 7: {
return new IntegerLiteral(
localDateTime.get(WeekFields.of(DayOfWeek.MONDAY, 7).weekBasedYear()) * 100
+ localDateTime.get(
WeekFields.of(DayOfWeek.MONDAY, 7).weekOfWeekBasedYear()));
}
default: {
throw new AnalysisException(
String.format("unknown mode %d in yearweek function", mode));
}
}
}
/**
* weekofyear
*/
@ExecFunction(name = "weekofyear")
public static Expression weekOfYear(DateTimeV2Literal dateTime) {
if (dateTime.getYear() == 0 && dateTime.getDayOfWeek() == 1) {
if (dateTime.getMonth() == 1 && dateTime.getDay() == 2) {
return new TinyIntLiteral((byte) 1);
}
return new TinyIntLiteral(
(byte) (dateTime.toJavaDateType().get(WeekFields.ISO.weekOfWeekBasedYear()) + 1));
}
return new TinyIntLiteral((byte) dateTime.toJavaDateType().get(WeekFields.ISO.weekOfWeekBasedYear()));
}
/**
* weekofyear
*/
@ExecFunction(name = "weekofyear")
public static Expression weekOfYear(DateTimeLiteral dateTime) {
if (dateTime.getYear() == 0 && dateTime.getDayOfWeek() == 1) {
if (dateTime.getMonth() == 1 && dateTime.getDay() == 2) {
return new TinyIntLiteral((byte) 1);
}
return new TinyIntLiteral(
(byte) (dateTime.toJavaDateType().get(WeekFields.ISO.weekOfWeekBasedYear()) + 1));
}
return new TinyIntLiteral((byte) dateTime.toJavaDateType().get(WeekFields.ISO.weekOfWeekBasedYear()));
}
/**
* weekofyear
*/
@ExecFunction(name = "weekofyear")
public static Expression weekOfYear(DateV2Literal date) {
if (date.getYear() == 0 && date.getDayOfWeek() == 1) {
if (date.getMonth() == 1 && date.getDay() == 2) {
return new TinyIntLiteral((byte) 1);
}
return new TinyIntLiteral((byte) (date.toJavaDateType().get(WeekFields.ISO.weekOfWeekBasedYear()) + 1));
}
return new TinyIntLiteral((byte) date.toJavaDateType().get(WeekFields.ISO.weekOfWeekBasedYear()));
}
@ExecFunction(name = "dayname")
public static Expression dayName(DateTimeV2Literal dateTime) {
return new VarcharLiteral(dateTime.toJavaDateType().getDayOfWeek().getDisplayName(TextStyle.FULL,
Locale.getDefault()));
}
@ExecFunction(name = "dayname")
public static Expression dayName(DateTimeLiteral dateTime) {
return new VarcharLiteral(dateTime.toJavaDateType().getDayOfWeek().getDisplayName(TextStyle.FULL,
Locale.getDefault()));
}
@ExecFunction(name = "dayname")
public static Expression dayName(DateV2Literal date) {
return new VarcharLiteral(date.toJavaDateType().getDayOfWeek().getDisplayName(TextStyle.FULL,
Locale.getDefault()));
}
@ExecFunction(name = "monthname")
public static Expression monthName(DateTimeV2Literal dateTime) {
return new VarcharLiteral(dateTime.toJavaDateType().getMonth().getDisplayName(TextStyle.FULL,
Locale.getDefault()));
}
@ExecFunction(name = "monthname")
public static Expression monthName(DateTimeLiteral dateTime) {
return new VarcharLiteral(dateTime.toJavaDateType().getMonth().getDisplayName(TextStyle.FULL,
Locale.getDefault()));
}
@ExecFunction(name = "monthname")
public static Expression monthName(DateV2Literal date) {
return new VarcharLiteral(date.toJavaDateType().getMonth().getDisplayName(TextStyle.FULL,
Locale.getDefault()));
}
@ExecFunction(name = "from_second")
public static Expression fromSecond(BigIntLiteral second) {
return fromMicroSecond(second.getValue() * 1000 * 1000);
}
@ExecFunction(name = "from_millisecond")
public static Expression fromMilliSecond(BigIntLiteral milliSecond) {
return fromMicroSecond(milliSecond.getValue() * 1000);
}
@ExecFunction(name = "from_microsecond")
public static Expression fromMicroSecond(BigIntLiteral microSecond) {
return fromMicroSecond(microSecond.getValue());
}
private static Expression fromMicroSecond(long microSecond) {
if (microSecond < 0 || microSecond > 253402271999999999L) {
return new NullLiteral(DateTimeV2Type.SYSTEM_DEFAULT);
}
LocalDateTime dateTime = LocalDateTime.ofInstant(
Instant.ofEpochMilli(microSecond / 1000).plusNanos(microSecond % 1000 * 1000),
DateUtils.getTimeZone());
return new DateTimeV2Literal(DateTimeV2Type.MAX, dateTime.getYear(),
dateTime.getMonthValue(), dateTime.getDayOfMonth(), dateTime.getHour(),
dateTime.getMinute(), dateTime.getSecond(), dateTime.getNano() / 1000);
}
@ExecFunction(name = "microseconds_diff")
public static Expression microsecondsDiff(DateTimeV2Literal t1, DateTimeV2Literal t2) {
return new BigIntLiteral(ChronoUnit.MICROS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "milliseconds_diff")
public static Expression millisecondsDiff(DateTimeV2Literal t1, DateTimeV2Literal t2) {
return new BigIntLiteral(ChronoUnit.MILLIS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "seconds_diff")
public static Expression secondsDiff(DateTimeV2Literal t1, DateTimeV2Literal t2) {
return new BigIntLiteral(ChronoUnit.SECONDS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "seconds_diff")
public static Expression secondsDiff(DateTimeV2Literal t1, DateV2Literal t2) {
return new BigIntLiteral(ChronoUnit.SECONDS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "seconds_diff")
public static Expression secondsDiff(DateV2Literal t1, DateTimeV2Literal t2) {
return new BigIntLiteral(ChronoUnit.SECONDS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "seconds_diff")
public static Expression secondsDiff(DateV2Literal t1, DateV2Literal t2) {
return new BigIntLiteral(ChronoUnit.SECONDS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "seconds_diff")
public static Expression secondsDiff(DateTimeLiteral t1, DateTimeLiteral t2) {
return new BigIntLiteral(ChronoUnit.SECONDS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "minutes_diff")
public static Expression minutesDiff(DateTimeV2Literal t1, DateTimeV2Literal t2) {
return new BigIntLiteral(ChronoUnit.MINUTES.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "minutes_diff")
public static Expression minutesDiff(DateTimeV2Literal t1, DateV2Literal t2) {
return new BigIntLiteral(ChronoUnit.MINUTES.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "minutes_diff")
public static Expression minutesDiff(DateV2Literal t1, DateTimeV2Literal t2) {
return new BigIntLiteral(ChronoUnit.MINUTES.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "minutes_diff")
public static Expression minutesDiff(DateV2Literal t1, DateV2Literal t2) {
return new BigIntLiteral(ChronoUnit.MINUTES.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "minutes_diff")
public static Expression minutesDiff(DateTimeLiteral t1, DateTimeLiteral t2) {
return new BigIntLiteral(ChronoUnit.MINUTES.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "hours_diff")
public static Expression hoursDiff(DateTimeV2Literal t1, DateTimeV2Literal t2) {
return new BigIntLiteral(ChronoUnit.HOURS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "hours_diff")
public static Expression hoursDiff(DateTimeV2Literal t1, DateV2Literal t2) {
return new BigIntLiteral(ChronoUnit.HOURS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "hours_diff")
public static Expression hoursDiff(DateV2Literal t1, DateTimeV2Literal t2) {
return new BigIntLiteral(ChronoUnit.HOURS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "hours_diff")
public static Expression hoursDiff(DateV2Literal t1, DateV2Literal t2) {
return new BigIntLiteral(ChronoUnit.HOURS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "hours_diff")
public static Expression hoursDiff(DateTimeLiteral t1, DateTimeLiteral t2) {
return new BigIntLiteral(ChronoUnit.HOURS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "days_diff")
public static Expression daysDiff(DateTimeV2Literal t1, DateTimeV2Literal t2) {
return new BigIntLiteral(ChronoUnit.DAYS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "days_diff")
public static Expression daysDiff(DateTimeV2Literal t1, DateV2Literal t2) {
return new BigIntLiteral(ChronoUnit.DAYS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "days_diff")
public static Expression daysDiff(DateV2Literal t1, DateTimeV2Literal t2) {
return new BigIntLiteral(ChronoUnit.DAYS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "days_diff")
public static Expression daysDiff(DateV2Literal t1, DateV2Literal t2) {
return new BigIntLiteral(ChronoUnit.DAYS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "days_diff")
public static Expression daysDiff(DateTimeLiteral t1, DateTimeLiteral t2) {
return new BigIntLiteral(ChronoUnit.DAYS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "weeks_diff")
public static Expression weeksDiff(DateTimeV2Literal t1, DateTimeV2Literal t2) {
return new BigIntLiteral(ChronoUnit.WEEKS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "weeks_diff")
public static Expression weeksDiff(DateTimeV2Literal t1, DateV2Literal t2) {
return new BigIntLiteral(ChronoUnit.WEEKS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "weeks_diff")
public static Expression weeksDiff(DateV2Literal t1, DateTimeV2Literal t2) {
return new BigIntLiteral(ChronoUnit.WEEKS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "weeks_diff")
public static Expression weeksDiff(DateV2Literal t1, DateV2Literal t2) {
return new BigIntLiteral(ChronoUnit.WEEKS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "weeks_diff")
public static Expression weeksDiff(DateTimeLiteral t1, DateTimeLiteral t2) {
return new BigIntLiteral(ChronoUnit.WEEKS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "months_diff")
public static Expression monthsDiff(DateTimeV2Literal t1, DateTimeV2Literal t2) {
return new BigIntLiteral(ChronoUnit.MONTHS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "months_diff")
public static Expression monthsDiff(DateTimeV2Literal t1, DateV2Literal t2) {
return new BigIntLiteral(ChronoUnit.MONTHS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "months_diff")
public static Expression monthsDiff(DateV2Literal t1, DateTimeV2Literal t2) {
return new BigIntLiteral(ChronoUnit.MONTHS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "months_diff")
public static Expression monthsDiff(DateV2Literal t1, DateV2Literal t2) {
return new BigIntLiteral(ChronoUnit.MONTHS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "months_diff")
public static Expression monthsDiff(DateTimeLiteral t1, DateTimeLiteral t2) {
return new BigIntLiteral(ChronoUnit.MONTHS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "years_diff")
public static Expression yearsDiff(DateTimeV2Literal t1, DateTimeV2Literal t2) {
return new BigIntLiteral(ChronoUnit.YEARS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "years_diff")
public static Expression yearsDiff(DateTimeV2Literal t1, DateV2Literal t2) {
return new BigIntLiteral(ChronoUnit.YEARS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "years_diff")
public static Expression yearsDiff(DateV2Literal t1, DateTimeV2Literal t2) {
return new BigIntLiteral(ChronoUnit.YEARS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "years_diff")
public static Expression yearsDiff(DateV2Literal t1, DateV2Literal t2) {
return new BigIntLiteral(ChronoUnit.YEARS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
@ExecFunction(name = "years_diff")
public static Expression yearsDiff(DateTimeLiteral t1, DateTimeLiteral t2) {
return new BigIntLiteral(ChronoUnit.YEARS.between(t2.toJavaDateType(), t1.toJavaDateType()));
}
/**
* months_between(date1, date2, round_off)
*/
@ExecFunction(name = "months_between")
public static Expression monthsBetween(DateV2Literal t1, DateV2Literal t2, BooleanLiteral roundOff) {
long yearBetween = t1.getYear() - t2.getYear();
long monthBetween = t1.getMonth() - t2.getMonth();
int daysInMonth1 = YearMonth.of((int) t1.getYear(), (int) t1.getMonth()).lengthOfMonth();
int daysInMonth2 = YearMonth.of((int) t2.getYear(), (int) t2.getMonth()).lengthOfMonth();
double dayBetween = 0;
if (t1.getDay() == daysInMonth1 && t2.getDay() == daysInMonth2) {
dayBetween = 0;
} else {
dayBetween = (t1.getDay() - t2.getDay()) / 31.0;
}
double result = yearBetween * 12 + monthBetween + dayBetween;
// rounded to 8 digits unless roundOff=false.
if (roundOff.getValue()) {
result = new BigDecimal(result).setScale(8, RoundingMode.HALF_UP).doubleValue();
}
return new DoubleLiteral(result);
}
private static int getDayOfWeek(String day) {
Integer dayOfWeek = DAY_OF_WEEK.get(day.toUpperCase());
if (dayOfWeek == null) {
return 0;
}
return dayOfWeek;
}
/**
* date arithmetic function next_day
*/
@ExecFunction(name = "next_day")
public static Expression nextDay(DateV2Literal date, StringLiteral day) {
int dayOfWeek = getDayOfWeek(day.getValue());
if (dayOfWeek == 0) {
throw new RuntimeException("Invalid day of week: " + day.getValue());
}
int daysToAdd = (dayOfWeek - date.getDayOfWeek() + 7) % 7;
daysToAdd = daysToAdd == 0 ? 7 : daysToAdd;
return date.plusDays(daysToAdd);
}
}