Skip to content

Commit 03fb2eb

Browse files
wt0530githubgxll
authored andcommitted
[fix][common] Fix performance issues with time zone handling
1 parent 3391ac5 commit 03fb2eb

6 files changed

Lines changed: 186 additions & 22 deletions

File tree

common/src/main/java/io/dingodb/expr/common/timezone/DateTimeUtils.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,43 @@ private DateTimeUtils() {
238238
if (trimmed.isEmpty()) {
239239
return null;
240240
}
241+
242+
// Structural pre-classification to reduce unnecessary formatter attempts.
243+
// Each failed DateTimeFormatter.parse() throws DateTimeParseException with
244+
// expensive stack trace capture (~1-5us per exception).
245+
boolean hasColon = trimmed.indexOf(':') > 0;
246+
boolean hasDateSep = trimmed.length() >= 8
247+
&& (trimmed.indexOf('-') > 0 || trimmed.indexOf('/') > 0
248+
|| Character.isDigit(trimmed.charAt(0)));
249+
boolean hasZoneInfo = false;
250+
if (trimmed.length() > 10) {
251+
hasZoneInfo = trimmed.indexOf('+', 10) >= 0
252+
|| trimmed.indexOf('Z', 10) >= 0
253+
|| trimmed.contains("[");
254+
}
255+
256+
if (hasZoneInfo) {
257+
if (matchesTimestampWithZone(trimmed, timestampTzFormatters)) {
258+
return DateTimeType.TIMESTAMP_TZ;
259+
}
260+
}
261+
if (hasDateSep && hasColon) {
262+
if (matchesLocalDateTime(trimmed, timestampFormatters)) {
263+
return DateTimeType.TIMESTAMP;
264+
}
265+
}
266+
if (hasDateSep && !hasColon) {
267+
if (matchesLocalDate(trimmed, dateFormatters)) {
268+
return DateTimeType.DATE;
269+
}
270+
}
271+
if (hasColon && !hasDateSep) {
272+
if (matchesLocalTime(trimmed, timeFormatters)) {
273+
return DateTimeType.TIME;
274+
}
275+
}
276+
277+
// Fallback: full scan for ambiguous inputs
241278
if (matchesTimestampWithZone(trimmed, timestampTzFormatters)) {
242279
return DateTimeType.TIMESTAMP_TZ;
243280
}

common/src/main/java/io/dingodb/expr/common/timezone/converter/DateTimeConverter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
public class DateTimeConverter {
3131

3232
private final ZoneId defaultZone;
33+
private final StringDateTimeParser stringParser = new StringDateTimeParser();
3334

3435
public DateTimeConverter(ZoneId defaultZone) {
3536
this.defaultZone = defaultZone;
@@ -53,8 +54,7 @@ public DingoDateTime convertInput(Object input, DateTimeType targetType) {
5354
} else if (input instanceof java.util.Date) {
5455
return convertUtilDate((java.util.Date) input, targetType);
5556
} else if (input instanceof String) {
56-
StringDateTimeParser parser = new StringDateTimeParser();
57-
return parser.parseString((String) input, targetType, defaultZone);
57+
return stringParser.parseString((String) input, targetType, defaultZone);
5858
} else if (input instanceof DingoDateTime) {
5959
return convertDingoDateTime((DingoDateTime) input, targetType);
6060
} else {

common/src/main/java/io/dingodb/expr/common/timezone/processor/DingoTimeZoneProcessor.java

Lines changed: 137 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,17 @@
2222
import io.dingodb.expr.common.timezone.core.DateTimeType;
2323
import io.dingodb.expr.common.timezone.core.DingoDateTime;
2424
import io.dingodb.expr.common.timezone.core.SimpleTimeZoneConfig;
25-
import io.dingodb.expr.common.timezone.operations.ArithmeticOperations;
2625
import io.dingodb.expr.common.timezone.operations.ExtractionOperations;
2726
import io.dingodb.expr.common.timezone.operations.FormattingOperations;
2827
import io.dingodb.expr.common.timezone.operations.OperationResult;
2928
import lombok.Getter;
3029

3130
import java.time.Instant;
3231
import java.time.LocalDate;
32+
import java.time.LocalDateTime;
33+
import java.time.LocalTime;
3334
import java.time.ZoneId;
35+
import java.time.ZonedDateTime;
3436
import java.time.format.DateTimeFormatter;
3537
import java.time.temporal.ChronoUnit;
3638

@@ -60,6 +62,18 @@ public Object processDateTime(Object input, DateTimeType inputType,
6062
if (input == null || inputType == null) {
6163
return null;
6264
}
65+
// Fast path: same type, non-TZ-sensitive SQL types — no conversion needed
66+
if (inputType == outputType) {
67+
if (input instanceof java.sql.Date && inputType == DateTimeType.DATE) {
68+
return input;
69+
}
70+
if (input instanceof java.sql.Time && inputType == DateTimeType.TIME) {
71+
return input;
72+
}
73+
if (input instanceof java.sql.Timestamp && inputType == DateTimeType.TIMESTAMP) {
74+
return input;
75+
}
76+
}
6377
try {
6478
DingoDateTime internal = tierProcessor.convertInput(input, inputType);
6579

@@ -134,29 +148,50 @@ public ZoneId getStorageZone() {
134148
}
135149

136150
public DingoDateTime dateAdd(DingoDateTime dateTime, long amount, ChronoUnit unit) {
137-
ArithmeticOperations.AddOperation operation = new ArithmeticOperations.AddOperation(unit, amount);
138-
139-
OperationResult result = (OperationResult) operation.execute(new DingoDateTime[]{dateTime}, getOutputZone());
140-
141-
if (result.isSuccess() && result.getValue() instanceof DingoDateTime) {
142-
return (DingoDateTime) result.getValue();
151+
if (dateTime.isTimeZoneSensitive()) {
152+
DingoDateTime.DingoTimestampTZ tzValue = (DingoDateTime.DingoTimestampTZ) dateTime;
153+
ZonedDateTime zdt = tzValue.getUtcValue().atZone(tzValue.getOriginalZone());
154+
return new DingoDateTime.DingoTimestampTZ(zdt.plus(amount, unit).toInstant(), tzValue.getOriginalZone());
143155
}
144-
145-
throw new DateTimeProcessingException("Date add operation failed: "
146-
+ (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error"));
156+
Object value = dateTime.getValue();
157+
if (value instanceof LocalDate) {
158+
return new DingoDateTime.DingoLocalDate(((LocalDate) value).plus(amount, unit));
159+
} else if (value instanceof LocalDateTime) {
160+
return new DingoDateTime.DingoLocalDateTime(((LocalDateTime) value).plus(amount, unit));
161+
} else if (value instanceof LocalTime) {
162+
return new DingoDateTime.DingoLocalTime(((LocalTime) value).plus(amount, unit));
163+
}
164+
throw new DateTimeProcessingException("Unsupported type for add: " + value.getClass());
147165
}
148166

149167
public DingoDateTime dateSubtract(DingoDateTime dateTime, long amount, ChronoUnit unit) {
150-
ArithmeticOperations.SubtractOperation operation = new ArithmeticOperations.SubtractOperation(unit, amount);
168+
if (dateTime.isTimeZoneSensitive()) {
169+
DingoDateTime.DingoTimestampTZ tzValue = (DingoDateTime.DingoTimestampTZ) dateTime;
170+
ZonedDateTime zdt = tzValue.getUtcValue().atZone(tzValue.getOriginalZone());
171+
return new DingoDateTime.DingoTimestampTZ(zdt.minus(amount, unit).toInstant(), tzValue.getOriginalZone());
172+
}
173+
Object value = dateTime.getValue();
174+
if (value instanceof LocalDate) {
175+
return new DingoDateTime.DingoLocalDate(((LocalDate) value).minus(amount, unit));
176+
} else if (value instanceof LocalDateTime) {
177+
return new DingoDateTime.DingoLocalDateTime(((LocalDateTime) value).minus(amount, unit));
178+
} else if (value instanceof LocalTime) {
179+
return new DingoDateTime.DingoLocalTime(((LocalTime) value).minus(amount, unit));
180+
}
181+
throw new DateTimeProcessingException("Unsupported type for subtract: " + value.getClass());
182+
}
151183

152-
OperationResult result = (OperationResult) operation.execute(new DingoDateTime[]{dateTime}, getOutputZone());
184+
// -------------------------------------------------------------------------
185+
// Type-specialized extraction fast paths — bypass the full pipeline
186+
// for non-timezone-sensitive java.sql types (Date, Timestamp).
187+
// -------------------------------------------------------------------------
153188

154-
if (result.isSuccess() && result.getValue() instanceof DingoDateTime) {
155-
return (DingoDateTime) result.getValue();
156-
}
189+
public Integer extractYear(java.sql.Date value) {
190+
return value == null ? null : value.toLocalDate().getYear();
191+
}
157192

158-
throw new DateTimeProcessingException("Date subtract operation failed: "
159-
+ (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error"));
193+
public Integer extractYear(java.sql.Timestamp value) {
194+
return value == null ? null : value.toLocalDateTime().getYear();
160195
}
161196

162197
public Integer extractYear(Object input) {
@@ -178,6 +213,14 @@ public Integer extractYear(Object input) {
178213
+ (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error"));
179214
}
180215

216+
public Integer extractMonth(java.sql.Date value) {
217+
return value == null ? null : value.toLocalDate().getMonthValue();
218+
}
219+
220+
public Integer extractMonth(java.sql.Timestamp value) {
221+
return value == null ? null : value.toLocalDateTime().getMonthValue();
222+
}
223+
181224
public Integer extractMonth(Object input) {
182225
ExtractionOperations.ExtractFieldOperation operation = ExtractionOperations.month();
183226
DateTimeType inputType = inferInputType(input);
@@ -198,6 +241,14 @@ public Integer extractMonth(Object input) {
198241
+ (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error"));
199242
}
200243

244+
public Integer extractDay(java.sql.Date value) {
245+
return value == null ? null : value.toLocalDate().getDayOfMonth();
246+
}
247+
248+
public Integer extractDay(java.sql.Timestamp value) {
249+
return value == null ? null : value.toLocalDateTime().getDayOfMonth();
250+
}
251+
201252
public Integer extractDay(Object input) {
202253
ExtractionOperations.ExtractFieldOperation operation = ExtractionOperations.dayOfMonth();
203254
DateTimeType inputType = inferInputType(input);
@@ -218,6 +269,14 @@ public Integer extractDay(Object input) {
218269
+ (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error"));
219270
}
220271

272+
public Integer extractQuarter(java.sql.Date value) {
273+
return value == null ? null : (value.toLocalDate().getMonthValue() - 1) / 3 + 1;
274+
}
275+
276+
public Integer extractQuarter(java.sql.Timestamp value) {
277+
return value == null ? null : (value.toLocalDateTime().getMonthValue() - 1) / 3 + 1;
278+
}
279+
221280
public Integer extractQuarter(Object input) {
222281
ExtractionOperations.ExtractQuarterOperation operation = ExtractionOperations.quarter();
223282
DateTimeType inputType = inferInputType(input);
@@ -238,6 +297,20 @@ public Integer extractQuarter(Object input) {
238297
+ (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error"));
239298
}
240299

300+
public Integer extractWeek(java.sql.Date value) {
301+
if (value == null) {
302+
return null;
303+
}
304+
return value.toLocalDate().get(java.time.temporal.IsoFields.WEEK_OF_WEEK_BASED_YEAR);
305+
}
306+
307+
public Integer extractWeek(java.sql.Timestamp value) {
308+
if (value == null) {
309+
return null;
310+
}
311+
return value.toLocalDateTime().get(java.time.temporal.IsoFields.WEEK_OF_WEEK_BASED_YEAR);
312+
}
313+
241314
public Integer extractWeek(Object input) {
242315
ExtractionOperations.ExtractFieldOperation operation = ExtractionOperations.week();
243316
DateTimeType inputType = inferInputType(input);
@@ -259,6 +332,18 @@ public Integer extractWeek(Object input) {
259332
+ (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error"));
260333
}
261334

335+
public Integer extractHour(java.sql.Date value) {
336+
return 0;
337+
}
338+
339+
public Integer extractHour(java.sql.Timestamp value) {
340+
return value == null ? 0 : value.toLocalDateTime().getHour();
341+
}
342+
343+
public Integer extractHour(java.sql.Time value) {
344+
return value == null ? 0 : value.toLocalTime().getHour();
345+
}
346+
262347
public Integer extractHour(Object input) {
263348
ExtractionOperations.ExtractFieldOperation operation = ExtractionOperations.hour();
264349
DateTimeType inputType = inferInputType(input);
@@ -276,6 +361,18 @@ public Integer extractHour(Object input) {
276361
+ (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error"));
277362
}
278363

364+
public Integer extractMinute(java.sql.Date value) {
365+
return 0;
366+
}
367+
368+
public Integer extractMinute(java.sql.Timestamp value) {
369+
return value == null ? 0 : value.toLocalDateTime().getMinute();
370+
}
371+
372+
public Integer extractMinute(java.sql.Time value) {
373+
return value == null ? 0 : value.toLocalTime().getMinute();
374+
}
375+
279376
public Integer extractMinute(Object input) {
280377
ExtractionOperations.ExtractFieldOperation operation = ExtractionOperations.minute();
281378
DateTimeType inputType = inferInputType(input);
@@ -293,6 +390,18 @@ public Integer extractMinute(Object input) {
293390
+ (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error"));
294391
}
295392

393+
public Integer extractSecond(java.sql.Date value) {
394+
return 0;
395+
}
396+
397+
public Integer extractSecond(java.sql.Timestamp value) {
398+
return value == null ? 0 : value.toLocalDateTime().getSecond();
399+
}
400+
401+
public Integer extractSecond(java.sql.Time value) {
402+
return value == null ? 0 : value.toLocalTime().getSecond();
403+
}
404+
296405
public Integer extractSecond(Object input) {
297406
ExtractionOperations.ExtractFieldOperation operation = ExtractionOperations.second();
298407
DateTimeType inputType = inferInputType(input);
@@ -310,6 +419,17 @@ public Integer extractSecond(Object input) {
310419
+ (result.getErrorMessage() != null ? result.getErrorMessage() : "Unknown error"));
311420
}
312421

422+
public Integer extractMillisecond(java.sql.Date value) {
423+
return 0;
424+
}
425+
426+
public Integer extractMillisecond(java.sql.Timestamp value) {
427+
if (value == null) {
428+
return 0;
429+
}
430+
return value.toLocalDateTime().getNano() / 1_000_000;
431+
}
432+
313433
public Integer extractMillisecond(Object input) {
314434
ExtractionOperations.ExtractFieldOperation operation = ExtractionOperations.millisecond();
315435
DateTimeType inputType = inferInputType(input);

runtime/src/main/java/io/dingodb/expr/runtime/ExprConfig.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ default void setProcessor(DingoTimeZoneProcessor processor) {
8888
}
8989

9090
default DingoTimeZoneProcessor getProcessor() {
91-
return new DingoTimeZoneProcessor(ZoneId.systemDefault());
91+
return DefaultProcessorHolder.INSTANCE;
9292
}
9393

9494
default DateTimeFormatter[] getParseDateFormatters() {
@@ -122,4 +122,11 @@ default DateTimeFormatter getOutputTimeFormatter() {
122122
default DateTimeFormatter getOutputTimestampFormatter() {
123123
return DateTimeUtils.DEFAULT_OUTPUT_TIMESTAMP_FORMATTER;
124124
}
125+
126+
// Lazy initialization holder for the default DingoTimeZoneProcessor singleton.
127+
// Avoids creating a new processor instance on every getProcessor() call.
128+
class DefaultProcessorHolder {
129+
static final DingoTimeZoneProcessor INSTANCE =
130+
new DingoTimeZoneProcessor(ZoneId.systemDefault());
131+
}
125132
}

runtime/src/main/java/io/dingodb/expr/runtime/op/time/CurrentDateFun.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public Object eval(EvalContext context, ExprConfig config) {
4747

4848
@Override
4949
public boolean isConst(@NonNull NullaryOpExpr expr) {
50-
return false;
50+
return true;
5151
}
5252

5353
@Override

runtime/src/main/java/io/dingodb/expr/runtime/op/time/CurrentTimeFun.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public Type getType() {
5252

5353
@Override
5454
public boolean isConst(@NonNull NullaryOpExpr expr) {
55-
return false;
55+
return true;
5656
}
5757

5858
@Override

0 commit comments

Comments
 (0)