Skip to content

Commit c1eb39e

Browse files
JinqingKuangclaude
andcommitted
feat(stream): add natural time units support for PERIOD trigger
Implement week/month/year units for stream PERIOD trigger with natural boundary alignment and offset support. Key changes: - Parser: Add validation for natural time units (w/n/y) and offset parameter - Time utilities: Add getDuration() support for week/month/year units - TriggerTask: Implement window calculation with natural boundary alignment - Week: align to Monday 00:00:00 - Month: align to 1st of month 00:00:00 - Year: align to Jan 1st 00:00:00 - Add offset support: PERIOD(1w, 1d) shifts window by 1 day - Unit tests: Parser validation, time utilities, TriggerTask window calculation - System tests: End-to-end tests for week/month/year units with offset - Documentation: Update user manual with natural time unit examples Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 156da4e commit c1eb39e

File tree

18 files changed

+2080
-51
lines changed

18 files changed

+2080
-51
lines changed

docs/en/14-reference/03-taos-sql/41-stream.md

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,18 +75,20 @@ PERIOD(period_time[, offset_time])
7575

7676
A scheduled trigger is driven by a fixed interval based on the system time, essentially functioning as a scheduled task. It does not belong to the category of window triggers. Parameter definitions are as follows:
7777

78-
- period_time: The scheduling interval. Supported time units include milliseconds (a), seconds (s), minutes (m), hours (h), and days (d). The supported range is [10a, 3650d].
79-
- offset_time: (Optional) The scheduling offset. Supported units include milliseconds (a), seconds (s), minutes (m), and hours (h). The offset value must be less than 1 day.
78+
- period_time: The scheduling interval. Supported time units include milliseconds (a), seconds (s), minutes (m), hours (h), days (d), weeks (w), months (n), and years (y). The supported range is [10a, 3650d].
79+
- offset_time: (Optional) The scheduling offset. Supported units include milliseconds (a), seconds (s), minutes (m), hours (h), and days (d). For week/month/year units, the offset must be strictly less than the trigger period; for month units, validation is based on 28 days/month (e.g., `PERIOD(1n, 28d)` is invalid).
8080

8181
Usage Notes:
8282

8383
- When the scheduling interval is less than one day, the base time is calculated as midnight (00:00) plus the scheduling offset. The next trigger time is determined based on this base time and the specified interval. The base time resets to midnight each day. The time between the last trigger of one day and the base time of the next day may be shorter than the scheduling interval. For example:
8484
- If the scheduling interval is 5 hours 30 minutes, the trigger times for the day will be [00:00, 05:30, 11:00, 16:30, 22:00]. The trigger times for subsequent days will be the same.
8585
- With the same interval but an offset of 1 minute, the trigger times will be [00:01, 05:31, 11:01, 16:31, 22:01] each day.
8686
- Under the same conditions, if the stream is created when the system time is 12:00, the trigger times for the current day will be [16:31, 22:01]. From the next day onwards, the trigger times will be [00:01, 05:31, 11:01, 16:31, 22:01].
87-
- When the scheduling interval is greater than or equal to 1 day, the base time is calculated as midnight (00:00) of the current day plus the scheduling offset, and it will not reset on subsequent days. For example:
88-
- If the scheduling interval is 1 day 1 hour and the stream is created when the system time is 05-01 12:00, the trigger times will be [05-02 01:00, 05-03 02:00, 05-04 03:00, 05-05 04:00, …].
89-
- Under the same conditions, if the time offset is 1 minute, the trigger times will be [05-02 01:01, 05-03 02:02, 05-04 03:03, 05-05 04:04, …].
87+
- When the scheduling interval is greater than or equal to 1 day, the base time is calculated from the server timezone's Unix epoch (1970-01-01 00:00:00) plus the scheduling offset, aligned by integer multiples of the trigger interval to ensure global consistency across all tasks. For example:
88+
- With a scheduling interval of 2 days, all tasks using this interval will trigger at times that are integer multiples of 2 days from the epoch (e.g., 1970-01-03 00:00:00, 1970-01-05 00:00:00, ...), ensuring global alignment.
89+
- With a scheduling interval of 1 week (`PERIOD(1w)`), triggers align to every Monday at 00:00:00; `PERIOD(1w, 1d)` triggers every Tuesday at 00:00:00.
90+
- With a scheduling interval of 1 month (`PERIOD(1n)`), triggers align to the 1st of each month at 00:00:00; `PERIOD(1n, 14d)` triggers on the 15th of each month at 00:00:00.
91+
- With a scheduling interval of 1 year (`PERIOD(1y)`), triggers align to January 1st at 00:00:00 each year; `PERIOD(1y, 31d)` triggers on February 1st at 00:00:00 each year.
9092

9193
Applicable scenarios: Situations requiring scheduled computation driven continuously by system time, such as generating daily statistics every hour, or sending scheduled statistical reports once a day.
9294

@@ -1018,3 +1020,72 @@ CREATE stream stream_consumer_energy
10181020
FROM meters
10191021
WHERE ts >= cast(_tprev_localtime/1000000 AS timestamp) AND ts <= cast(_tlocaltime/1000000 AS timestamp);
10201022
```
1023+
1024+
- Every Monday at 00:00:00, compute the weekly device operation summary for the previous week and write the results to the weekly_summary table.
1025+
1026+
```SQL
1027+
CREATE STREAM weekly_device_summary
1028+
PERIOD(1w)
1029+
FROM meters PARTITION BY location
1030+
INTO weekly_summary
1031+
AS
1032+
SELECT _wstart AS week_start,
1033+
location,
1034+
AVG(current) AS avg_current,
1035+
MAX(voltage) AS max_voltage,
1036+
COUNT(*) AS record_count
1037+
FROM meters
1038+
INTERVAL(1w)
1039+
PARTITION BY location;
1040+
```
1041+
1042+
- On the 1st of each month at 00:00:00, compute the energy consumption bill for the previous month and write the results to the monthly_bill table.
1043+
1044+
```SQL
1045+
CREATE STREAM monthly_energy_bill
1046+
PERIOD(1n)
1047+
FROM meters PARTITION BY location, groupId
1048+
INTO monthly_bill
1049+
AS
1050+
SELECT _wstart AS month_start,
1051+
location,
1052+
groupId,
1053+
SUM(current * voltage) AS total_energy
1054+
FROM meters
1055+
INTERVAL(1n)
1056+
PARTITION BY location, groupId;
1057+
```
1058+
1059+
- On the 15th of each month at 00:00:00, compute the mid-month settlement report (using the offset parameter).
1060+
1061+
```SQL
1062+
CREATE STREAM mid_month_settlement
1063+
PERIOD(1n, 14d)
1064+
FROM meters PARTITION BY location
1065+
INTO mid_month_settlement_table
1066+
AS
1067+
SELECT _wstart AS period_start,
1068+
location,
1069+
SUM(current * voltage) AS total_energy
1070+
FROM meters
1071+
INTERVAL(1n)
1072+
PARTITION BY location;
1073+
```
1074+
1075+
- On January 1st at 00:00:00 each year, archive the full data from the previous year.
1076+
1077+
```SQL
1078+
CREATE STREAM yearly_archive
1079+
PERIOD(1y)
1080+
FROM meters PARTITION BY location, groupId
1081+
INTO yearly_archive_table
1082+
AS
1083+
SELECT _wstart AS year_start,
1084+
location,
1085+
groupId,
1086+
AVG(current) AS avg_current,
1087+
SUM(current * voltage) AS total_energy
1088+
FROM meters
1089+
INTERVAL(1y)
1090+
PARTITION BY location, groupId;
1091+
```

docs/zh/14-reference/03-taos-sql/41-stream.md

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,18 +74,20 @@ PERIOD(period_time[, offset_time])
7474

7575
定时触发通过系统时间的固定间隔来驱动,本质上就是我们常说的定时任务。定时触发不属于窗口触发。各参数含义如下:
7676

77-
- period_time:定时间隔,支持的时间单位包括:毫秒 (a)、秒 (s)、分 (m)、小时 (h)、天 (d),支持的时间范围为 `[10a, 3650d]`
78-
- offset_time:可选,定时偏移,支持的时间单位包括:毫秒 (a)、秒 (s)、分 (m)、小时 (h),偏移大小应该小于 1 天
77+
- period_time:定时间隔,支持的时间单位包括:毫秒 (a)、秒 (s)、分 (m)、小时 (h)、天 (d)、周 (w)、月 (n)、年 (y),支持的时间范围为 `[10a, 3650d]`
78+
- offset_time:可选,定时偏移,支持的时间单位包括:毫秒 (a)、秒 (s)、分 (m)、小时 (h)、天 (d)。对于周/月/年单位,offset 必须严格小于触发周期;对于月单位,以 28 天/月为基准静态校验(如 `PERIOD(1n, 28d)` 非法)
7979

8080
使用说明:
8181

8282
- 定时间隔小于 1 天时,基准时间点为每日零点加定时偏移,根据定时间隔来确定下次触发的时间点。基准时间点在每日零点重置。每日最后一次触发的时间点与下一日的基准时间点之间的间隔可能小于定时间隔。例如:
8383
- 定时间隔为 5 小时 30 分钟,那么当天的触发时刻为 `[00:00, 05:30, 11:00, 16:30, 22:00]`,后续每一天的触发时刻都是相同的。
8484
- 同样的定时间隔,如果指定时间偏移为 1 分钟,那么当天的触发时刻为 `[00:01, 05:31, 11:01, 16:31, 22:01]`,后续每一天的触发时刻都是相同的。
8585
- 同样条件下,如果建流时当前系统时间为 `12:00`,那么当天的触发时刻为 `[16:31, 22:01]`,后续每一天内的触发时刻为 `[00:01, 05:31, 11:01, 16:31, 22:01]`
86-
- 定时间隔大于等于 1 天时,基准时间点为当日的零点加定时偏移,后续不会重置。例如:
87-
- 定时间隔为 1 天 1 小时,建流时当前系统时间为 `05-01 12:00`,那么在当天及随后几天的触发时刻为 `[05-02 01:00, 05-03 02:00, 05-04 03:00, 05-05 04:00, ……]`
88-
- 同样条件下,如果指定时间偏移为 1 分钟,那么当天及随后几天的触发时刻为 `[05-02 01:01, 05-03 02:02, 05-04 03:03, 05-05 04:04, ……]`
86+
- 定时间隔大于等于 1 天时,基准时间点为服务端时区的 Unix epoch(1970-01-01 00:00:00)加定时偏移,按触发间隔整除对齐,保证所有任务触发时刻全局一致。例如:
87+
- 定时间隔为 2 天,所有使用该间隔的任务都会在距离 epoch 整数倍 2 天的时刻触发(如 1970-01-03 00:00:00, 1970-01-05 00:00:00, ...),确保全局对齐。
88+
- 定时间隔为 1 周(`PERIOD(1w)`),触发时刻对齐每周一 00:00:00;`PERIOD(1w, 1d)` 则在每周二 00:00:00 触发。
89+
- 定时间隔为 1 月(`PERIOD(1n)`),触发时刻对齐每月 1 日 00:00:00;`PERIOD(1n, 14d)` 则在每月 15 日 00:00:00 触发。
90+
- 定时间隔为 1 年(`PERIOD(1y)`),触发时刻对齐每年 1 月 1 日 00:00:00;`PERIOD(1y, 31d)` 则在每年 2 月 1 日 00:00:00 触发。
8991

9092
适用场景:需要按照系统时间连续定时驱动计算的场景,例如每小时计算生成一次当天的统计数据,每天定时发送统计报告等。
9193

@@ -1016,3 +1018,72 @@ CREATE stream stream_consumer_energy
10161018
FROM meters
10171019
WHERE ts >= cast(_tprev_localtime/1000000 AS timestamp) AND ts <= cast(_tlocaltime/1000000 AS timestamp);
10181020
```
1021+
1022+
- 每周一 00:00:00 计算上周的设备运行汇总,计算结果写入 weekly_summary 表。
1023+
1024+
```SQL
1025+
CREATE STREAM weekly_device_summary
1026+
PERIOD(1w)
1027+
FROM meters PARTITION BY location
1028+
INTO weekly_summary
1029+
AS
1030+
SELECT _wstart AS week_start,
1031+
location,
1032+
AVG(current) AS avg_current,
1033+
MAX(voltage) AS max_voltage,
1034+
COUNT(*) AS record_count
1035+
FROM meters
1036+
INTERVAL(1w)
1037+
PARTITION BY location;
1038+
```
1039+
1040+
- 每月 1 日 00:00:00 计算上月的能耗账单,计算结果写入 monthly_bill 表。
1041+
1042+
```SQL
1043+
CREATE STREAM monthly_energy_bill
1044+
PERIOD(1n)
1045+
FROM meters PARTITION BY location, groupId
1046+
INTO monthly_bill
1047+
AS
1048+
SELECT _wstart AS month_start,
1049+
location,
1050+
groupId,
1051+
SUM(current * voltage) AS total_energy
1052+
FROM meters
1053+
INTERVAL(1n)
1054+
PARTITION BY location, groupId;
1055+
```
1056+
1057+
- 每月 15 日 00:00:00 计算半月结算报表(使用 offset 参数)。
1058+
1059+
```SQL
1060+
CREATE STREAM mid_month_settlement
1061+
PERIOD(1n, 14d)
1062+
FROM meters PARTITION BY location
1063+
INTO mid_month_settlement_table
1064+
AS
1065+
SELECT _wstart AS period_start,
1066+
location,
1067+
SUM(current * voltage) AS total_energy
1068+
FROM meters
1069+
INTERVAL(1n)
1070+
PARTITION BY location;
1071+
```
1072+
1073+
- 每年 1 月 1 日 00:00:00 归档上一年的全量数据。
1074+
1075+
```SQL
1076+
CREATE STREAM yearly_archive
1077+
PERIOD(1y)
1078+
FROM meters PARTITION BY location, groupId
1079+
INTO yearly_archive_table
1080+
AS
1081+
SELECT _wstart AS year_start,
1082+
location,
1083+
groupId,
1084+
AVG(current) AS avg_current,
1085+
SUM(current * voltage) AS total_energy
1086+
FROM meters
1087+
INTERVAL(1y)
1088+
PARTITION BY location, groupId;
1089+
```

include/common/ttime.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ int32_t convertCalendarTimeFromUnitToPrecision(int64_t time, char fromUnit, int
8383
int32_t convertTimeFromPrecisionToUnit(int64_t time, int32_t fromPrecision, char toUnit, int64_t* pRes);
8484
int32_t convertStringToTimestamp(int16_t type, char* inputData, int64_t timePrec, int64_t* timeVal, timezone_t tz, void* charsetCxt);
8585
int32_t getDuration(int64_t val, char unit, int64_t* result, int32_t timePrecision);
86+
int64_t alignToNaturalBoundary(int64_t timestamp, char unit, int64_t value, int64_t offset, int32_t precision, timezone_t tz);
8687

8788
int32_t taosFormatUtcTime(char* buf, int32_t bufLen, int64_t ts, int32_t precision);
8889
char* formatTimestampLocal(char* buf, int64_t val, int precision);

include/util/taoserror.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,9 @@ int32_t taosGetErrSize();
10781078
#define TSDB_CODE_PAR_NOT_ALLOWED_FILL_MODE TAOS_DEF_ERROR_CODE(0, 0x26AC)
10791079
#define TSDB_CODE_PAR_NOT_ALLOWED_FILL_VALUES TAOS_DEF_ERROR_CODE(0, 0x26AD)
10801080
#define TSDB_CODE_PAR_INVALID_SURROUND_TIME_VALUES TAOS_DEF_ERROR_CODE(0, 0x26AE)
1081+
#define TSDB_CODE_PAR_INVALID_OFFSET_UNIT TAOS_DEF_ERROR_CODE(0, 0x26AF)
1082+
#define TSDB_CODE_PAR_INVALID_OFFSET_VALUE TAOS_DEF_ERROR_CODE(0, 0x26B0)
1083+
#define TSDB_CODE_STREAM_METADATA_INCOMPATIBLE TAOS_DEF_ERROR_CODE(0, 0x26B1)
10811084
//
10821085
#define TSDB_CODE_PAR_PRIV_TYPE_TARGET_CONFLICT TAOS_DEF_ERROR_CODE(0, 0x26E0)
10831086
#define TSDB_CODE_PAR_COL_PERMISSION_DENIED TAOS_DEF_ERROR_CODE(0, 0x26E1)

include/util/tringbuf.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,10 @@ extern "C" {
4343
} \
4444
} while (0)
4545

46+
typedef TRINGBUF(void) TRingBufGeneral;
47+
4648
static FORCE_INLINE int32_t tringbufExtend(void *rbuf, int32_t expSize, int32_t eleSize) {
47-
TRINGBUF(void) *rb = rbuf;
49+
TRingBufGeneral *rb = (TRingBufGeneral *)rbuf;
4850

4951
int32_t capacity = TRINGBUF_CAPACITY(rb);
5052
if (expSize <= capacity) return 0;
@@ -72,7 +74,7 @@ static FORCE_INLINE int32_t tringbufExtend(void *rbuf, int32_t expSize, int32_t
7274
}
7375

7476
static FORCE_INLINE int32_t tringbufPushBatch(void *rbuf, const void *elePtr, int32_t numEle, int32_t eleSize) {
75-
TRINGBUF(void) *rb = rbuf;
77+
TRingBufGeneral *rb = (TRingBufGeneral *)rbuf;
7678

7779
int32_t ret = 0;
7880
if (rb->size + numEle > rb->capacity) {

source/common/src/msg/streamMsg.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,9 @@ int32_t tEncodeSStreamTriggerDeployMsg(SEncoder* pEncoder, const SStreamTriggerD
10691069
}
10701070
case WINDOW_TYPE_PERIOD: {
10711071
// period trigger
1072+
TAOS_CHECK_EXIT(tEncodeI8(pEncoder, pMsg->trigger.period.periodUnit));
1073+
TAOS_CHECK_EXIT(tEncodeI8(pEncoder, pMsg->trigger.period.offsetUnit));
1074+
TAOS_CHECK_EXIT(tEncodeI8(pEncoder, pMsg->trigger.period.precision));
10721075
TAOS_CHECK_EXIT(tEncodeI64(pEncoder, pMsg->trigger.period.period));
10731076
TAOS_CHECK_EXIT(tEncodeI64(pEncoder, pMsg->trigger.period.offset));
10741077
break;
@@ -1641,6 +1644,9 @@ int32_t tDecodeSStreamTriggerDeployMsg(SDecoder* pDecoder, SStreamTriggerDeployM
16411644

16421645
case WINDOW_TYPE_PERIOD:
16431646
// period trigger
1647+
TAOS_CHECK_EXIT(tDecodeI8(pDecoder, (int8_t*)&pMsg->trigger.period.periodUnit));
1648+
TAOS_CHECK_EXIT(tDecodeI8(pDecoder, (int8_t*)&pMsg->trigger.period.offsetUnit));
1649+
TAOS_CHECK_EXIT(tDecodeI8(pDecoder, &pMsg->trigger.period.precision));
16441650
TAOS_CHECK_EXIT(tDecodeI64(pDecoder, &pMsg->trigger.period.period));
16451651
TAOS_CHECK_EXIT(tDecodeI64(pDecoder, &pMsg->trigger.period.offset));
16461652
break;

source/common/src/ttime.c

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,116 @@ int32_t getDuration(int64_t val, char unit, int64_t* result, int32_t timePrecisi
696696
TAOS_RETURN(TSDB_CODE_SUCCESS);
697697
}
698698

699+
/**
700+
* @brief Align timestamp to natural calendar boundary (Monday/Month start/Year start)
701+
*
702+
* @param timestamp Input timestamp in specified precision
703+
* @param unit Time unit: 'w' (week), 'n' (month), 'y' (year)
704+
* @param value Period multiplier (e.g., 2 for 2 weeks)
705+
* @param offset Offset from boundary in same precision as timestamp
706+
* @param precision Time precision (TSDB_TIME_PRECISION_MILLI/MICRO/NANO)
707+
* @param tz Timezone for calendar calculations
708+
* @return Aligned timestamp at natural boundary + offset
709+
*
710+
* Examples:
711+
* - alignToNaturalBoundary(ts, 'w', 1, 0, ...) -> Monday 00:00:00
712+
* - alignToNaturalBoundary(ts, 'n', 1, 0, ...) -> 1st of month 00:00:00
713+
* - alignToNaturalBoundary(ts, 'y', 1, 0, ...) -> Jan 1st 00:00:00
714+
*/
715+
int64_t alignToNaturalBoundary(int64_t timestamp, char unit, int64_t value, int64_t offset, int32_t precision,
716+
timezone_t tz) {
717+
// Convert timestamp to seconds for calendar calculations
718+
int64_t precisionFactor = TSDB_TICK_PER_SECOND(precision);
719+
time_t t = timestamp / precisionFactor;
720+
struct tm tm;
721+
if (taosLocalTime(&t, &tm, NULL, 0, tz) == NULL){
722+
uError("%s failed to get local time, code:%d", __FUNCTION__, ERRNO);
723+
return timestamp;
724+
}
725+
726+
int64_t aligned = 0;
727+
728+
switch (unit) {
729+
case 'w': {
730+
// Align to Monday 00:00:00
731+
int daysSinceMonday = (tm.tm_wday + 6) % 7; // Convert Sunday=0 to Monday=0
732+
tm.tm_mday -= daysSinceMonday;
733+
tm.tm_hour = 0;
734+
tm.tm_min = 0;
735+
tm.tm_sec = 0;
736+
737+
// For multi-week periods, align based on epoch
738+
time_t mondayTime = taosMktime(&tm, tz);
739+
if (value > 1) {
740+
// Calculate epoch Monday (1970-01-05 00:00:00) in the same timezone
741+
struct tm epochTm = {0};
742+
epochTm.tm_year = 70; // 1970
743+
epochTm.tm_mon = 0; // January
744+
epochTm.tm_mday = 5; // 5th (first Monday)
745+
epochTm.tm_hour = 0;
746+
epochTm.tm_min = 0;
747+
epochTm.tm_sec = 0;
748+
epochTm.tm_isdst = -1;
749+
time_t epochMonday = taosMktime(&epochTm, tz);
750+
751+
int64_t weeksSinceEpoch = (mondayTime - epochMonday) / (7 * 86400);
752+
int64_t alignedWeeks = (weeksSinceEpoch / value) * value;
753+
mondayTime = epochMonday + alignedWeeks * 7 * 86400;
754+
}
755+
756+
aligned = (int64_t)mondayTime * precisionFactor;
757+
break;
758+
}
759+
760+
case 'n': {
761+
// Align to 1st of month 00:00:00
762+
tm.tm_mday = 1;
763+
tm.tm_hour = 0;
764+
tm.tm_min = 0;
765+
tm.tm_sec = 0;
766+
767+
// For multi-month periods, align based on epoch
768+
if (value > 1) {
769+
int monthsSinceEpoch = (tm.tm_year - 70) * 12 + tm.tm_mon;
770+
int alignedMonths = (monthsSinceEpoch / value) * value;
771+
tm.tm_year = 70 + alignedMonths / 12;
772+
tm.tm_mon = alignedMonths % 12;
773+
}
774+
775+
aligned = (int64_t)taosMktime(&tm, tz) * precisionFactor;
776+
break;
777+
}
778+
779+
case 'y': {
780+
// Align to Jan 1st 00:00:00
781+
tm.tm_mon = 0;
782+
tm.tm_mday = 1;
783+
tm.tm_hour = 0;
784+
tm.tm_min = 0;
785+
tm.tm_sec = 0;
786+
787+
// For multi-year periods, align based on epoch
788+
if (value > 1) {
789+
int yearsSinceEpoch = tm.tm_year - 70;
790+
int alignedYears = (yearsSinceEpoch / value) * value;
791+
tm.tm_year = 70 + alignedYears;
792+
}
793+
794+
aligned = (int64_t)taosMktime(&tm, tz) * precisionFactor;
795+
break;
796+
}
797+
798+
default:
799+
// For other units, return timestamp as-is
800+
return timestamp;
801+
}
802+
803+
// Apply offset to the aligned boundary
804+
// Offset is already in the same precision as the timestamp
805+
// Example: PERIOD(1w, 1d) -> Monday 00:00:00 + 1 day = Tuesday 00:00:00
806+
return aligned + offset;
807+
}
808+
699809
/*
700810
* n - months
701811
* y - Years
@@ -2302,4 +2412,4 @@ int32_t taosParseShortWeekday(const char* str) {
23022412
}
23032413
}
23042414
return -1;
2305-
}
2415+
}

0 commit comments

Comments
 (0)