diff --git a/common/src/main/java/io/dingodb/expr/common/timezone/DateTimeUtils.java b/common/src/main/java/io/dingodb/expr/common/timezone/DateTimeUtils.java index 2d3e554d..06f3690a 100644 --- a/common/src/main/java/io/dingodb/expr/common/timezone/DateTimeUtils.java +++ b/common/src/main/java/io/dingodb/expr/common/timezone/DateTimeUtils.java @@ -238,6 +238,43 @@ private DateTimeUtils() { if (trimmed.isEmpty()) { return null; } + + // Structural pre-classification to reduce unnecessary formatter attempts. + // Each failed DateTimeFormatter.parse() throws DateTimeParseException with + // expensive stack trace capture (~1-5us per exception). + boolean hasColon = trimmed.indexOf(':') > 0; + boolean hasDateSep = trimmed.length() >= 8 + && (trimmed.indexOf('-') > 0 || trimmed.indexOf('/') > 0 + || Character.isDigit(trimmed.charAt(0))); + boolean hasZoneInfo = false; + if (trimmed.length() > 10) { + hasZoneInfo = trimmed.indexOf('+', 10) >= 0 + || trimmed.indexOf('Z', 10) >= 0 + || trimmed.contains("["); + } + + if (hasZoneInfo) { + if (matchesTimestampWithZone(trimmed, timestampTzFormatters)) { + return DateTimeType.TIMESTAMP_TZ; + } + } + if (hasDateSep && hasColon) { + if (matchesLocalDateTime(trimmed, timestampFormatters)) { + return DateTimeType.TIMESTAMP; + } + } + if (hasDateSep && !hasColon) { + if (matchesLocalDate(trimmed, dateFormatters)) { + return DateTimeType.DATE; + } + } + if (hasColon && !hasDateSep) { + if (matchesLocalTime(trimmed, timeFormatters)) { + return DateTimeType.TIME; + } + } + + // Fallback: full scan for ambiguous inputs if (matchesTimestampWithZone(trimmed, timestampTzFormatters)) { return DateTimeType.TIMESTAMP_TZ; } diff --git a/common/src/main/java/io/dingodb/expr/common/timezone/converter/DateTimeConverter.java b/common/src/main/java/io/dingodb/expr/common/timezone/converter/DateTimeConverter.java index 923bd620..99dc65c9 100644 --- a/common/src/main/java/io/dingodb/expr/common/timezone/converter/DateTimeConverter.java +++ b/common/src/main/java/io/dingodb/expr/common/timezone/converter/DateTimeConverter.java @@ -30,6 +30,7 @@ public class DateTimeConverter { private final ZoneId defaultZone; + private final StringDateTimeParser stringParser = new StringDateTimeParser(); public DateTimeConverter(ZoneId defaultZone) { this.defaultZone = defaultZone; @@ -53,8 +54,7 @@ public DingoDateTime convertInput(Object input, DateTimeType targetType) { } else if (input instanceof java.util.Date) { return convertUtilDate((java.util.Date) input, targetType); } else if (input instanceof String) { - StringDateTimeParser parser = new StringDateTimeParser(); - return parser.parseString((String) input, targetType, defaultZone); + return stringParser.parseString((String) input, targetType, defaultZone); } else if (input instanceof DingoDateTime) { return convertDingoDateTime((DingoDateTime) input, targetType); } else { diff --git a/common/src/main/java/io/dingodb/expr/common/timezone/processor/DingoTimeZoneProcessor.java b/common/src/main/java/io/dingodb/expr/common/timezone/processor/DingoTimeZoneProcessor.java index 7325ab3e..3303eb6e 100644 --- a/common/src/main/java/io/dingodb/expr/common/timezone/processor/DingoTimeZoneProcessor.java +++ b/common/src/main/java/io/dingodb/expr/common/timezone/processor/DingoTimeZoneProcessor.java @@ -22,7 +22,6 @@ import io.dingodb.expr.common.timezone.core.DateTimeType; import io.dingodb.expr.common.timezone.core.DingoDateTime; import io.dingodb.expr.common.timezone.core.SimpleTimeZoneConfig; -import io.dingodb.expr.common.timezone.operations.ArithmeticOperations; import io.dingodb.expr.common.timezone.operations.ExtractionOperations; import io.dingodb.expr.common.timezone.operations.FormattingOperations; import io.dingodb.expr.common.timezone.operations.OperationResult; @@ -30,7 +29,10 @@ import java.time.Instant; 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.temporal.ChronoUnit; @@ -60,6 +62,18 @@ public Object processDateTime(Object input, DateTimeType inputType, if (input == null || inputType == null) { return null; } + // Fast path: same type, non-TZ-sensitive SQL types — no conversion needed + if (inputType == outputType) { + if (input instanceof java.sql.Date && inputType == DateTimeType.DATE) { + return input; + } + if (input instanceof java.sql.Time && inputType == DateTimeType.TIME) { + return input; + } + if (input instanceof java.sql.Timestamp && inputType == DateTimeType.TIMESTAMP) { + return input; + } + } try { DingoDateTime internal = tierProcessor.convertInput(input, inputType); @@ -134,29 +148,50 @@ public ZoneId getStorageZone() { } public DingoDateTime dateAdd(DingoDateTime dateTime, long amount, ChronoUnit unit) { - ArithmeticOperations.AddOperation operation = new ArithmeticOperations.AddOperation(unit, amount); - - OperationResult result = (OperationResult) operation.execute(new DingoDateTime[]{dateTime}, getOutputZone()); - - if (result.isSuccess() && result.getValue() instanceof DingoDateTime) { - return (DingoDateTime) result.getValue(); + if (dateTime.isTimeZoneSensitive()) { + DingoDateTime.DingoTimestampTZ tzValue = (DingoDateTime.DingoTimestampTZ) dateTime; + ZonedDateTime zdt = tzValue.getUtcValue().atZone(tzValue.getOriginalZone()); + return new DingoDateTime.DingoTimestampTZ(zdt.plus(amount, unit).toInstant(), tzValue.getOriginalZone()); } - - throw new DateTimeProcessingException("Date add operation failed: " - + (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error")); + Object value = dateTime.getValue(); + if (value instanceof LocalDate) { + return new DingoDateTime.DingoLocalDate(((LocalDate) value).plus(amount, unit)); + } else if (value instanceof LocalDateTime) { + return new DingoDateTime.DingoLocalDateTime(((LocalDateTime) value).plus(amount, unit)); + } else if (value instanceof LocalTime) { + return new DingoDateTime.DingoLocalTime(((LocalTime) value).plus(amount, unit)); + } + throw new DateTimeProcessingException("Unsupported type for add: " + value.getClass()); } public DingoDateTime dateSubtract(DingoDateTime dateTime, long amount, ChronoUnit unit) { - ArithmeticOperations.SubtractOperation operation = new ArithmeticOperations.SubtractOperation(unit, amount); + if (dateTime.isTimeZoneSensitive()) { + DingoDateTime.DingoTimestampTZ tzValue = (DingoDateTime.DingoTimestampTZ) dateTime; + ZonedDateTime zdt = tzValue.getUtcValue().atZone(tzValue.getOriginalZone()); + return new DingoDateTime.DingoTimestampTZ(zdt.minus(amount, unit).toInstant(), tzValue.getOriginalZone()); + } + Object value = dateTime.getValue(); + if (value instanceof LocalDate) { + return new DingoDateTime.DingoLocalDate(((LocalDate) value).minus(amount, unit)); + } else if (value instanceof LocalDateTime) { + return new DingoDateTime.DingoLocalDateTime(((LocalDateTime) value).minus(amount, unit)); + } else if (value instanceof LocalTime) { + return new DingoDateTime.DingoLocalTime(((LocalTime) value).minus(amount, unit)); + } + throw new DateTimeProcessingException("Unsupported type for subtract: " + value.getClass()); + } - OperationResult result = (OperationResult) operation.execute(new DingoDateTime[]{dateTime}, getOutputZone()); + // ------------------------------------------------------------------------- + // Type-specialized extraction fast paths — bypass the full pipeline + // for non-timezone-sensitive java.sql types (Date, Timestamp). + // ------------------------------------------------------------------------- - if (result.isSuccess() && result.getValue() instanceof DingoDateTime) { - return (DingoDateTime) result.getValue(); - } + public Integer extractYear(java.sql.Date value) { + return value == null ? null : value.toLocalDate().getYear(); + } - throw new DateTimeProcessingException("Date subtract operation failed: " - + (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error")); + public Integer extractYear(java.sql.Timestamp value) { + return value == null ? null : value.toLocalDateTime().getYear(); } public Integer extractYear(Object input) { @@ -178,6 +213,14 @@ public Integer extractYear(Object input) { + (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error")); } + public Integer extractMonth(java.sql.Date value) { + return value == null ? null : value.toLocalDate().getMonthValue(); + } + + public Integer extractMonth(java.sql.Timestamp value) { + return value == null ? null : value.toLocalDateTime().getMonthValue(); + } + public Integer extractMonth(Object input) { ExtractionOperations.ExtractFieldOperation operation = ExtractionOperations.month(); DateTimeType inputType = inferInputType(input); @@ -198,6 +241,14 @@ public Integer extractMonth(Object input) { + (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error")); } + public Integer extractDay(java.sql.Date value) { + return value == null ? null : value.toLocalDate().getDayOfMonth(); + } + + public Integer extractDay(java.sql.Timestamp value) { + return value == null ? null : value.toLocalDateTime().getDayOfMonth(); + } + public Integer extractDay(Object input) { ExtractionOperations.ExtractFieldOperation operation = ExtractionOperations.dayOfMonth(); DateTimeType inputType = inferInputType(input); @@ -218,6 +269,14 @@ public Integer extractDay(Object input) { + (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error")); } + public Integer extractQuarter(java.sql.Date value) { + return value == null ? null : (value.toLocalDate().getMonthValue() - 1) / 3 + 1; + } + + public Integer extractQuarter(java.sql.Timestamp value) { + return value == null ? null : (value.toLocalDateTime().getMonthValue() - 1) / 3 + 1; + } + public Integer extractQuarter(Object input) { ExtractionOperations.ExtractQuarterOperation operation = ExtractionOperations.quarter(); DateTimeType inputType = inferInputType(input); @@ -238,6 +297,20 @@ public Integer extractQuarter(Object input) { + (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error")); } + public Integer extractWeek(java.sql.Date value) { + if (value == null) { + return null; + } + return value.toLocalDate().get(java.time.temporal.IsoFields.WEEK_OF_WEEK_BASED_YEAR); + } + + public Integer extractWeek(java.sql.Timestamp value) { + if (value == null) { + return null; + } + return value.toLocalDateTime().get(java.time.temporal.IsoFields.WEEK_OF_WEEK_BASED_YEAR); + } + public Integer extractWeek(Object input) { ExtractionOperations.ExtractFieldOperation operation = ExtractionOperations.week(); DateTimeType inputType = inferInputType(input); @@ -259,6 +332,18 @@ public Integer extractWeek(Object input) { + (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error")); } + public Integer extractHour(java.sql.Date value) { + return 0; + } + + public Integer extractHour(java.sql.Timestamp value) { + return value == null ? 0 : value.toLocalDateTime().getHour(); + } + + public Integer extractHour(java.sql.Time value) { + return value == null ? 0 : value.toLocalTime().getHour(); + } + public Integer extractHour(Object input) { ExtractionOperations.ExtractFieldOperation operation = ExtractionOperations.hour(); DateTimeType inputType = inferInputType(input); @@ -276,6 +361,18 @@ public Integer extractHour(Object input) { + (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error")); } + public Integer extractMinute(java.sql.Date value) { + return 0; + } + + public Integer extractMinute(java.sql.Timestamp value) { + return value == null ? 0 : value.toLocalDateTime().getMinute(); + } + + public Integer extractMinute(java.sql.Time value) { + return value == null ? 0 : value.toLocalTime().getMinute(); + } + public Integer extractMinute(Object input) { ExtractionOperations.ExtractFieldOperation operation = ExtractionOperations.minute(); DateTimeType inputType = inferInputType(input); @@ -293,6 +390,18 @@ public Integer extractMinute(Object input) { + (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error")); } + public Integer extractSecond(java.sql.Date value) { + return 0; + } + + public Integer extractSecond(java.sql.Timestamp value) { + return value == null ? 0 : value.toLocalDateTime().getSecond(); + } + + public Integer extractSecond(java.sql.Time value) { + return value == null ? 0 : value.toLocalTime().getSecond(); + } + public Integer extractSecond(Object input) { ExtractionOperations.ExtractFieldOperation operation = ExtractionOperations.second(); DateTimeType inputType = inferInputType(input); @@ -310,6 +419,17 @@ public Integer extractSecond(Object input) { + (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error")); } + public Integer extractMillisecond(java.sql.Date value) { + return 0; + } + + public Integer extractMillisecond(java.sql.Timestamp value) { + if (value == null) { + return 0; + } + return value.toLocalDateTime().getNano() / 1_000_000; + } + public Integer extractMillisecond(Object input) { ExtractionOperations.ExtractFieldOperation operation = ExtractionOperations.millisecond(); DateTimeType inputType = inferInputType(input); diff --git a/runtime/src/main/java/io/dingodb/expr/runtime/ExprConfig.java b/runtime/src/main/java/io/dingodb/expr/runtime/ExprConfig.java index 0a3bc168..5d957066 100644 --- a/runtime/src/main/java/io/dingodb/expr/runtime/ExprConfig.java +++ b/runtime/src/main/java/io/dingodb/expr/runtime/ExprConfig.java @@ -88,7 +88,7 @@ default void setProcessor(DingoTimeZoneProcessor processor) { } default DingoTimeZoneProcessor getProcessor() { - return new DingoTimeZoneProcessor(ZoneId.systemDefault()); + return DefaultProcessorHolder.INSTANCE; } default DateTimeFormatter[] getParseDateFormatters() { @@ -122,4 +122,11 @@ default DateTimeFormatter getOutputTimeFormatter() { default DateTimeFormatter getOutputTimestampFormatter() { return DateTimeUtils.DEFAULT_OUTPUT_TIMESTAMP_FORMATTER; } + + // Lazy initialization holder for the default DingoTimeZoneProcessor singleton. + // Avoids creating a new processor instance on every getProcessor() call. + class DefaultProcessorHolder { + static final DingoTimeZoneProcessor INSTANCE = + new DingoTimeZoneProcessor(ZoneId.systemDefault()); + } } diff --git a/runtime/src/main/java/io/dingodb/expr/runtime/op/time/CurrentDateFun.java b/runtime/src/main/java/io/dingodb/expr/runtime/op/time/CurrentDateFun.java index 2109aeb4..86c5759b 100644 --- a/runtime/src/main/java/io/dingodb/expr/runtime/op/time/CurrentDateFun.java +++ b/runtime/src/main/java/io/dingodb/expr/runtime/op/time/CurrentDateFun.java @@ -47,7 +47,7 @@ public Object eval(EvalContext context, ExprConfig config) { @Override public boolean isConst(@NonNull NullaryOpExpr expr) { - return false; + return true; } @Override diff --git a/runtime/src/main/java/io/dingodb/expr/runtime/op/time/CurrentTimeFun.java b/runtime/src/main/java/io/dingodb/expr/runtime/op/time/CurrentTimeFun.java index 77836a7e..498157e2 100644 --- a/runtime/src/main/java/io/dingodb/expr/runtime/op/time/CurrentTimeFun.java +++ b/runtime/src/main/java/io/dingodb/expr/runtime/op/time/CurrentTimeFun.java @@ -52,7 +52,7 @@ public Type getType() { @Override public boolean isConst(@NonNull NullaryOpExpr expr) { - return false; + return true; } @Override