This is an automated email from the ASF dual-hosted git repository.
jackietien pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/tsfile.git
The following commit(s) were added to refs/heads/develop by this push:
new 838d2903 Implement extract time filters
838d2903 is described below
commit 838d2903189de41c7c0d559f2d0d933c676e87ee
Author: Weihao Li <[email protected]>
AuthorDate: Wed Jul 16 17:29:48 2025 +0800
Implement extract time filters
---
.../apache/tsfile/read/filter/basic/Filter.java | 13 +
.../tsfile/read/filter/basic/OperatorType.java | 10 +-
.../tsfile/read/filter/factory/TimeFilterApi.java | 38 ++
.../operator/ExtractTimeFilterOperators.java | 616 +++++++++++++++++++++
.../tsfile/read/filter/ExtractTimeFilterTest.java | 436 +++++++++++++++
5 files changed, 1112 insertions(+), 1 deletion(-)
diff --git
a/java/tsfile/src/main/java/org/apache/tsfile/read/filter/basic/Filter.java
b/java/tsfile/src/main/java/org/apache/tsfile/read/filter/basic/Filter.java
index cc48210d..db5276e8 100755
--- a/java/tsfile/src/main/java/org/apache/tsfile/read/filter/basic/Filter.java
+++ b/java/tsfile/src/main/java/org/apache/tsfile/read/filter/basic/Filter.java
@@ -26,6 +26,7 @@ import org.apache.tsfile.read.filter.factory.FilterFactory;
import org.apache.tsfile.read.filter.factory.TimeFilterApi;
import org.apache.tsfile.read.filter.factory.ValueFilterApi;
import org.apache.tsfile.read.filter.operator.And;
+import org.apache.tsfile.read.filter.operator.ExtractTimeFilterOperators;
import org.apache.tsfile.read.filter.operator.GroupByFilter;
import org.apache.tsfile.read.filter.operator.GroupByMonthFilter;
import org.apache.tsfile.read.filter.operator.Not;
@@ -245,6 +246,18 @@ public abstract class Filter {
return new Or(buffer);
case NOT:
return new Not(buffer);
+ case EXTRACT_TIME_EQ:
+ return new ExtractTimeFilterOperators.ExtractTimeEq(buffer);
+ case EXTRACT_TIME_NEQ:
+ return new ExtractTimeFilterOperators.ExtractTimeNotEq(buffer);
+ case EXTRACT_TIME_GT:
+ return new ExtractTimeFilterOperators.ExtractTimeGt(buffer);
+ case EXTRACT_TIME_GTEQ:
+ return new ExtractTimeFilterOperators.ExtractTimeGtEq(buffer);
+ case EXTRACT_TIME_LT:
+ return new ExtractTimeFilterOperators.ExtractTimeLt(buffer);
+ case EXTRACT_TIME_LTEQ:
+ return new ExtractTimeFilterOperators.ExtractTimeLtEq(buffer);
default:
throw new UnsupportedOperationException("Unsupported operator type:" +
type);
}
diff --git
a/java/tsfile/src/main/java/org/apache/tsfile/read/filter/basic/OperatorType.java
b/java/tsfile/src/main/java/org/apache/tsfile/read/filter/basic/OperatorType.java
index db8be985..bad1ce76 100644
---
a/java/tsfile/src/main/java/org/apache/tsfile/read/filter/basic/OperatorType.java
+++
b/java/tsfile/src/main/java/org/apache/tsfile/read/filter/basic/OperatorType.java
@@ -65,7 +65,15 @@ public enum OperatorType {
// is null
VALUE_IS_NULL("IS NULL"),
- VALUE_IS_NOT_NULL("IS NOT NULL");
+ VALUE_IS_NOT_NULL("IS NOT NULL"),
+
+ // extract comparison
+ EXTRACT_TIME_EQ("="),
+ EXTRACT_TIME_NEQ("!="),
+ EXTRACT_TIME_GT(">"),
+ EXTRACT_TIME_GTEQ(">="),
+ EXTRACT_TIME_LT("<"),
+ EXTRACT_TIME_LTEQ("<=");
private final String symbol;
diff --git
a/java/tsfile/src/main/java/org/apache/tsfile/read/filter/factory/TimeFilterApi.java
b/java/tsfile/src/main/java/org/apache/tsfile/read/filter/factory/TimeFilterApi.java
index ce9f6129..3ba55053 100644
---
a/java/tsfile/src/main/java/org/apache/tsfile/read/filter/factory/TimeFilterApi.java
+++
b/java/tsfile/src/main/java/org/apache/tsfile/read/filter/factory/TimeFilterApi.java
@@ -19,6 +19,13 @@
package org.apache.tsfile.read.filter.factory;
+import
org.apache.tsfile.read.filter.operator.ExtractTimeFilterOperators.ExtractTimeEq;
+import
org.apache.tsfile.read.filter.operator.ExtractTimeFilterOperators.ExtractTimeGt;
+import
org.apache.tsfile.read.filter.operator.ExtractTimeFilterOperators.ExtractTimeGtEq;
+import
org.apache.tsfile.read.filter.operator.ExtractTimeFilterOperators.ExtractTimeLt;
+import
org.apache.tsfile.read.filter.operator.ExtractTimeFilterOperators.ExtractTimeLtEq;
+import
org.apache.tsfile.read.filter.operator.ExtractTimeFilterOperators.ExtractTimeNotEq;
+import org.apache.tsfile.read.filter.operator.ExtractTimeFilterOperators.Field;
import org.apache.tsfile.read.filter.operator.GroupByFilter;
import org.apache.tsfile.read.filter.operator.GroupByMonthFilter;
import
org.apache.tsfile.read.filter.operator.TimeFilterOperators.TimeBetweenAnd;
@@ -33,6 +40,7 @@ import
org.apache.tsfile.read.filter.operator.TimeFilterOperators.TimeNotEq;
import org.apache.tsfile.read.filter.operator.TimeFilterOperators.TimeNotIn;
import org.apache.tsfile.utils.TimeDuration;
+import java.time.ZoneId;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
@@ -98,4 +106,34 @@ public class TimeFilterApi {
return new GroupByMonthFilter(
startTime, endTime, interval, slidingStep, timeZone, currPrecision);
}
+
+ public static ExtractTimeGt extractTimeGt(
+ long value, Field field, ZoneId zoneId, TimeUnit currPrecision) {
+ return new ExtractTimeGt(value, field, zoneId, currPrecision);
+ }
+
+ public static ExtractTimeGtEq extractTimeGtEq(
+ long value, Field field, ZoneId zoneId, TimeUnit currPrecision) {
+ return new ExtractTimeGtEq(value, field, zoneId, currPrecision);
+ }
+
+ public static ExtractTimeLt extractTimeLt(
+ long value, Field field, ZoneId zoneId, TimeUnit currPrecision) {
+ return new ExtractTimeLt(value, field, zoneId, currPrecision);
+ }
+
+ public static ExtractTimeLtEq extractTimeLtEq(
+ long value, Field field, ZoneId zoneId, TimeUnit currPrecision) {
+ return new ExtractTimeLtEq(value, field, zoneId, currPrecision);
+ }
+
+ public static ExtractTimeEq extractTimeEq(
+ long value, Field field, ZoneId zoneId, TimeUnit currPrecision) {
+ return new ExtractTimeEq(value, field, zoneId, currPrecision);
+ }
+
+ public static ExtractTimeNotEq extractTimeNotEq(
+ long value, Field field, ZoneId zoneId, TimeUnit currPrecision) {
+ return new ExtractTimeNotEq(value, field, zoneId, currPrecision);
+ }
}
diff --git
a/java/tsfile/src/main/java/org/apache/tsfile/read/filter/operator/ExtractTimeFilterOperators.java
b/java/tsfile/src/main/java/org/apache/tsfile/read/filter/operator/ExtractTimeFilterOperators.java
new file mode 100644
index 00000000..8c43d1bf
--- /dev/null
+++
b/java/tsfile/src/main/java/org/apache/tsfile/read/filter/operator/ExtractTimeFilterOperators.java
@@ -0,0 +1,616 @@
+/*
+ * 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.tsfile.read.filter.operator;
+
+import org.apache.tsfile.read.common.TimeRange;
+import org.apache.tsfile.read.filter.basic.Filter;
+import org.apache.tsfile.read.filter.basic.OperatorType;
+import org.apache.tsfile.read.filter.basic.TimeFilter;
+import org.apache.tsfile.read.filter.factory.TimeFilterApi;
+import org.apache.tsfile.utils.ReadWriteIOUtils;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.time.DayOfWeek;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR;
+
+/**
+ * These are the extract time column operators in a filter predicate
expression tree. They are
+ * constructed by using the methods in {@link TimeFilterApi}
+ */
+public final class ExtractTimeFilterOperators {
+
+ public enum Field {
+ YEAR,
+ QUARTER,
+ MONTH,
+ WEEK,
+ DAY,
+ DAY_OF_MONTH,
+ DAY_OF_WEEK,
+ DOW,
+ DAY_OF_YEAR,
+ DOY,
+ HOUR,
+ MINUTE,
+ SECOND,
+ MS,
+ US,
+ NS
+ }
+
+ private ExtractTimeFilterOperators() {
+ // forbidden construction
+ }
+
+ private static final String OPERATOR_TO_STRING_FORMAT = "extract %s from
time %s %s";
+
+ abstract static class ExtractTimeCompareFilter extends TimeFilter {
+ protected final long constant;
+
+ protected final Field field;
+ protected final ZoneId zoneId;
+ protected final TimeUnit currPrecision;
+
+ private final transient Function<Long, Long> CAST_TIMESTAMP_TO_MS;
+ private final transient Function<Long, Long> EXTRACT_TIMESTAMP_MS_PART;
+ private final transient Function<Long, Long> EXTRACT_TIMESTAMP_US_PART;
+ private final transient Function<Long, Long> EXTRACT_TIMESTAMP_NS_PART;
+ protected final transient Function<Integer, Long> GET_YEAR_TIMESTAMP;
+
+ // calculate extraction of time
+ protected final transient Function<Long, Long> evaluateFunction;
+ // calculate if the truncations of input times are the same
+ protected final transient BiFunction<Long, Long, Boolean>
truncatedEqualsFunction;
+
+ // constant cannot be null
+ protected ExtractTimeCompareFilter(
+ long constant, Field field, ZoneId zoneId, TimeUnit currPrecision) {
+ this.constant = constant;
+ this.field = field;
+ this.zoneId = zoneId;
+ this.currPrecision = currPrecision;
+ // make lifestyles of these functions are same with object to avoid
switch case in calculation
+ switch (currPrecision) {
+ case MICROSECONDS:
+ CAST_TIMESTAMP_TO_MS = timestamp -> timestamp / 1000;
+ EXTRACT_TIMESTAMP_MS_PART = timestamp -> Math.floorMod(timestamp,
1000_000L) / 1000;
+ EXTRACT_TIMESTAMP_US_PART = timestamp -> Math.floorMod(timestamp,
1000L);
+ EXTRACT_TIMESTAMP_NS_PART = timestamp -> 0L;
+ GET_YEAR_TIMESTAMP =
+ year ->
+ Math.multiplyExact(
+ LocalDate.of(year, 1,
1).atStartOfDay(zoneId).toEpochSecond(), 1000_000L);
+ break;
+ case NANOSECONDS:
+ CAST_TIMESTAMP_TO_MS = timestamp -> timestamp / 1000000;
+ EXTRACT_TIMESTAMP_MS_PART =
+ timestamp -> Math.floorMod(timestamp, 1000_000_000L) / 1000_000;
+ EXTRACT_TIMESTAMP_US_PART = timestamp -> Math.floorMod(timestamp,
1000_000L) / 1000;
+ EXTRACT_TIMESTAMP_NS_PART = timestamp -> Math.floorMod(timestamp,
1000L);
+ GET_YEAR_TIMESTAMP =
+ year ->
+ Math.multiplyExact(
+ LocalDate.of(year, 1,
1).atStartOfDay(zoneId).toEpochSecond(), 1000_000_000L);
+ break;
+ case MILLISECONDS:
+ default:
+ CAST_TIMESTAMP_TO_MS = timestamp -> timestamp;
+ EXTRACT_TIMESTAMP_MS_PART = timestamp -> Math.floorMod(timestamp,
1000L);
+ EXTRACT_TIMESTAMP_US_PART = timestamp -> 0L;
+ EXTRACT_TIMESTAMP_NS_PART = timestamp -> 0L;
+ GET_YEAR_TIMESTAMP =
+ year ->
+ Math.multiplyExact(
+ LocalDate.of(year, 1,
1).atStartOfDay(zoneId).toEpochSecond(), 1000L);
+ break;
+ }
+ evaluateFunction = constructEvaluateFunction(field, zoneId);
+ truncatedEqualsFunction = constructTruncatedEqualsFunction(field,
zoneId);
+ }
+
+ protected Function<Long, Long> constructEvaluateFunction(Field field,
ZoneId zoneId) {
+ switch (field) {
+ case YEAR:
+ return timestamp -> (long) convertToZonedDateTime(timestamp,
zoneId).getYear();
+ case QUARTER:
+ return timestamp -> (convertToZonedDateTime(timestamp,
zoneId).getMonthValue() + 2L) / 3L;
+ case MONTH:
+ return timestamp -> (long) convertToZonedDateTime(timestamp,
zoneId).getMonthValue();
+ case WEEK:
+ return timestamp ->
+ convertToZonedDateTime(timestamp,
zoneId).getLong(ALIGNED_WEEK_OF_YEAR);
+ case DAY:
+ case DAY_OF_MONTH:
+ return timestamp -> (long) convertToZonedDateTime(timestamp,
zoneId).getDayOfMonth();
+ case DAY_OF_WEEK:
+ case DOW:
+ return timestamp ->
+ (long) convertToZonedDateTime(timestamp,
zoneId).getDayOfWeek().getValue();
+ case DAY_OF_YEAR:
+ case DOY:
+ return timestamp -> (long) convertToZonedDateTime(timestamp,
zoneId).getDayOfYear();
+ case HOUR:
+ return timestamp -> (long) convertToZonedDateTime(timestamp,
zoneId).getHour();
+ case MINUTE:
+ return timestamp -> (long) convertToZonedDateTime(timestamp,
zoneId).getMinute();
+ case SECOND:
+ return timestamp -> (long) convertToZonedDateTime(timestamp,
zoneId).getSecond();
+ case MS:
+ return EXTRACT_TIMESTAMP_MS_PART;
+ case US:
+ return EXTRACT_TIMESTAMP_US_PART;
+ case NS:
+ return EXTRACT_TIMESTAMP_NS_PART;
+ default:
+ throw new UnsupportedOperationException("Unexpected extract field: "
+ field);
+ }
+ }
+
+ /** Truncate timestamps to based unit then compare */
+ protected BiFunction<Long, Long, Boolean> constructTruncatedEqualsFunction(
+ Field field, ZoneId zoneId) {
+ switch (field) {
+ case YEAR:
+ return (timestamp1, timestamp2) -> true;
+ // base YEAR
+ case QUARTER:
+ case MONTH:
+ case WEEK:
+ case DAY_OF_YEAR:
+ case DOY:
+ return (timestamp1, timestamp2) ->
+ convertToZonedDateTime(timestamp1, zoneId)
+ .withMonth(1)
+ .withDayOfMonth(1)
+ .truncatedTo(ChronoUnit.DAYS)
+ .equals(
+ convertToZonedDateTime(timestamp2, zoneId)
+ .withMonth(1)
+ .withDayOfMonth(1)
+ .truncatedTo(ChronoUnit.DAYS));
+ // base MONTH
+ case DAY:
+ case DAY_OF_MONTH:
+ return (timestamp1, timestamp2) ->
+ convertToZonedDateTime(timestamp1, zoneId)
+ .withDayOfMonth(1)
+ .truncatedTo(ChronoUnit.DAYS)
+ .equals(
+ convertToZonedDateTime(timestamp2, zoneId)
+ .withDayOfMonth(1)
+ .truncatedTo(ChronoUnit.DAYS));
+ // base WEEK
+ case DAY_OF_WEEK:
+ case DOW:
+ return (timestamp1, timestamp2) ->
+ convertToZonedDateTime(timestamp1, zoneId)
+ .with(DayOfWeek.MONDAY)
+ .truncatedTo(ChronoUnit.DAYS)
+ .equals(
+ convertToZonedDateTime(timestamp2, zoneId)
+ .with(DayOfWeek.MONDAY)
+ .truncatedTo(ChronoUnit.DAYS));
+ // base DAY
+ case HOUR:
+ return (timestamp1, timestamp2) ->
+ convertToZonedDateTime(timestamp1, zoneId)
+ .truncatedTo(ChronoUnit.DAYS)
+ .equals(convertToZonedDateTime(timestamp2,
zoneId).truncatedTo(ChronoUnit.DAYS));
+ // base HOUR
+ case MINUTE:
+ return (timestamp1, timestamp2) ->
+ convertToZonedDateTime(timestamp1, zoneId)
+ .truncatedTo(ChronoUnit.HOURS)
+ .equals(convertToZonedDateTime(timestamp2,
zoneId).truncatedTo(ChronoUnit.HOURS));
+ // base MINUTE
+ case SECOND:
+ return (timestamp1, timestamp2) ->
+ convertToZonedDateTime(timestamp1, zoneId)
+ .truncatedTo(ChronoUnit.MINUTES)
+ .equals(
+ convertToZonedDateTime(timestamp2,
zoneId).truncatedTo(ChronoUnit.MINUTES));
+ // base SECOND
+ case MS:
+ return (timestamp1, timestamp2) ->
+ convertToZonedDateTime(timestamp1, zoneId)
+ .truncatedTo(ChronoUnit.SECONDS)
+ .equals(
+ convertToZonedDateTime(timestamp2,
zoneId).truncatedTo(ChronoUnit.SECONDS));
+ // base MS
+ case US:
+ return (timestamp1, timestamp2) ->
+ convertToZonedDateTime(timestamp1, zoneId)
+ .truncatedTo(ChronoUnit.MILLIS)
+ .equals(
+ convertToZonedDateTime(timestamp2,
zoneId).truncatedTo(ChronoUnit.MILLIS));
+ // base US
+ case NS:
+ return (timestamp1, timestamp2) ->
+ convertToZonedDateTime(timestamp1, zoneId)
+ .truncatedTo(ChronoUnit.MICROS)
+ .equals(
+ convertToZonedDateTime(timestamp2,
zoneId).truncatedTo(ChronoUnit.MICROS));
+ default:
+ throw new UnsupportedOperationException("Unexpected extract field: "
+ field);
+ }
+ }
+
+ private ZonedDateTime convertToZonedDateTime(long timestamp, ZoneId
zoneId) {
+ timestamp = CAST_TIMESTAMP_TO_MS.apply(timestamp);
+ return ZonedDateTime.ofInstant(Instant.ofEpochMilli(timestamp), zoneId);
+ }
+
+ protected ExtractTimeCompareFilter(ByteBuffer buffer) {
+ this(
+ ReadWriteIOUtils.readLong(buffer),
+ Field.values()[ReadWriteIOUtils.readInt(buffer)],
+
ZoneId.of(Objects.requireNonNull(ReadWriteIOUtils.readString(buffer))),
+ TimeUnit.values()[ReadWriteIOUtils.readInt(buffer)]);
+ }
+
+ @Override
+ public void serialize(DataOutputStream outputStream) throws IOException {
+ super.serialize(outputStream);
+ ReadWriteIOUtils.write(constant, outputStream);
+ ReadWriteIOUtils.write(field.ordinal(), outputStream);
+ ReadWriteIOUtils.write(zoneId.getId(), outputStream);
+ ReadWriteIOUtils.write(currPrecision.ordinal(), outputStream);
+ }
+
+ @Override
+ public List<TimeRange> getTimeRanges() {
+ return Collections.singletonList(new TimeRange(Long.MIN_VALUE,
Long.MAX_VALUE));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ExtractTimeCompareFilter that = (ExtractTimeCompareFilter) o;
+ return constant == that.constant
+ && zoneId.equals(that.zoneId)
+ && currPrecision == that.currPrecision;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(constant, field, zoneId, currPrecision);
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ OPERATOR_TO_STRING_FORMAT, field, getOperatorType().getSymbol(),
constant);
+ }
+ }
+
+ public static final class ExtractTimeEq extends ExtractTimeCompareFilter {
+
+ public ExtractTimeEq(long constant, Field field, ZoneId zoneId, TimeUnit
currPrecision) {
+ super(constant, field, zoneId, currPrecision);
+ }
+
+ public ExtractTimeEq(ByteBuffer buffer) {
+ super(buffer);
+ }
+
+ @Override
+ public boolean timeSatisfy(long time) {
+ return evaluateFunction.apply(time) == constant;
+ }
+
+ @Override
+ public boolean satisfyStartEndTime(long startTime, long endTime) {
+ return !(truncatedEqualsFunction.apply(startTime, endTime)
+ && (evaluateFunction.apply(endTime) < constant
+ || evaluateFunction.apply(startTime) > constant));
+ }
+
+ @Override
+ public boolean containStartEndTime(long startTime, long endTime) {
+ return truncatedEqualsFunction.apply(startTime, endTime)
+ && evaluateFunction.apply(startTime) == constant
+ && evaluateFunction.apply(endTime) == constant;
+ }
+
+ @Override
+ public List<TimeRange> getTimeRanges() {
+ if (field == Field.YEAR) {
+ int year = (int) constant;
+ return Collections.singletonList(
+ new TimeRange(GET_YEAR_TIMESTAMP.apply(year),
GET_YEAR_TIMESTAMP.apply(year + 1) - 1));
+ }
+ return Collections.singletonList(new TimeRange(Long.MIN_VALUE,
Long.MAX_VALUE));
+ }
+
+ @Override
+ public Filter reverse() {
+ return new ExtractTimeNotEq(constant, field, zoneId, currPrecision);
+ }
+
+ @Override
+ public OperatorType getOperatorType() {
+ return OperatorType.EXTRACT_TIME_EQ;
+ }
+ }
+
+ public static final class ExtractTimeNotEq extends ExtractTimeCompareFilter {
+
+ public ExtractTimeNotEq(long constant, Field field, ZoneId zoneId,
TimeUnit currPrecision) {
+ super(constant, field, zoneId, currPrecision);
+ }
+
+ public ExtractTimeNotEq(ByteBuffer buffer) {
+ super(buffer);
+ }
+
+ @Override
+ public boolean timeSatisfy(long time) {
+ return evaluateFunction.apply(time) != constant;
+ }
+
+ @Override
+ public boolean satisfyStartEndTime(long startTime, long endTime) {
+ return !(truncatedEqualsFunction.apply(startTime, endTime)
+ && evaluateFunction.apply(startTime) == constant
+ && evaluateFunction.apply(endTime) == constant);
+ }
+
+ @Override
+ public boolean containStartEndTime(long startTime, long endTime) {
+ return truncatedEqualsFunction.apply(startTime, endTime)
+ && (evaluateFunction.apply(startTime) > constant
+ || evaluateFunction.apply(endTime) < constant);
+ }
+
+ @Override
+ public List<TimeRange> getTimeRanges() {
+ if (field == Field.YEAR) {
+ List<TimeRange> res = new ArrayList<>();
+ int year = (int) constant;
+ res.add(new TimeRange(Long.MIN_VALUE, GET_YEAR_TIMESTAMP.apply(year) -
1));
+ res.add(new TimeRange(GET_YEAR_TIMESTAMP.apply(year + 1),
Long.MAX_VALUE));
+ return res;
+ }
+ return Collections.singletonList(new TimeRange(Long.MIN_VALUE,
Long.MAX_VALUE));
+ }
+
+ @Override
+ public Filter reverse() {
+ return new ExtractTimeEq(constant, field, zoneId, currPrecision);
+ }
+
+ @Override
+ public OperatorType getOperatorType() {
+ return OperatorType.EXTRACT_TIME_NEQ;
+ }
+ }
+
+ public static final class ExtractTimeLt extends ExtractTimeCompareFilter {
+
+ public ExtractTimeLt(long constant, Field field, ZoneId zoneId, TimeUnit
currPrecision) {
+ super(constant, field, zoneId, currPrecision);
+ }
+
+ public ExtractTimeLt(ByteBuffer buffer) {
+ super(buffer);
+ }
+
+ @Override
+ public boolean timeSatisfy(long time) {
+ return evaluateFunction.apply(time) < constant;
+ }
+
+ @Override
+ public boolean satisfyStartEndTime(long startTime, long endTime) {
+ return !(truncatedEqualsFunction.apply(startTime, endTime)
+ && evaluateFunction.apply(startTime) >= constant);
+ }
+
+ @Override
+ public boolean containStartEndTime(long startTime, long endTime) {
+ return truncatedEqualsFunction.apply(startTime, endTime)
+ && evaluateFunction.apply(endTime) < constant;
+ }
+
+ @Override
+ public List<TimeRange> getTimeRanges() {
+ if (field == Field.YEAR) {
+ int year = (int) constant;
+ return Collections.singletonList(
+ new TimeRange(Long.MIN_VALUE, GET_YEAR_TIMESTAMP.apply(year) - 1));
+ }
+ return Collections.singletonList(new TimeRange(Long.MIN_VALUE,
Long.MAX_VALUE));
+ }
+
+ @Override
+ public Filter reverse() {
+ return new ExtractTimeGtEq(constant, field, zoneId, currPrecision);
+ }
+
+ @Override
+ public OperatorType getOperatorType() {
+ return OperatorType.EXTRACT_TIME_LT;
+ }
+ }
+
+ public static final class ExtractTimeLtEq extends ExtractTimeCompareFilter {
+
+ public ExtractTimeLtEq(long constant, Field field, ZoneId zoneId, TimeUnit
currPrecision) {
+ super(constant, field, zoneId, currPrecision);
+ }
+
+ public ExtractTimeLtEq(ByteBuffer buffer) {
+ super(buffer);
+ }
+
+ @Override
+ public boolean timeSatisfy(long time) {
+ return evaluateFunction.apply(time) <= constant;
+ }
+
+ @Override
+ public boolean satisfyStartEndTime(long startTime, long endTime) {
+ return !(truncatedEqualsFunction.apply(startTime, endTime)
+ && evaluateFunction.apply(startTime) > constant);
+ }
+
+ @Override
+ public boolean containStartEndTime(long startTime, long endTime) {
+ return truncatedEqualsFunction.apply(startTime, endTime)
+ && evaluateFunction.apply(endTime) <= constant;
+ }
+
+ @Override
+ public List<TimeRange> getTimeRanges() {
+ if (field == Field.YEAR) {
+ int year = (int) constant;
+ return Collections.singletonList(
+ new TimeRange(Long.MIN_VALUE, GET_YEAR_TIMESTAMP.apply(year + 1) -
1));
+ }
+ return Collections.singletonList(new TimeRange(Long.MIN_VALUE,
Long.MAX_VALUE));
+ }
+
+ @Override
+ public Filter reverse() {
+ return new ExtractTimeGt(constant, field, zoneId, currPrecision);
+ }
+
+ @Override
+ public OperatorType getOperatorType() {
+ return OperatorType.EXTRACT_TIME_LTEQ;
+ }
+ }
+
+ public static final class ExtractTimeGt extends ExtractTimeCompareFilter {
+
+ public ExtractTimeGt(long constant, Field field, ZoneId zoneId, TimeUnit
currPrecision) {
+ super(constant, field, zoneId, currPrecision);
+ }
+
+ public ExtractTimeGt(ByteBuffer buffer) {
+ super(buffer);
+ }
+
+ @Override
+ public boolean timeSatisfy(long time) {
+ return evaluateFunction.apply(time) > constant;
+ }
+
+ @Override
+ public boolean satisfyStartEndTime(long startTime, long endTime) {
+ return !(truncatedEqualsFunction.apply(startTime, endTime)
+ && evaluateFunction.apply(endTime) <= constant);
+ }
+
+ @Override
+ public boolean containStartEndTime(long startTime, long endTime) {
+ return truncatedEqualsFunction.apply(startTime, endTime)
+ && evaluateFunction.apply(startTime) > constant;
+ }
+
+ @Override
+ public List<TimeRange> getTimeRanges() {
+ if (field == Field.YEAR) {
+ int year = (int) constant;
+ return Collections.singletonList(
+ new TimeRange(GET_YEAR_TIMESTAMP.apply(year + 1), Long.MAX_VALUE));
+ }
+ return Collections.singletonList(new TimeRange(Long.MIN_VALUE,
Long.MAX_VALUE));
+ }
+
+ @Override
+ public Filter reverse() {
+ return new ExtractTimeLtEq(constant, field, zoneId, currPrecision);
+ }
+
+ @Override
+ public OperatorType getOperatorType() {
+ return OperatorType.EXTRACT_TIME_GT;
+ }
+ }
+
+ public static final class ExtractTimeGtEq extends ExtractTimeCompareFilter {
+
+ public ExtractTimeGtEq(long constant, Field field, ZoneId zoneId, TimeUnit
currPrecision) {
+ super(constant, field, zoneId, currPrecision);
+ }
+
+ public ExtractTimeGtEq(ByteBuffer buffer) {
+ super(buffer);
+ }
+
+ @Override
+ public boolean timeSatisfy(long time) {
+ return evaluateFunction.apply(time) >= constant;
+ }
+
+ @Override
+ public boolean satisfyStartEndTime(long startTime, long endTime) {
+ return !(truncatedEqualsFunction.apply(startTime, endTime)
+ && evaluateFunction.apply(endTime) < constant);
+ }
+
+ @Override
+ public boolean containStartEndTime(long startTime, long endTime) {
+ return truncatedEqualsFunction.apply(startTime, endTime)
+ && evaluateFunction.apply(startTime) >= constant;
+ }
+
+ @Override
+ public List<TimeRange> getTimeRanges() {
+ if (field == Field.YEAR) {
+ int year = (int) constant;
+ return Collections.singletonList(
+ new TimeRange(GET_YEAR_TIMESTAMP.apply(year), Long.MAX_VALUE));
+ }
+ return Collections.singletonList(new TimeRange(Long.MIN_VALUE,
Long.MAX_VALUE));
+ }
+
+ @Override
+ public Filter reverse() {
+ return new ExtractTimeLt(constant, field, zoneId, currPrecision);
+ }
+
+ @Override
+ public OperatorType getOperatorType() {
+ return OperatorType.EXTRACT_TIME_GTEQ;
+ }
+ }
+}
diff --git
a/java/tsfile/src/test/java/org/apache/tsfile/read/filter/ExtractTimeFilterTest.java
b/java/tsfile/src/test/java/org/apache/tsfile/read/filter/ExtractTimeFilterTest.java
new file mode 100644
index 00000000..aff2001c
--- /dev/null
+++
b/java/tsfile/src/test/java/org/apache/tsfile/read/filter/ExtractTimeFilterTest.java
@@ -0,0 +1,436 @@
+/*
+ * 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.tsfile.read.filter;
+
+import org.apache.tsfile.read.common.TimeRange;
+import org.apache.tsfile.read.filter.basic.Filter;
+import org.apache.tsfile.read.filter.factory.TimeFilterApi;
+import org.apache.tsfile.read.filter.operator.ExtractTimeFilterOperators.Field;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.time.ZoneId;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.concurrent.TimeUnit;
+
+public class ExtractTimeFilterTest {
+ // 2025/07/08 09:18:51 00:00:00+8:00
+ private final long testTime1 = 1751937531000L;
+ // 2025/07/08 10:18:51 00:00:00+8:00
+ private final long testTime2 = 1751941131000L;
+ private final ZoneId zoneId1 = ZoneId.of("+0000");
+ private final ZoneId zoneId2 = ZoneId.of("+0800");
+
+ private final long DAY_INTERVAL = TimeUnit.DAYS.toMillis(1);
+
+ @Test
+ public void testEq() {
+ // 1751936400000L -> 2025/07/08 09:00:00+8:00
+ // 1751940000000L -> 2025/07/08 10:00:00+8:00
+ Filter extractTimeEq1 =
+ TimeFilterApi.extractTimeEq(1, Field.HOUR, zoneId1,
TimeUnit.MILLISECONDS);
+ Assert.assertTrue(extractTimeEq1.satisfy(testTime1, 100));
+ Assert.assertFalse(extractTimeEq1.satisfy(testTime2, 100));
+ Assert.assertTrue(extractTimeEq1.satisfyStartEndTime(testTime1 - 1,
testTime1 + 1));
+ Assert.assertTrue(extractTimeEq1.satisfyStartEndTime(1751936400000L,
2751936400000L));
+ Assert.assertTrue(extractTimeEq1.satisfyStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertFalse(extractTimeEq1.satisfyStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+
+ Assert.assertTrue(extractTimeEq1.containStartEndTime(1751936400000L,
1751940000000L - 1));
+ Assert.assertTrue(extractTimeEq1.containStartEndTime(testTime1 - 1,
testTime1 + 1));
+ Assert.assertFalse(extractTimeEq1.containStartEndTime(1751936400000L - 1,
1751940000000L));
+ Assert.assertFalse(extractTimeEq1.containStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertFalse(extractTimeEq1.containStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+ Assert.assertFalse(extractTimeEq1.containStartEndTime(1751936400000L,
2751936400000L));
+
+ Assert.assertEquals(
+ Collections.singletonList(new TimeRange(Long.MIN_VALUE,
Long.MAX_VALUE)),
+ extractTimeEq1.getTimeRanges());
+
+ Filter extractTimeEq2 =
+ TimeFilterApi.extractTimeEq(9, Field.HOUR, zoneId2,
TimeUnit.MILLISECONDS);
+ Assert.assertTrue(extractTimeEq2.satisfy(testTime1, 100));
+ Assert.assertFalse(extractTimeEq2.satisfy(testTime2, 100));
+ Assert.assertTrue(extractTimeEq2.satisfyStartEndTime(testTime1 - 1,
testTime1 + 1));
+ Assert.assertTrue(extractTimeEq2.satisfyStartEndTime(1751936400000L,
2751936400000L));
+ Assert.assertTrue(extractTimeEq2.satisfyStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertFalse(extractTimeEq2.satisfyStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+
+ Assert.assertTrue(extractTimeEq2.containStartEndTime(1751936400000L,
1751940000000L - 1));
+ Assert.assertTrue(extractTimeEq2.containStartEndTime(testTime1 - 1,
testTime1 + 1));
+ Assert.assertFalse(extractTimeEq2.containStartEndTime(1751936400000L - 1,
1751940000000L));
+ Assert.assertFalse(extractTimeEq2.containStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertFalse(extractTimeEq2.containStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+ Assert.assertFalse(extractTimeEq2.containStartEndTime(1751936400000L,
2751936400000L));
+
+ Assert.assertEquals(
+ Collections.singletonList(new TimeRange(Long.MIN_VALUE,
Long.MAX_VALUE)),
+ extractTimeEq2.getTimeRanges());
+
+ // test other extracted results
+ extractTimeEq1 = TimeFilterApi.extractTimeEq(2025, Field.YEAR, zoneId1,
TimeUnit.MILLISECONDS);
+ Assert.assertTrue(extractTimeEq1.satisfy(testTime1, 100));
+ // 1735689600000L -> 2025/01/01 00:00:00+00:00
+ // 1767225600000L -> 2026/01/01 00:00:00+00:00
+ Assert.assertEquals(
+ Collections.singletonList(new TimeRange(1735689600000L, 1767225600000L
- 1)),
+ extractTimeEq1.getTimeRanges());
+
+ extractTimeEq1 = TimeFilterApi.extractTimeEq(3, Field.QUARTER, zoneId1,
TimeUnit.MILLISECONDS);
+ Assert.assertTrue(extractTimeEq1.satisfy(testTime1, 100));
+ Assert.assertEquals(
+ Collections.singletonList(new TimeRange(Long.MIN_VALUE,
Long.MAX_VALUE)),
+ extractTimeEq1.getTimeRanges());
+
+ extractTimeEq1 = TimeFilterApi.extractTimeEq(7, Field.MONTH, zoneId1,
TimeUnit.MILLISECONDS);
+ Assert.assertTrue(extractTimeEq1.satisfy(testTime1, 100));
+ Assert.assertEquals(
+ Collections.singletonList(new TimeRange(Long.MIN_VALUE,
Long.MAX_VALUE)),
+ extractTimeEq1.getTimeRanges());
+
+ extractTimeEq1 = TimeFilterApi.extractTimeEq(27, Field.WEEK, zoneId1,
TimeUnit.MILLISECONDS);
+ Assert.assertTrue(extractTimeEq1.satisfy(testTime1, 100));
+ Assert.assertEquals(
+ Collections.singletonList(new TimeRange(Long.MIN_VALUE,
Long.MAX_VALUE)),
+ extractTimeEq1.getTimeRanges());
+
+ extractTimeEq1 = TimeFilterApi.extractTimeEq(8, Field.DAY, zoneId1,
TimeUnit.MILLISECONDS);
+ Assert.assertTrue(extractTimeEq1.satisfy(testTime1, 100));
+ Assert.assertEquals(
+ Collections.singletonList(new TimeRange(Long.MIN_VALUE,
Long.MAX_VALUE)),
+ extractTimeEq1.getTimeRanges());
+
+ extractTimeEq1 =
+ TimeFilterApi.extractTimeEq(8, Field.DAY_OF_MONTH, zoneId1,
TimeUnit.MILLISECONDS);
+ Assert.assertTrue(extractTimeEq1.satisfy(testTime1, 100));
+ Assert.assertEquals(
+ Collections.singletonList(new TimeRange(Long.MIN_VALUE,
Long.MAX_VALUE)),
+ extractTimeEq1.getTimeRanges());
+
+ extractTimeEq1 =
+ TimeFilterApi.extractTimeEq(2, Field.DAY_OF_WEEK, zoneId1,
TimeUnit.MILLISECONDS);
+ Assert.assertTrue(extractTimeEq1.satisfy(testTime1, 100));
+ Assert.assertEquals(
+ Collections.singletonList(new TimeRange(Long.MIN_VALUE,
Long.MAX_VALUE)),
+ extractTimeEq1.getTimeRanges());
+
+ extractTimeEq1 =
+ TimeFilterApi.extractTimeEq(189, Field.DAY_OF_YEAR, zoneId1,
TimeUnit.MILLISECONDS);
+ Assert.assertTrue(extractTimeEq1.satisfy(testTime1, 100));
+ Assert.assertEquals(
+ Collections.singletonList(new TimeRange(Long.MIN_VALUE,
Long.MAX_VALUE)),
+ extractTimeEq1.getTimeRanges());
+
+ extractTimeEq1 = TimeFilterApi.extractTimeEq(18, Field.MINUTE, zoneId1,
TimeUnit.MILLISECONDS);
+ Assert.assertTrue(extractTimeEq1.satisfy(testTime1, 100));
+ Assert.assertEquals(
+ Collections.singletonList(new TimeRange(Long.MIN_VALUE,
Long.MAX_VALUE)),
+ extractTimeEq1.getTimeRanges());
+
+ extractTimeEq1 = TimeFilterApi.extractTimeEq(51, Field.SECOND, zoneId1,
TimeUnit.MILLISECONDS);
+ Assert.assertTrue(extractTimeEq1.satisfy(testTime1, 100));
+ Assert.assertEquals(
+ Collections.singletonList(new TimeRange(Long.MIN_VALUE,
Long.MAX_VALUE)),
+ extractTimeEq1.getTimeRanges());
+
+ extractTimeEq1 = TimeFilterApi.extractTimeEq(0, Field.MS, zoneId1,
TimeUnit.MILLISECONDS);
+ Assert.assertTrue(extractTimeEq1.satisfy(testTime1, 100));
+ Assert.assertEquals(
+ Collections.singletonList(new TimeRange(Long.MIN_VALUE,
Long.MAX_VALUE)),
+ extractTimeEq1.getTimeRanges());
+
+ extractTimeEq1 = TimeFilterApi.extractTimeEq(0, Field.US, zoneId1,
TimeUnit.MILLISECONDS);
+ Assert.assertTrue(extractTimeEq1.satisfy(testTime1, 100));
+ Assert.assertEquals(
+ Collections.singletonList(new TimeRange(Long.MIN_VALUE,
Long.MAX_VALUE)),
+ extractTimeEq1.getTimeRanges());
+
+ extractTimeEq1 = TimeFilterApi.extractTimeEq(0, Field.NS, zoneId1,
TimeUnit.MILLISECONDS);
+ Assert.assertTrue(extractTimeEq1.satisfy(testTime1, 100));
+ Assert.assertEquals(
+ Collections.singletonList(new TimeRange(Long.MIN_VALUE,
Long.MAX_VALUE)),
+ extractTimeEq1.getTimeRanges());
+
+ extractTimeEq1 = TimeFilterApi.extractTimeEq(0, Field.MS, zoneId1,
TimeUnit.MICROSECONDS);
+ Assert.assertTrue(extractTimeEq1.satisfy(1751937531000025L, 100));
+ extractTimeEq1 = TimeFilterApi.extractTimeEq(25, Field.US, zoneId1,
TimeUnit.MICROSECONDS);
+ Assert.assertTrue(extractTimeEq1.satisfy(1751937531000025L, 100));
+ extractTimeEq1 = TimeFilterApi.extractTimeEq(0, Field.NS, zoneId1,
TimeUnit.MICROSECONDS);
+ Assert.assertTrue(extractTimeEq1.satisfy(1751937531000025L, 100));
+
+ extractTimeEq1 = TimeFilterApi.extractTimeEq(0, Field.MS, zoneId1,
TimeUnit.NANOSECONDS);
+ Assert.assertTrue(extractTimeEq1.satisfy(1751937531000025026L, 100));
+ extractTimeEq1 = TimeFilterApi.extractTimeEq(25, Field.US, zoneId1,
TimeUnit.NANOSECONDS);
+ Assert.assertTrue(extractTimeEq1.satisfy(1751937531000025026L, 100));
+ extractTimeEq1 = TimeFilterApi.extractTimeEq(26, Field.NS, zoneId1,
TimeUnit.NANOSECONDS);
+ Assert.assertTrue(extractTimeEq1.satisfy(1751937531000025026L, 100));
+ }
+
+ @Test
+ public void testNotEq() {
+ // 1751936400000L -> 2025/07/08 09:00:00+8:00
+ // 1751940000000L -> 2025/07/08 10:00:00+8:00
+ Filter filter1 = TimeFilterApi.extractTimeNotEq(1, Field.HOUR, zoneId1,
TimeUnit.MILLISECONDS);
+ Assert.assertFalse(filter1.satisfy(testTime1, 100));
+ Assert.assertTrue(filter1.satisfy(testTime2, 100));
+ Assert.assertFalse(filter1.satisfyStartEndTime(testTime1 - 1, testTime1 +
1));
+ Assert.assertFalse(filter1.satisfyStartEndTime(testTime1 - 1, testTime1 +
1));
+ Assert.assertTrue(filter1.satisfyStartEndTime(1751936400000L,
2751936400000L));
+ Assert.assertTrue(filter1.satisfyStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertTrue(filter1.satisfyStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+
+ Assert.assertFalse(filter1.containStartEndTime(1751936400000L,
1751940000000L - 1));
+ Assert.assertFalse(filter1.containStartEndTime(testTime1 - 1, testTime1 +
1));
+ Assert.assertFalse(filter1.containStartEndTime(1751936400000L - 1,
1751940000000L));
+ Assert.assertFalse(filter1.containStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertTrue(filter1.containStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+ Assert.assertFalse(filter1.containStartEndTime(1751936400000L,
2751936400000L));
+
+ // attention: actual contains, but the method returns false
+ Assert.assertFalse(
+ filter1.containStartEndTime(1751940000000L, 1751936400000L +
DAY_INTERVAL - 1));
+
+ // 1735689600000L -> 2025/01/01 00:00:00+00:00
+ // 1767225600000L -> 2026/01/01 00:00:00+00:00
+ filter1 = TimeFilterApi.extractTimeNotEq(2025, Field.YEAR, zoneId1,
TimeUnit.MICROSECONDS);
+ Assert.assertEquals(
+ Arrays.asList(
+ new TimeRange(Long.MIN_VALUE, 1735689600000_000L - 1),
+ new TimeRange(1767225600000_000L, Long.MAX_VALUE)),
+ filter1.getTimeRanges());
+
+ Filter filter2 = TimeFilterApi.extractTimeNotEq(9, Field.HOUR, zoneId2,
TimeUnit.MILLISECONDS);
+ Assert.assertFalse(filter2.satisfy(testTime1, 100));
+ Assert.assertTrue(filter2.satisfy(testTime2, 100));
+ Assert.assertFalse(filter2.satisfyStartEndTime(testTime1 - 1, testTime1 +
1));
+ Assert.assertTrue(filter2.satisfyStartEndTime(1751936400000L,
2751936400000L));
+ Assert.assertTrue(filter2.satisfyStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertTrue(filter2.satisfyStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+
+ Assert.assertFalse(filter2.containStartEndTime(1751936400000L,
1751940000000L - 1));
+ Assert.assertFalse(filter2.containStartEndTime(testTime1 - 1, testTime1 +
1));
+ Assert.assertFalse(filter2.containStartEndTime(1751936400000L - 1,
1751940000000L));
+ Assert.assertFalse(filter2.containStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertTrue(filter2.containStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+ Assert.assertFalse(filter2.containStartEndTime(1751936400000L,
2751936400000L));
+
+ // attention: actual contains, but the method returns false
+ Assert.assertFalse(
+ filter2.containStartEndTime(1751940000000L, 1751936400000L +
DAY_INTERVAL - 1));
+
+ // 1735660800000L -> 2025/01/01 00:00:00+08:00
+ // 1767196800000L -> 2026/01/01 00:00:00+08:00
+ filter2 = TimeFilterApi.extractTimeNotEq(2025, Field.YEAR, zoneId2,
TimeUnit.MICROSECONDS);
+ Assert.assertEquals(
+ Arrays.asList(
+ new TimeRange(Long.MIN_VALUE, 1735660800000_000L - 1),
+ new TimeRange(1767196800000_000L, Long.MAX_VALUE)),
+ filter2.getTimeRanges());
+ }
+
+ @Test
+ public void testGt() {
+ // 1751936400000L -> 2025/07/08 09:00:00+8:00
+ // 1751940000000L -> 2025/07/08 10:00:00+8:00
+ Filter filter1 = TimeFilterApi.extractTimeGt(5, Field.HOUR, zoneId1,
TimeUnit.MILLISECONDS);
+ Assert.assertFalse(filter1.satisfy(testTime1, 100));
+ Assert.assertFalse(filter1.satisfy(testTime2, 100));
+ Assert.assertFalse(filter1.satisfyStartEndTime(testTime1 - 1, testTime1 +
1));
+ Assert.assertTrue(filter1.satisfyStartEndTime(1751936400000L,
2751936400000L));
+ Assert.assertFalse(filter1.satisfyStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertFalse(filter1.satisfyStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+
+ Assert.assertFalse(filter1.containStartEndTime(1751936400000L,
1751940000000L - 1));
+ Assert.assertFalse(filter1.containStartEndTime(testTime1 - 1, testTime1 +
1));
+ Assert.assertFalse(filter1.containStartEndTime(1751936400000L - 1,
1751940000000L));
+ Assert.assertFalse(filter1.containStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertFalse(filter1.containStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+ Assert.assertFalse(filter1.containStartEndTime(1751936400000L,
2751936400000L));
+
+ // 1735689600000L -> 2025/01/01 00:00:00+00:00
+ // 1767225600000L -> 2026/01/01 00:00:00+00:00
+ filter1 = TimeFilterApi.extractTimeGt(2025, Field.YEAR, zoneId1,
TimeUnit.MICROSECONDS);
+ Assert.assertEquals(
+ Collections.singletonList(new TimeRange(1767225600000_000L,
Long.MAX_VALUE)),
+ filter1.getTimeRanges());
+
+ Filter filter2 = TimeFilterApi.extractTimeGt(5, Field.HOUR, zoneId2,
TimeUnit.MILLISECONDS);
+ Assert.assertTrue(filter2.satisfy(testTime1, 100));
+ Assert.assertTrue(filter2.satisfy(testTime2, 100));
+ Assert.assertTrue(filter2.satisfyStartEndTime(testTime1 - 1, testTime1 +
1));
+ Assert.assertTrue(filter2.satisfyStartEndTime(1751936400000L,
2751936400000L));
+ Assert.assertTrue(filter2.satisfyStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertTrue(filter2.satisfyStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+
+ Assert.assertTrue(filter2.containStartEndTime(1751936400000L,
1751940000000L - 1));
+ Assert.assertTrue(filter2.containStartEndTime(testTime1 - 1, testTime1 +
1));
+ Assert.assertTrue(filter2.containStartEndTime(1751936400000L - 1,
1751940000000L));
+ Assert.assertTrue(filter2.containStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertTrue(filter2.containStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+ Assert.assertFalse(filter2.containStartEndTime(1751936400000L,
2751936400000L));
+
+ Filter filter3 = TimeFilterApi.extractTimeGt(9, Field.HOUR, zoneId2,
TimeUnit.MILLISECONDS);
+ Assert.assertFalse(filter3.satisfy(testTime1, 100));
+ Assert.assertTrue(filter3.satisfy(testTime2, 100));
+ Assert.assertTrue(filter3.satisfyStartEndTime(testTime1, testTime2));
+ Assert.assertFalse(filter3.containStartEndTime(testTime1, testTime2));
+ }
+
+ @Test
+ public void testGtEq() {
+ Filter filter1 = TimeFilterApi.extractTimeGtEq(5, Field.HOUR, zoneId1,
TimeUnit.MILLISECONDS);
+ Assert.assertFalse(filter1.satisfy(testTime1, 100));
+ Assert.assertFalse(filter1.satisfy(testTime2, 100));
+ Assert.assertFalse(filter1.satisfyStartEndTime(testTime1 - 1, testTime1 +
1));
+ Assert.assertTrue(filter1.satisfyStartEndTime(1751936400000L,
2751936400000L));
+ Assert.assertFalse(filter1.satisfyStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertFalse(filter1.satisfyStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+
+ Assert.assertFalse(filter1.containStartEndTime(1751936400000L,
1751940000000L - 1));
+ Assert.assertFalse(filter1.containStartEndTime(testTime1 - 1, testTime1 +
1));
+ Assert.assertFalse(filter1.containStartEndTime(1751936400000L - 1,
1751940000000L));
+ Assert.assertFalse(filter1.containStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertFalse(filter1.containStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+ Assert.assertFalse(filter1.containStartEndTime(1751936400000L,
2751936400000L));
+
+ // 1735689600000L -> 2025/01/01 00:00:00+00:00
+ // 1767225600000L -> 2026/01/01 00:00:00+00:00
+ filter1 = TimeFilterApi.extractTimeGtEq(2025, Field.YEAR, zoneId1,
TimeUnit.NANOSECONDS);
+ Assert.assertEquals(
+ Collections.singletonList(new TimeRange(1735689600000_000_000L,
Long.MAX_VALUE)),
+ filter1.getTimeRanges());
+
+ Filter filter2 = TimeFilterApi.extractTimeGtEq(5, Field.HOUR, zoneId2,
TimeUnit.MILLISECONDS);
+ Assert.assertTrue(filter2.satisfy(testTime1, 100));
+ Assert.assertTrue(filter2.satisfy(testTime2, 100));
+ Assert.assertTrue(filter2.satisfyStartEndTime(testTime1 - 1, testTime1 +
1));
+ Assert.assertTrue(filter2.satisfyStartEndTime(1751936400000L,
2751936400000L));
+ Assert.assertTrue(filter2.satisfyStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertTrue(filter2.satisfyStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+
+ Assert.assertTrue(filter2.containStartEndTime(1751936400000L,
1751940000000L - 1));
+ Assert.assertTrue(filter2.containStartEndTime(testTime1 - 1, testTime1 +
1));
+ Assert.assertTrue(filter2.containStartEndTime(1751936400000L - 1,
1751940000000L));
+ Assert.assertTrue(filter2.containStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertTrue(filter2.containStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+ Assert.assertFalse(filter2.containStartEndTime(1751936400000L,
2751936400000L));
+
+ Filter filter3 = TimeFilterApi.extractTimeGtEq(9, Field.HOUR, zoneId2,
TimeUnit.MILLISECONDS);
+ Assert.assertTrue(filter3.satisfy(testTime1, 100));
+ Assert.assertTrue(filter3.satisfy(testTime2, 100));
+ Assert.assertTrue(filter3.satisfyStartEndTime(testTime1, testTime2));
+ Assert.assertTrue(filter3.containStartEndTime(testTime1, testTime2));
+ }
+
+ @Test
+ public void testLt() {
+ Filter filter1 = TimeFilterApi.extractTimeLt(5, Field.HOUR, zoneId1,
TimeUnit.MILLISECONDS);
+ Assert.assertTrue(filter1.satisfy(testTime1, 100));
+ Assert.assertTrue(filter1.satisfy(testTime2, 100));
+ Assert.assertTrue(filter1.satisfyStartEndTime(testTime1 - 1, testTime1 +
1));
+ Assert.assertTrue(filter1.satisfyStartEndTime(1751936400000L,
2751936400000L));
+ Assert.assertTrue(filter1.satisfyStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertTrue(filter1.satisfyStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+
+ Assert.assertTrue(filter1.containStartEndTime(1751936400000L,
1751940000000L - 1));
+ Assert.assertTrue(filter1.containStartEndTime(testTime1 - 1, testTime1 +
1));
+ Assert.assertTrue(filter1.containStartEndTime(1751936400000L - 1,
1751940000000L));
+ Assert.assertTrue(filter1.containStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertTrue(filter1.containStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+ Assert.assertFalse(filter1.containStartEndTime(1751936400000L,
2751936400000L));
+
+ // 1735689600000L -> 2025/01/01 00:00:00+00:00
+ // 1767225600000L -> 2026/01/01 00:00:00+00:00
+ filter1 = TimeFilterApi.extractTimeLt(2025, Field.YEAR, zoneId1,
TimeUnit.MILLISECONDS);
+ Assert.assertEquals(
+ Collections.singletonList(new TimeRange(Long.MIN_VALUE, 1735689600000L
- 1)),
+ filter1.getTimeRanges());
+
+ Filter filter2 = TimeFilterApi.extractTimeLt(5, Field.HOUR, zoneId2,
TimeUnit.MILLISECONDS);
+ Assert.assertFalse(filter2.satisfy(testTime1, 100));
+ Assert.assertFalse(filter2.satisfy(testTime2, 100));
+ Assert.assertFalse(filter2.satisfyStartEndTime(testTime1 - 1, testTime1 +
1));
+ Assert.assertTrue(filter2.satisfyStartEndTime(1751936400000L,
2751936400000L));
+ Assert.assertFalse(filter2.satisfyStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertFalse(filter2.satisfyStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+
+ Assert.assertFalse(filter2.containStartEndTime(1751936400000L,
1751940000000L - 1));
+ Assert.assertFalse(filter2.containStartEndTime(testTime1 - 1, testTime1 +
1));
+ Assert.assertFalse(filter2.containStartEndTime(1751936400000L - 1,
1751940000000L));
+ Assert.assertFalse(filter2.containStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertFalse(filter2.containStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+ Assert.assertFalse(filter2.containStartEndTime(1751936400000L,
2751936400000L));
+
+ Filter filter3 = TimeFilterApi.extractTimeGtEq(9, Field.HOUR, zoneId2,
TimeUnit.MILLISECONDS);
+ Assert.assertTrue(filter3.satisfy(testTime1, 100));
+ Assert.assertTrue(filter3.satisfy(testTime2, 100));
+ Assert.assertTrue(filter3.satisfyStartEndTime(testTime1, testTime2));
+ Assert.assertTrue(filter3.containStartEndTime(testTime1, testTime2));
+ }
+
+ @Test
+ public void testLtEq() {
+ // 1751936400000L -> 2025/07/08 09:00:00+8:00
+ // 1751940000000L -> 2025/07/08 10:00:00+8:00
+ Filter filter1 = TimeFilterApi.extractTimeLtEq(5, Field.HOUR, zoneId1,
TimeUnit.MILLISECONDS);
+ Assert.assertTrue(filter1.satisfy(testTime1, 100));
+ Assert.assertTrue(filter1.satisfy(testTime2, 100));
+ Assert.assertTrue(filter1.satisfyStartEndTime(testTime1 - 1, testTime1 +
1));
+ Assert.assertTrue(filter1.satisfyStartEndTime(1751936400000L,
2751936400000L));
+ Assert.assertTrue(filter1.satisfyStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertTrue(filter1.satisfyStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+
+ Assert.assertTrue(filter1.containStartEndTime(1751936400000L,
1751940000000L - 1));
+ Assert.assertTrue(filter1.containStartEndTime(testTime1 - 1, testTime1 +
1));
+ Assert.assertTrue(filter1.containStartEndTime(1751936400000L - 1,
1751940000000L));
+ Assert.assertTrue(filter1.containStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertTrue(filter1.containStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+ Assert.assertFalse(filter1.containStartEndTime(1751936400000L,
2751936400000L));
+
+ // 1735689600000L -> 2025/01/01 00:00:00+00:00
+ // 1767225600000L -> 2026/01/01 00:00:00+00:00
+ filter1 = TimeFilterApi.extractTimeLtEq(2025, Field.YEAR, zoneId1,
TimeUnit.MILLISECONDS);
+ Assert.assertEquals(
+ Collections.singletonList(new TimeRange(Long.MIN_VALUE, 1767225600000L
- 1)),
+ filter1.getTimeRanges());
+
+ Filter filter2 = TimeFilterApi.extractTimeLt(5, Field.HOUR, zoneId2,
TimeUnit.MILLISECONDS);
+ Assert.assertFalse(filter2.satisfy(testTime1, 100));
+ Assert.assertFalse(filter2.satisfy(testTime2, 100));
+ Assert.assertFalse(filter2.satisfyStartEndTime(testTime1 - 1, testTime1 +
1));
+ Assert.assertTrue(filter2.satisfyStartEndTime(1751936400000L,
2751936400000L));
+ Assert.assertFalse(filter2.satisfyStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertFalse(filter2.satisfyStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+
+ Assert.assertFalse(filter2.containStartEndTime(1751936400000L,
1751940000000L - 1));
+ Assert.assertFalse(filter2.containStartEndTime(testTime1 - 1, testTime1 +
1));
+ Assert.assertFalse(filter2.containStartEndTime(1751936400000L - 1,
1751940000000L));
+ Assert.assertFalse(filter2.containStartEndTime(1751936400000L - 1,
testTime1 + 1));
+ Assert.assertFalse(filter2.containStartEndTime(1751936400000L - 2,
1751936400000L - 1));
+ Assert.assertFalse(filter2.containStartEndTime(1751936400000L,
2751936400000L));
+
+ Filter filter3 = TimeFilterApi.extractTimeGt(9, Field.HOUR, zoneId2,
TimeUnit.MILLISECONDS);
+ Assert.assertFalse(filter3.satisfy(testTime1, 100));
+ Assert.assertTrue(filter3.satisfy(testTime2, 100));
+ Assert.assertTrue(filter3.satisfyStartEndTime(testTime1, testTime2));
+ Assert.assertFalse(filter3.containStartEndTime(testTime1, testTime2));
+ }
+}