[feature](datetime) Support timezone when insert datetime value (#21898)
This commit is contained in:
@ -25,6 +25,7 @@ import org.apache.doris.catalog.ScalarType;
|
||||
import org.apache.doris.catalog.Type;
|
||||
import org.apache.doris.common.AnalysisException;
|
||||
import org.apache.doris.common.InvalidFormatException;
|
||||
import org.apache.doris.nereids.util.DateUtils;
|
||||
import org.apache.doris.thrift.TDateLiteral;
|
||||
import org.apache.doris.thrift.TExprNode;
|
||||
import org.apache.doris.thrift.TExprNodeType;
|
||||
@ -175,6 +176,7 @@ public class DateLiteral extends LiteralExpr {
|
||||
|
||||
//Regex used to determine if the TIME field exists int date_format
|
||||
private static final Pattern HAS_TIME_PART = Pattern.compile("^.*[HhIiklrSsTp]+.*$");
|
||||
private static final Pattern HAS_OFFSET_PART = Pattern.compile("[\\+\\-]\\d{2}:\\d{2}");
|
||||
|
||||
//Date Literal persist type in meta
|
||||
private enum DateLiteralType {
|
||||
@ -358,6 +360,27 @@ public class DateLiteral extends LiteralExpr {
|
||||
Preconditions.checkArgument(type.isDateType());
|
||||
TemporalAccessor dateTime = null;
|
||||
boolean parsed = false;
|
||||
int offset = 0;
|
||||
|
||||
// parse timezone
|
||||
if (haveTimeZoneOffset(s) || haveTimeZoneName(s)) {
|
||||
String tzString = new String();
|
||||
if (haveTimeZoneName(s)) { // GMT, UTC+8, Z[, CN, Asia/Shanghai]
|
||||
int split = getTimeZoneSplitPos(s);
|
||||
Preconditions.checkArgument(split > 0);
|
||||
tzString = s.substring(split);
|
||||
s = s.substring(0, split);
|
||||
} else { // +04:30
|
||||
Preconditions.checkArgument(s.charAt(s.length() - 6) == '-' || s.charAt(s.length() - 6) == '+');
|
||||
tzString = s.substring(s.length() - 6);
|
||||
s = s.substring(0, s.length() - 6);
|
||||
}
|
||||
ZoneId zone = ZoneId.of(tzString);
|
||||
ZoneId dorisZone = DateUtils.getTimeZone();
|
||||
offset = dorisZone.getRules().getOffset(java.time.Instant.now()).getTotalSeconds()
|
||||
- zone.getRules().getOffset(java.time.Instant.now()).getTotalSeconds();
|
||||
}
|
||||
|
||||
if (!s.contains("-")) {
|
||||
// handle format like 20210106, but should not handle 2021-1-6
|
||||
for (DateTimeFormatter formatter : formatterList) {
|
||||
@ -455,6 +478,17 @@ public class DateLiteral extends LiteralExpr {
|
||||
type = ScalarType.createDatetimeV2Type(scale);
|
||||
}
|
||||
this.type = type;
|
||||
|
||||
if (offset != 0) {
|
||||
DateLiteral result = this.plusSeconds(offset);
|
||||
this.second = result.second;
|
||||
this.minute = result.minute;
|
||||
this.hour = result.hour;
|
||||
this.day = result.day;
|
||||
this.month = result.month;
|
||||
this.year = result.year;
|
||||
}
|
||||
|
||||
if (checkRange() || checkDate()) {
|
||||
throw new AnalysisException("Datetime value is out of range");
|
||||
}
|
||||
@ -1770,4 +1804,27 @@ public class DateLiteral extends LiteralExpr {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean haveTimeZoneOffset(String arg) {
|
||||
Preconditions.checkArgument(arg.length() > 6);
|
||||
return HAS_OFFSET_PART.matcher(arg.substring(arg.length() - 6)).matches();
|
||||
}
|
||||
|
||||
private static boolean haveTimeZoneName(String arg) {
|
||||
for (char ch : arg.toCharArray()) {
|
||||
if (Character.isUpperCase(ch) && ch != 'T') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int getTimeZoneSplitPos(String arg) {
|
||||
int split = arg.length() - 1;
|
||||
for (; !Character.isAlphabetic(arg.charAt(split)); split--) {
|
||||
} // skip +8 of UTC+8
|
||||
for (; split >= 0 && (Character.isUpperCase(arg.charAt(split)) || arg.charAt(split) == '/'); split--) {
|
||||
}
|
||||
return split + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -261,7 +261,7 @@ public class StringLiteral extends LiteralExpr {
|
||||
} else if (targetType.isDateType()) {
|
||||
// FE only support 'yyyy-MM-dd hh:mm:ss' && 'yyyy-MM-dd' format
|
||||
// so if FE unchecked cast fail, we also build CastExpr for BE
|
||||
// BE support other format suck as 'yyyyMMdd'...
|
||||
// BE support other format such as 'yyyyMMdd'...
|
||||
try {
|
||||
return convertToDate(targetType);
|
||||
} catch (AnalysisException e) {
|
||||
|
||||
@ -26,11 +26,13 @@ import org.apache.doris.nereids.types.DateTimeType;
|
||||
import org.apache.doris.nereids.types.coercion.DateLikeType;
|
||||
import org.apache.doris.nereids.util.DateUtils;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.time.format.DateTimeParseException;
|
||||
@ -40,6 +42,7 @@ import java.time.temporal.TemporalAccessor;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* date time literal.
|
||||
@ -58,6 +61,8 @@ public class DateTimeLiteral extends DateLiteral {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(DateTimeLiteral.class);
|
||||
|
||||
private static final Pattern HAS_OFFSET_PART = Pattern.compile("[\\+\\-]\\d{2}:\\d{2}");
|
||||
|
||||
protected long hour;
|
||||
protected long minute;
|
||||
protected long second;
|
||||
@ -146,6 +151,26 @@ public class DateTimeLiteral extends DateLiteral {
|
||||
protected void init(String s) throws AnalysisException {
|
||||
try {
|
||||
TemporalAccessor dateTime = null;
|
||||
int offset = 0;
|
||||
// parse timezone
|
||||
if (haveTimeZoneOffset(s) || haveTimeZoneName(s)) {
|
||||
String tzString = new String();
|
||||
if (haveTimeZoneName(s)) { // GMT, UTC+8, Z[, CN, Asia/Shanghai]
|
||||
int split = getTimeZoneSplitPos(s);
|
||||
Preconditions.checkArgument(split > 0);
|
||||
tzString = s.substring(split);
|
||||
s = s.substring(0, split);
|
||||
} else { // +04:30
|
||||
Preconditions.checkArgument(s.charAt(s.length() - 6) == '-' || s.charAt(s.length() - 6) == '+');
|
||||
tzString = s.substring(s.length() - 6);
|
||||
s = s.substring(0, s.length() - 6);
|
||||
}
|
||||
ZoneId zone = ZoneId.of(tzString);
|
||||
ZoneId dorisZone = DateUtils.getTimeZone();
|
||||
offset = dorisZone.getRules().getOffset(java.time.Instant.now()).getTotalSeconds()
|
||||
- zone.getRules().getOffset(java.time.Instant.now()).getTotalSeconds();
|
||||
}
|
||||
|
||||
if (!s.contains("-")) {
|
||||
// handle format like 20210106, but should not handle 2021-1-6
|
||||
boolean parsed = false;
|
||||
@ -231,6 +256,16 @@ public class DateTimeLiteral extends DateLiteral {
|
||||
second = DateUtils.getOrDefault(dateTime, ChronoField.SECOND_OF_MINUTE);
|
||||
microSecond = DateUtils.getOrDefault(dateTime, ChronoField.MICRO_OF_SECOND);
|
||||
|
||||
if (offset != 0) {
|
||||
DateTimeLiteral result = (DateTimeLiteral) this.plusSeconds(offset);
|
||||
this.second = result.second;
|
||||
this.minute = result.minute;
|
||||
this.hour = result.hour;
|
||||
this.day = result.day;
|
||||
this.month = result.month;
|
||||
this.year = result.year;
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
throw new AnalysisException("datetime literal [" + s + "] is invalid");
|
||||
}
|
||||
@ -344,4 +379,27 @@ public class DateTimeLiteral extends DateLiteral {
|
||||
: new DateTimeLiteral(dateTime.getYear(), dateTime.getMonthValue(), dateTime.getDayOfMonth(),
|
||||
dateTime.getHour(), dateTime.getMinute(), dateTime.getSecond());
|
||||
}
|
||||
|
||||
private static boolean haveTimeZoneOffset(String arg) {
|
||||
Preconditions.checkArgument(arg.length() > 6);
|
||||
return HAS_OFFSET_PART.matcher(arg.substring(arg.length() - 6)).matches();
|
||||
}
|
||||
|
||||
private static boolean haveTimeZoneName(String arg) {
|
||||
for (char ch : arg.toCharArray()) {
|
||||
if (Character.isUpperCase(ch) && ch != 'T') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int getTimeZoneSplitPos(String arg) {
|
||||
int split = arg.length() - 1;
|
||||
for (; !Character.isAlphabetic(arg.charAt(split)); split--) {
|
||||
} // skip +8 of UTC+8
|
||||
for (; split >= 0 && (Character.isUpperCase(arg.charAt(split)) || arg.charAt(split) == '/'); split--) {
|
||||
}
|
||||
return split + 1;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user