diff --git a/iotdb-thingsboard-table/src/main/java/org/apache/iotdb/extras/thingsboard/table/IoTDBTableTimeseriesDao.java b/iotdb-thingsboard-table/src/main/java/org/apache/iotdb/extras/thingsboard/table/IoTDBTableTimeseriesDao.java index 96387db..f7c9a27 100644 --- a/iotdb-thingsboard-table/src/main/java/org/apache/iotdb/extras/thingsboard/table/IoTDBTableTimeseriesDao.java +++ b/iotdb-thingsboard-table/src/main/java/org/apache/iotdb/extras/thingsboard/table/IoTDBTableTimeseriesDao.java @@ -389,14 +389,16 @@ private ReadTsKvQueryResult readMillisecondsAggregatedQuery( *

IoTDB 2.0.8's native {@code date_bin} calendar primitive cannot reproduce ThingsBoard's * boundaries: it anchors each calendar bucket on the origin's day-of-month (so {@code * date_bin(1mo, time, startTs)} from a mid-month {@code startTs} steps day-15 → day-15, not to - * the 1st of each month) and it exposes no timezone argument (it computes in the - * server's UTC zone only). ThingsBoard instead advances {@code startTs} to the start of the next - * calendar unit in {@code tzId} via {@link TimeUtils#calculateIntervalEnd}, so the first bucket - * is the partial {@code [startTs, nextCalendarBoundary)} and later buckets are full calendar - * units. This path therefore reproduces ThingsBoard exactly the way ThingsBoard itself does: it - * walks the calendar boundaries in Java and issues one bounded aggregate query per bucket - * (ThingsBoard issues one future per bucket), reusing the very same projection, row mapper and - * typed-COUNT logic as the {@code MILLISECONDS} path. + * the 1st of each month) and it exposes no timezone argument — its calendar arithmetic + * runs in the session's zone, which is fixed when the session pool is built and cannot + * be rebound per query, while each {@link ReadTsKvQuery} carries its own {@code tzId}. + * ThingsBoard instead advances {@code startTs} to the start of the next calendar unit in {@code + * tzId} via {@link TimeUtils#calculateIntervalEnd}, so the first bucket is the partial {@code + * [startTs, nextCalendarBoundary)} and later buckets are full calendar units. This path therefore + * reproduces ThingsBoard exactly the way ThingsBoard itself does: it walks the calendar + * boundaries in Java and issues one bounded aggregate query per bucket (ThingsBoard issues one + * future per bucket), reusing the very same projection, row mapper and typed-COUNT logic as the + * {@code MILLISECONDS} path. * *

Walking {@code [startTs, endPeriod)} where {@code endPeriod = max(startTs + 1, endTs)}: each * iteration takes {@code bucketStart = startPeriod}, {@code bucketEnd = min(calculateIntervalEnd(