Skip to content

Commit e26bb6a

Browse files
committed
Fix date_bin month bins counting intra-second milliseconds twice
convertToTimestamp added toEpochMilli() and the full nanosecond-of-second, so a month or year bin boundary that is not an exact second was shifted by those milliseconds. Encode the Instant from its epoch second and nanosecond-of-second instead. Hour and day bins are unchanged. Fixes #19264. Signed-off-by: DaZuiZui <y51288033@outlook.com>
1 parent 8a2f85a commit e26bb6a

2 files changed

Lines changed: 67 additions & 8 deletions

File tree

‎iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/DateBinFunctionColumnTransformer.java‎

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,19 +90,22 @@ private static LocalDateTime convertToLocalDateTime(long timestamp, ZoneId zoneI
9090
}
9191

9292
private static long convertToTimestamp(LocalDateTime dateTime, ZoneId zoneId) {
93-
// Converting LocalDateTime to Seconds since epoch
94-
long epochMilliSecond = dateTime.atZone(zoneId).toInstant().toEpochMilli();
95-
// Get the nanoseconds section
96-
long nanoAdjustment = dateTime.getNano();
93+
// Encode from the epoch second and the nanosecond-of-second. toEpochMilli() already
94+
// contains the whole milliseconds of that second, so adding getNano() on top counts
95+
// those milliseconds a second time.
96+
Instant instant = dateTime.atZone(zoneId).toInstant();
97+
long seconds = instant.getEpochSecond();
98+
long nanoOfSecond = instant.getNano();
9799

98100
switch (CommonDescriptor.getInstance().getConfig().getTimestampPrecision()) {
99101
case "ms":
100-
return epochMilliSecond + nanoAdjustment / NANOSECONDS_IN_MILLISECOND;
102+
return Math.addExact(
103+
Math.multiplyExact(seconds, 1000L), nanoOfSecond / NANOSECONDS_IN_MILLISECOND);
101104
case "us":
102-
return TimeUnit.MILLISECONDS.toMicros(epochMilliSecond)
103-
+ nanoAdjustment / NANOSECONDS_IN_MICROSECOND;
105+
return Math.addExact(
106+
Math.multiplyExact(seconds, 1_000_000L), nanoOfSecond / NANOSECONDS_IN_MICROSECOND);
104107
case "ns":
105-
return TimeUnit.MILLISECONDS.toNanos(epochMilliSecond) + nanoAdjustment;
108+
return Math.addExact(Math.multiplyExact(seconds, 1_000_000_000L), nanoOfSecond);
106109
default:
107110
throw new IllegalArgumentException(
108111
CalcMessages.EXCEPTION_UNKNOWN_PRECISION_0119BEB0

‎iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/DateBinFunctionTest.java‎

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919

2020
package org.apache.iotdb.db.queryengine.transformation.dag.column.unary.scalar;
2121

22+
import org.apache.iotdb.calc.transformation.dag.column.leaf.NullColumnTransformer;
23+
import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.DateBinFunctionColumnTransformer;
24+
import org.apache.iotdb.commons.conf.CommonDescriptor;
25+
2226
import org.junit.Before;
2327
import org.junit.Test;
2428

@@ -28,6 +32,9 @@
2832
import java.util.TimeZone;
2933

3034
import static org.apache.iotdb.calc.transformation.dag.column.unary.scalar.DateBinFunctionColumnTransformer.dateBin;
35+
import static org.apache.iotdb.calc.transformation.dag.column.unary.scalar.DateBinFunctionColumnTransformer.nextDateBin;
36+
import static org.apache.tsfile.read.common.type.TimestampType.TIMESTAMP;
37+
import static org.junit.Assert.assertArrayEquals;
3138
import static org.junit.Assert.assertEquals;
3239

3340
public class DateBinFunctionTest {
@@ -126,4 +133,53 @@ public void testSourceBeforeUnixEpoch() throws ParseException {
126133
zoneId);
127134
assertEquals(getTimestamp("1969-12-31 00:00:00.000"), result);
128135
}
136+
137+
@Test
138+
public void testMonthBinKeepsFractionalOrigin() throws ParseException {
139+
long origin = 500L;
140+
long oneMonthLater = getTimestamp("1970-02-01 00:00:00.500");
141+
long justBeforeNextMonth = oneMonthLater - 1;
142+
143+
assertEquals(origin, dateBin(origin, origin, 1, 0, zoneId));
144+
assertEquals(origin, dateBin(justBeforeNextMonth, origin, 1, 0, zoneId));
145+
assertEquals(oneMonthLater, dateBin(oneMonthLater, origin, 1, 0, zoneId));
146+
147+
long marchOrigin = getTimestamp("2024-03-15 00:00:00.500");
148+
long aprilOrigin = getTimestamp("2024-04-15 00:00:00.500");
149+
assertEquals(marchOrigin, dateBin(marchOrigin, marchOrigin, 1, 0, zoneId));
150+
assertEquals(aprilOrigin, dateBin(aprilOrigin, marchOrigin, 1, 0, zoneId));
151+
152+
long exactSecond = getTimestamp("2024-03-15 00:00:00.000");
153+
long nextExactMonth = getTimestamp("2024-04-15 00:00:00.000");
154+
assertEquals(exactSecond, dateBin(exactSecond, exactSecond, 1, 0, zoneId));
155+
assertEquals(nextExactMonth, dateBin(nextExactMonth, exactSecond, 1, 0, zoneId));
156+
}
157+
158+
@Test
159+
public void testMonthBinStartEndAndGapFillKeepFractionalMillis() throws ParseException {
160+
long origin = 500L;
161+
DateBinFunctionColumnTransformer transformer =
162+
new DateBinFunctionColumnTransformer(
163+
TIMESTAMP, 1, 0, new NullColumnTransformer(), origin, zoneId);
164+
165+
long oneMonthLater = getTimestamp("1970-02-01 00:00:00.500");
166+
assertArrayEquals(new long[] {origin, oneMonthLater}, transformer.dateBinStartEnd(origin));
167+
assertEquals(oneMonthLater, nextDateBin(1, zoneId, origin));
168+
}
169+
170+
@Test
171+
public void testMonthBinKeepsSubMillisecondOriginUnderUsAndNs() {
172+
String originalPrecision = CommonDescriptor.getInstance().getConfig().getTimestampPrecision();
173+
try {
174+
// 1.5 ms after the epoch, expressed in microseconds and nanoseconds.
175+
CommonDescriptor.getInstance().getConfig().setTimestampPrecision("us");
176+
assertEquals(1500L, dateBin(1500L, 1500L, 1, 0, zoneId));
177+
assertEquals(1500L, nextDateBin(1, zoneId, 1500L) - 2678400000000L);
178+
179+
CommonDescriptor.getInstance().getConfig().setTimestampPrecision("ns");
180+
assertEquals(1_500_000L, dateBin(1_500_000L, 1_500_000L, 1, 0, zoneId));
181+
} finally {
182+
CommonDescriptor.getInstance().getConfig().setTimestampPrecision(originalPrecision);
183+
}
184+
}
129185
}

0 commit comments

Comments
 (0)