• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

taosdata / TDengine / #3911

24 Apr 2025 11:36PM UTC coverage: 53.735% (-1.6%) from 55.295%
#3911

push

travis-ci

happyguoxy
Sync branches at 2025-04-25 07:35

170049 of 316459 relevant lines covered (53.73%)

1192430.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.72
/source/common/src/ttime.c
1
/*
2
 * Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
3
 *
4
 * This program is free software: you can use, redistribute, and/or modify
5
 * it under the terms of the GNU Affero General Public License, version 3
6
 * or later ("AGPL"), as published by the Free Software Foundation.
7
 *
8
 * This program is distributed in the hope that it will be useful, but WITHOUT
9
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
 * FITNESS FOR A PARTICULAR PURPOSE.
11
 *
12
 * You should have received a copy of the GNU Affero General Public License
13
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14
 */
15

16
#ifdef DARWIN
17
#define _XOPEN_SOURCE
18
#else
19
#define _XOPEN_SOURCE 500
20
#endif
21

22
#define _BSD_SOURCE
23
#define _DEFAULT_SOURCE
24
#include "ttime.h"
25

26
#include "tlog.h"
27

28
static int32_t parseFraction(char* str, char** end, int32_t timePrec, int64_t* pFraction);
29
static int32_t parseTimeWithTz(const char* timestr, int64_t* time, int32_t timePrec, char delim);
30
static int32_t parseLocaltimeDst(char* timestr, int32_t len, int64_t* utime, int32_t timePrec, char delim, timezone_t tz);
31
static char*   forwardToTimeStringEnd(char* str);
32
static bool    checkTzPresent(const char* str, int32_t len);
33
static int32_t parseTimezone(char* str, int64_t* tzOffset);
34

35
int32_t taosParseTime(const char* timestr, int64_t* utime, int32_t len, int32_t timePrec, timezone_t tz) {
3,343✔
36
  /* parse datatime string in with tz */
37
  if (strnchr(timestr, 'T', len, false) != NULL) {
3,343✔
38
    if (checkTzPresent(timestr, len)) {
18✔
39
      return parseTimeWithTz(timestr, utime, timePrec, 'T');
14✔
40
    } else {
41
      return parseLocaltimeDst((char*)timestr, len, utime, timePrec, 'T', tz);
4✔
42
    }
43
  } else {
44
    if (checkTzPresent(timestr, len)) {
3,325✔
45
      return parseTimeWithTz(timestr, utime, timePrec, 0);
63✔
46
    } else {
47
      return parseLocaltimeDst((char*)timestr, len, utime, timePrec, 0, tz);
3,262✔
48
    }
49
  }
50
}
51

52
bool checkTzPresent(const char* str, int32_t len) {
3,343✔
53
  char*   seg = forwardToTimeStringEnd((char*)str);
3,343✔
54
  int32_t seg_len = len - (int32_t)(seg - str);
3,343✔
55

56
  char* c = &seg[seg_len - 1];
3,343✔
57
  for (int32_t i = 0; i < seg_len; ++i) {
12,958✔
58
    if (*c == 'Z' || *c == 'z' || *c == '+' || *c == '-') {
9,692✔
59
      return true;
77✔
60
    }
61
    c--;
9,615✔
62
  }
63

64
  return false;
3,266✔
65
}
66

67
char* forwardToTimeStringEnd(char* str) {
3,420✔
68
  int32_t i = 0;
3,420✔
69
  int32_t numOfSep = 0;
3,420✔
70

71
  while (str[i] != 0 && numOfSep < 2) {
61,423✔
72
    if (str[i++] == ':') {
58,003✔
73
      numOfSep++;
6,806✔
74
    }
75
  }
76

77
  while (str[i] >= '0' && str[i] <= '9') {
10,226✔
78
    i++;
6,806✔
79
  }
80

81
  return &str[i];
3,420✔
82
}
83

84
int32_t parseFraction(char* str, char** end, int32_t timePrec, int64_t* pFraction) {
3,094✔
85
  int32_t i = 0;
3,094✔
86
  int64_t fraction = 0;
3,094✔
87

88
  const int32_t MILLI_SEC_FRACTION_LEN = 3;
3,094✔
89
  const int32_t MICRO_SEC_FRACTION_LEN = 6;
3,094✔
90
  const int32_t NANO_SEC_FRACTION_LEN = 9;
3,094✔
91

92
  int32_t factor[9] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};
3,094✔
93
  int32_t times = 1;
3,094✔
94

95
  while (str[i] >= '0' && str[i] <= '9') {
9,421✔
96
    i++;
6,327✔
97
  }
98

99
  int32_t totalLen = i;
3,094✔
100
  if (totalLen <= 0) {
3,094✔
101
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
102
  }
103

104
  /* parse the fraction */
105
  if (timePrec == TSDB_TIME_PRECISION_MILLI) {
3,094✔
106
    /* only use the initial 3 bits */
107
    if (i >= MILLI_SEC_FRACTION_LEN) {
3,090✔
108
      i = MILLI_SEC_FRACTION_LEN;
126✔
109
    }
110

111
    times = MILLI_SEC_FRACTION_LEN - i;
3,090✔
112
  } else if (timePrec == TSDB_TIME_PRECISION_MICRO) {
4✔
113
    if (i >= MICRO_SEC_FRACTION_LEN) {
1✔
114
      i = MICRO_SEC_FRACTION_LEN;
1✔
115
    }
116
    times = MICRO_SEC_FRACTION_LEN - i;
1✔
117
  } else if (timePrec == TSDB_TIME_PRECISION_NANO) {
3✔
118
    if (i >= NANO_SEC_FRACTION_LEN) {
3✔
119
      i = NANO_SEC_FRACTION_LEN;
1✔
120
    }
121
    times = NANO_SEC_FRACTION_LEN - i;
3✔
122
  } else {
123
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
124
  }
125

126
  fraction = strnatoi(str, i) * factor[times];
3,094✔
127
  *end = str + totalLen;
3,094✔
128
  *pFraction = fraction;
3,094✔
129

130
  TAOS_RETURN(TSDB_CODE_SUCCESS);
3,094✔
131
}
132

133
#define PARSE(str,len,result) \
134
  if (len != 2) {\
135
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);\
136
  }\
137
  result = strnatoi(str, len);
138

139
int32_t parseTimezone(char* str, int64_t* tzOffset) {
104✔
140
  int64_t hour = 0;
104✔
141
  int64_t minute = 0;
104✔
142

143
  int32_t i = 0;
104✔
144
  if (str[i] != '+' && str[i] != '-') {
104✔
145
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
146
  }
147

148
  i++;
104✔
149

150
  int32_t j = i;
104✔
151
  while (str[j]) {
481✔
152
    if ((str[j] >= '0' && str[j] <= '9') || str[j] == ':') {
386✔
153
      ++j;
377✔
154
      continue;
377✔
155
    }
156

157
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
9✔
158
  }
159

160
  char* sep = strchr(&str[i], ':');
95✔
161
  if (sep != NULL) {
95✔
162
    int32_t hourSize = (int32_t)(sep - &str[i]);
32✔
163
    PARSE(&str[i], hourSize, hour);
32✔
164

165
    i += hourSize + 1;
23✔
166
    size_t minSize = strlen(&str[i]);
23✔
167
    PARSE(&str[i], minSize, minute);
23✔
168
  } else {
169
    size_t hourSize = strlen(&str[i]);
63✔
170
    if (hourSize > 2){
63✔
171
      hourSize = 2;
51✔
172
    }
173
    PARSE(&str[i], hourSize, hour)
63✔
174
    i += hourSize;
61✔
175
    size_t minSize = strlen(&str[i]);
61✔
176
    if (minSize > 0){
61✔
177
      PARSE(&str[i], minSize, minute);
51✔
178
    }
179
  }
180

181
  if (hour > 13 || hour < 0) {
80✔
182
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
1✔
183
  }
184
  if (minute > 59 || minute < 0) {
79✔
185
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
186
  }
187

188
  if (str[0] == '+') {
79✔
189
    *tzOffset = -(hour * 3600 + minute * 60);
60✔
190
  } else {
191
    *tzOffset = hour * 3600 + minute * 60;
19✔
192
  }
193

194
  TAOS_RETURN(TSDB_CODE_SUCCESS);
79✔
195
}
196

197
int32_t offsetOfTimezone(char* tzStr, int64_t* offset) {
27✔
198
  if (tzStr && (tzStr[0] == 'z' || tzStr[0] == 'Z')) {
27✔
199
    *offset = 0;
×
200
    return TSDB_CODE_SUCCESS;
×
201
  }
202
  return parseTimezone(tzStr, offset);
27✔
203
}
204

205
/*
206
 * rfc3339 format:
207
 * 2013-04-12T15:52:01+08:00
208
 * 2013-04-12T15:52:01.123+08:00
209
 *
210
 * 2013-04-12T15:52:01Z
211
 * 2013-04-12T15:52:01.123Z
212
 *
213
 * iso-8601 format:
214
 * 2013-04-12T15:52:01+0800
215
 * 2013-04-12T15:52:01.123+0800
216
 */
217
int32_t parseTimeWithTz(const char* timestr, int64_t* time, int32_t timePrec, char delim) {
77✔
218
  int64_t factor = TSDB_TICK_PER_SECOND(timePrec);
77✔
219
  int64_t tzOffset = 0;
77✔
220

221
  struct tm tm = {0};
77✔
222

223
  char* str;
224
  if (delim == 'T') {
77✔
225
    str = taosStrpTime(timestr, "%Y-%m-%dT%H:%M:%S", &tm);
14✔
226
  } else if (delim == 0) {
63✔
227
    str = taosStrpTime(timestr, "%Y-%m-%d %H:%M:%S", &tm);
63✔
228
  } else {
229
    str = NULL;
×
230
  }
231

232
  if (str == NULL) {
77✔
233
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
234
  }
235

236
/* mktime will be affected by TZ, set by using taos_options */
237
#if defined(WINDOWS) || defined(TD_ASTRA) 
238
  int64_t seconds = user_mktime64(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, 0);
239
  // int64_t seconds = gmtime(&tm);
240
#else
241
  int64_t seconds = taosTimeGm(&tm);
77✔
242
  if (seconds == -1){
77✔
243
    TAOS_RETURN(TAOS_SYSTEM_ERROR(ERRNO));
×
244
  }
245
#endif
246

247
  int64_t fraction = 0;
77✔
248
  str = forwardToTimeStringEnd((char*)timestr);
77✔
249

250
  if ((str[0] == 'Z' || str[0] == 'z') && str[1] == '\0') {
77✔
251
    /* utc time, no millisecond, return directly*/
252
    *time = seconds * factor;
×
253
  } else if (str[0] == '.') {
77✔
254
    str += 1;
22✔
255
    TAOS_CHECK_RETURN(parseFraction(str, &str, timePrec, &fraction));
22✔
256

257
    *time = seconds * factor + fraction;
22✔
258

259
    char seg = str[0];
22✔
260
    if (seg != 'Z' && seg != 'z' && seg != '+' && seg != '-') {
22✔
261
      TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
262
    } else if ((seg == 'Z' || seg == 'z') && str[1] != '\0') {
22✔
263
      TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
264
    } else if (seg == '+' || seg == '-') {
22✔
265
      // parse the timezone
266
      TAOS_CHECK_RETURN(parseTimezone(str, &tzOffset));
22✔
267

268
      *time += tzOffset * factor;
22✔
269
    }
270

271
  } else if (str[0] == '+' || str[0] == '-') {
55✔
272
    *time = seconds * factor + fraction;
55✔
273

274
    // parse the timezone
275
    TAOS_CHECK_RETURN(parseTimezone(str, &tzOffset));
55✔
276

277
    *time += tzOffset * factor;
30✔
278
  } else {
279
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
280
  }
281

282
  TAOS_RETURN(TSDB_CODE_SUCCESS);
52✔
283
}
284

285
static FORCE_INLINE bool validateTm(struct tm* pTm) {
286
  if (pTm == NULL) {
3,254✔
287
    return false;
×
288
  }
289

290
  int32_t dayOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
3,254✔
291

292
  int32_t leapYearMonthDay = 29;
3,254✔
293
  int32_t year = pTm->tm_year + 1900;
3,254✔
294
  bool    isLeapYear = ((year % 100) == 0) ? ((year % 400) == 0) : ((year % 4) == 0);
3,254✔
295

296
  if (isLeapYear && (pTm->tm_mon == 1)) {
3,254✔
297
    if (pTm->tm_mday > leapYearMonthDay) {
×
298
      return false;
×
299
    }
300
  } else {
301
    if (pTm->tm_mday > dayOfMonth[pTm->tm_mon]) {
3,254✔
302
      return false;
×
303
    }
304
  }
305

306
  return true;
3,254✔
307
}
308

309
int32_t parseLocaltimeDst(char* timestr, int32_t len, int64_t* utime, int32_t timePrec, char delim, timezone_t tz) {
3,266✔
310
  *utime = 0;
3,266✔
311
  struct tm tm = {0};
3,266✔
312
  tm.tm_isdst = -1;
3,266✔
313

314
  char* str;
315
  if (delim == 'T') {
3,266✔
316
    str = taosStrpTime(timestr, "%Y-%m-%dT%H:%M:%S", &tm);
4✔
317
  } else if (delim == 0) {
3,262✔
318
    str = taosStrpTime(timestr, "%Y-%m-%d %H:%M:%S", &tm);
3,262✔
319
  } else {
320
    str = NULL;
×
321
  }
322

323
  if (str == NULL || (((str - timestr) < len) && (*str != '.')) || !validateTm(&tm)) {
6,515✔
324
    // if parse failed, try "%Y-%m-%d" format
325
    str = taosStrpTime(timestr, "%Y-%m-%d", &tm);
17✔
326
    if (str == NULL || (((str - timestr) < len) && (*str != '.')) || !validateTm(&tm)) {
22✔
327
      TAOS_RETURN(TSDB_CODE_INVALID_PARA);
12✔
328
    }
329
  }
330

331
  int64_t seconds = taosMktime(&tm, tz);
3,254✔
332

333
  int64_t fraction = 0;
3,254✔
334
  if (*str == '.') {
3,254✔
335
    /* parse the second fraction part */
336
    TAOS_CHECK_RETURN(parseFraction(str + 1, &str, timePrec, &fraction));
3,072✔
337
  }
338

339
  *utime = TSDB_TICK_PER_SECOND(timePrec) * seconds + fraction;
3,254✔
340
  TAOS_RETURN(TSDB_CODE_SUCCESS);
3,254✔
341
}
342

343
char getPrecisionUnit(int32_t precision) {
3✔
344
  static char units[3] = {TIME_UNIT_MILLISECOND, TIME_UNIT_MICROSECOND, TIME_UNIT_NANOSECOND};
345
  switch (precision) {
3✔
346
    case TSDB_TIME_PRECISION_MILLI:
3✔
347
    case TSDB_TIME_PRECISION_MICRO:
348
    case TSDB_TIME_PRECISION_NANO:
349
      return units[precision];
3✔
350
    default:
×
351
      return 0;
×
352
  }
353
}
354

355
int64_t convertTimePrecision(int64_t utime, int32_t fromPrecision, int32_t toPrecision) {
212,357✔
356
  switch (fromPrecision) {
212,357✔
357
    case TSDB_TIME_PRECISION_MILLI: {
117,337✔
358
      switch (toPrecision) {
117,337✔
359
        case TSDB_TIME_PRECISION_MILLI:
17,917✔
360
          return utime;
17,917✔
361
        case TSDB_TIME_PRECISION_MICRO:
37✔
362
          if (utime > INT64_MAX / 1000) {
37✔
363
            return INT64_MAX;
×
364
          }
365
          return utime * 1000;
37✔
366
        case TSDB_TIME_PRECISION_NANO:
99,392✔
367
          if (utime > INT64_MAX / 1000000) {
99,392✔
368
            return INT64_MAX;
×
369
          }
370
          return utime * 1000000;
99,392✔
371
        default:
×
372
          return utime;
×
373
      }
374
    }  // end from milli
375
    case TSDB_TIME_PRECISION_MICRO: {
5✔
376
      switch (toPrecision) {
5✔
377
        case TSDB_TIME_PRECISION_MILLI:
1✔
378
          return utime / 1000;
1✔
379
        case TSDB_TIME_PRECISION_MICRO:
×
380
          return utime;
×
381
        case TSDB_TIME_PRECISION_NANO:
4✔
382
          if (utime > INT64_MAX / 1000) {
4✔
383
            return INT64_MAX;
×
384
          }
385
          return utime * 1000;
4✔
386
        default:
×
387
          return utime;
×
388
      }
389
    }  // end from micro
390
    case TSDB_TIME_PRECISION_NANO: {
95,447✔
391
      switch (toPrecision) {
95,447✔
392
        case TSDB_TIME_PRECISION_MILLI:
95,416✔
393
          return utime / 1000000;
95,416✔
394
        case TSDB_TIME_PRECISION_MICRO:
4✔
395
          return utime / 1000;
4✔
396
        case TSDB_TIME_PRECISION_NANO:
4✔
397
          return utime;
4✔
398
        default:
23✔
399
          return utime;
23✔
400
      }
401
    }  // end from nano
402
    default: {
×
403
      return utime;  // only to pass windows compilation
×
404
    }
405
  }  // end switch fromPrecision
406

407
  return utime;
408
}
409

410
// !!!!notice:there are precision problems, double lose precison if time is too large, for example:
411
// 1626006833631000000*1.0 = double = 1626006833631000064
412
// int64_t convertTimePrecision(int64_t time, int32_t fromPrecision, int32_t toPrecision) {
413
//  static double factors[3][3] = {{1., 1000., 1000000.}, {1.0 / 1000, 1., 1000.}, {1.0 / 1000000, 1.0 / 1000, 1.}};
414
//  ((double)time * factors[fromPrecision][toPrecision]);
415
//}
416

417
// !!!!notice: double lose precison if time is too large, for example: 1626006833631000000*1.0 = double =
418
// 1626006833631000064
419
int32_t convertTimeFromPrecisionToUnit(int64_t time, int32_t fromPrecision, char toUnit, int64_t* pRes) {
78✔
420
  if (fromPrecision != TSDB_TIME_PRECISION_MILLI && fromPrecision != TSDB_TIME_PRECISION_MICRO &&
78✔
421
      fromPrecision != TSDB_TIME_PRECISION_NANO) {
422
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
423
  }
424

425
  int64_t factors[3] = {NANOSECOND_PER_MSEC, NANOSECOND_PER_USEC, 1};
78✔
426
  double  tmp = time;
78✔
427
  switch (toUnit) {
78✔
428
    case 's': {
21✔
429
      time /= (NANOSECOND_PER_SEC / factors[fromPrecision]);
21✔
430
      tmp = (double)time;
21✔
431
      break;
21✔
432
    }
433
    case 'm':
25✔
434
      time /= (NANOSECOND_PER_MINUTE / factors[fromPrecision]);
25✔
435
      tmp = (double)time;
25✔
436
      break;
25✔
437
    case 'h':
8✔
438
      time /= (NANOSECOND_PER_HOUR / factors[fromPrecision]);
8✔
439
      tmp = (double)time;
8✔
440
      break;
8✔
441
    case 'd':
13✔
442
      time /= (NANOSECOND_PER_DAY / factors[fromPrecision]);
13✔
443
      tmp = (double)time;
13✔
444
      break;
13✔
445
    case 'w':
8✔
446
      time /= (NANOSECOND_PER_WEEK / factors[fromPrecision]);
8✔
447
      tmp = (double)time;
8✔
448
      break;
8✔
449
    case 'a':
1✔
450
      time /= (NANOSECOND_PER_MSEC / factors[fromPrecision]);
1✔
451
      tmp = (double)time;
1✔
452
      break;
1✔
453
    case 'u':
1✔
454
      // the result of (NANOSECOND_PER_USEC/(double)factors[fromPrecision]) maybe a double
455
      switch (fromPrecision) {
456
        case TSDB_TIME_PRECISION_MILLI: {
1✔
457
          tmp *= 1000;
1✔
458
          time *= 1000;
1✔
459
          break;
1✔
460
        }
461
        case TSDB_TIME_PRECISION_MICRO: {
×
462
          time /= 1;
×
463
          tmp = (double)time;
×
464
          break;
×
465
        }
466
        case TSDB_TIME_PRECISION_NANO: {
×
467
          time /= 1000;
×
468
          tmp = (double)time;
×
469
          break;
×
470
        }
471
      }
472
      break;
1✔
473
    case 'b':
1✔
474
      tmp *= factors[fromPrecision];
1✔
475
      time *= factors[fromPrecision];
1✔
476
      break;
1✔
477
    default: {
×
478
      TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
479
    }
480
  }
481
  if (tmp >= (double)INT64_MAX) {
78✔
482
    *pRes = INT64_MAX;
×
483
  } else if (tmp <= (double)INT64_MIN) {
78✔
484
    *pRes = INT64_MIN;
×
485
  } else {
486
    *pRes = time;
78✔
487
  }
488

489
  TAOS_RETURN(TSDB_CODE_SUCCESS);
78✔
490
}
491

492
int32_t convertStringToTimestamp(int16_t type, char* inputData, int64_t timePrec, int64_t* timeVal, timezone_t tz, void* charsetCxt) {
182✔
493
  int32_t charLen = varDataLen(inputData);
182✔
494
  char*   newColData;
495
  if (type == TSDB_DATA_TYPE_BINARY || type == TSDB_DATA_TYPE_VARBINARY) {
364✔
496
    newColData = taosMemoryCalloc(1, charLen + 1);
182✔
497
    if (NULL == newColData) {
182✔
498
      TAOS_RETURN(terrno);
×
499
    }
500
    (void)memcpy(newColData, varDataVal(inputData), charLen);
182✔
501
    int32_t ret = taosParseTime(newColData, timeVal, charLen, (int32_t)timePrec, tz);
182✔
502
    if (ret != TSDB_CODE_SUCCESS) {
182✔
503
      taosMemoryFree(newColData);
×
504
      TAOS_RETURN(TSDB_CODE_INVALID_TIMESTAMP);
×
505
    }
506
    taosMemoryFree(newColData);
182✔
507
  } else if (type == TSDB_DATA_TYPE_NCHAR) {
×
508
    newColData = taosMemoryCalloc(1, charLen + TSDB_NCHAR_SIZE);
×
509
    if (NULL == newColData) {
×
510
      TAOS_RETURN(terrno);
×
511
    }
512
    int len = taosUcs4ToMbs((TdUcs4*)varDataVal(inputData), charLen, newColData, charsetCxt);
×
513
    if (len < 0) {
×
514
      taosMemoryFree(newColData);
×
515
      TAOS_RETURN(TSDB_CODE_FAILED);
×
516
    }
517
    newColData[len] = 0;
×
518
    int32_t ret = taosParseTime(newColData, timeVal, len, (int32_t)timePrec, tz);
×
519
    if (ret != TSDB_CODE_SUCCESS) {
×
520
      taosMemoryFree(newColData);
×
521
      TAOS_RETURN(ret);
×
522
    }
523
    taosMemoryFree(newColData);
×
524
  } else {
525
    TAOS_RETURN(TSDB_CODE_FAILED);
×
526
  }
527
  TAOS_RETURN(TSDB_CODE_SUCCESS);
182✔
528
}
529

530
int32_t getDuration(int64_t val, char unit, int64_t* result, int32_t timePrecision) {
16,566✔
531
  switch (unit) {
16,566✔
532
    case 's':
586✔
533
      if (val > INT64_MAX / MILLISECOND_PER_SECOND) {
586✔
534
        TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
535
      }
536
      (*result) = convertTimePrecision(val * MILLISECOND_PER_SECOND, TSDB_TIME_PRECISION_MILLI, timePrecision);
586✔
537
      break;
586✔
538
    case 'm':
144✔
539
      if (val > INT64_MAX / MILLISECOND_PER_MINUTE) {
144✔
540
        TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
541
      }
542
      (*result) = convertTimePrecision(val * MILLISECOND_PER_MINUTE, TSDB_TIME_PRECISION_MILLI, timePrecision);
144✔
543
      break;
144✔
544
    case 'h':
91✔
545
      if (val > INT64_MAX / MILLISECOND_PER_MINUTE) {
91✔
546
        TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
547
      }
548
      (*result) = convertTimePrecision(val * MILLISECOND_PER_HOUR, TSDB_TIME_PRECISION_MILLI, timePrecision);
91✔
549
      break;
91✔
550
    case 'd':
233✔
551
      if (val > INT64_MAX / MILLISECOND_PER_DAY) {
233✔
552
        TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
553
      }
554
      (*result) = convertTimePrecision(val * MILLISECOND_PER_DAY, TSDB_TIME_PRECISION_MILLI, timePrecision);
233✔
555
      break;
233✔
556
    case 'w':
25✔
557
      if (val > INT64_MAX / MILLISECOND_PER_WEEK) {
25✔
558
        TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
559
      }
560
      (*result) = convertTimePrecision(val * MILLISECOND_PER_WEEK, TSDB_TIME_PRECISION_MILLI, timePrecision);
25✔
561
      break;
25✔
562
    case 'a':
15,485✔
563
      (*result) = convertTimePrecision(val, TSDB_TIME_PRECISION_MILLI, timePrecision);
15,485✔
564
      break;
15,485✔
565
    case 'u':
1✔
566
      (*result) = convertTimePrecision(val, TSDB_TIME_PRECISION_MICRO, timePrecision);
1✔
567
      break;
1✔
568
    case 'b':
1✔
569
      (*result) = convertTimePrecision(val, TSDB_TIME_PRECISION_NANO, timePrecision);
1✔
570
      break;
1✔
571
    default: {
×
572
      TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
573
    }
574
  }
575
  TAOS_RETURN(TSDB_CODE_SUCCESS);
16,566✔
576
}
577

578
/*
579
 * n - months
580
 * y - Years
581
 * is not allowed, since the duration of month or year are both variable.
582
 *
583
 * b - nanoseconds;
584
 * u - microseconds;
585
 * a - Millionseconds
586
 * s - Seconds
587
 * m - Minutes
588
 * h - Hours
589
 * d - Days (24 hours)
590
 * w - Weeks (7 days)
591
 */
592
int32_t parseAbsoluteDuration(const char* token, int32_t tokenlen, int64_t* duration, char* unit,
15,805✔
593
                              int32_t timePrecision) {
594
  SET_ERRNO(0);
15,805✔
595
  char* endPtr = NULL;
15,805✔
596

597
  /* get the basic numeric value */
598
  int64_t timestamp = taosStr2Int64(token, &endPtr, 10);
15,805✔
599
  if ((timestamp == 0 && token[0] != '0') || ERRNO != 0) {
15,805✔
600
    TAOS_RETURN(TAOS_SYSTEM_ERROR(ERRNO));
×
601
  }
602

603
  /* natual month/year are not allowed in absolute duration */
604
  *unit = token[tokenlen - 1];
15,805✔
605
  if (*unit == 'n' || *unit == 'y') {
15,805✔
606
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
607
  }
608

609
  return getDuration(timestamp, *unit, duration, timePrecision);
15,805✔
610
}
611

612
int32_t parseNatualDuration(const char* token, int32_t tokenLen, int64_t* duration, char* unit, int32_t timePrecision,
723✔
613
                            bool negativeAllow) {
614
  SET_ERRNO(0);
723✔
615

616
  /* get the basic numeric value */
617
  *duration = taosStr2Int64(token, NULL, 10);
723✔
618
  if ((*duration < 0 && !negativeAllow) || ERRNO != 0) {
723✔
619
    TAOS_RETURN(TAOS_SYSTEM_ERROR(ERRNO));
×
620
  }
621

622
  *unit = token[tokenLen - 1];
723✔
623
  if (*unit == 'n' || *unit == 'y') {
723✔
624
    TAOS_RETURN(TSDB_CODE_SUCCESS);
34✔
625
  }
626
  if (isdigit(*unit)) {
689✔
627
    *unit = getPrecisionUnit(timePrecision);
×
628
  }
629

630
  return getDuration(*duration, *unit, duration, timePrecision);
689✔
631
}
632

633
static bool taosIsLeapYear(int32_t year) { return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); }
9✔
634

635
int64_t taosTimeAdd(int64_t t, int64_t duration, char unit, int32_t precision, timezone_t tz) {
14,347✔
636
  if (duration == 0) {
14,347✔
637
    return t;
4,902✔
638
  }
639

640
  if (!IS_CALENDAR_TIME_DURATION(unit)) {
9,445✔
641
    return t + duration;
9,436✔
642
  }
643

644
  // The following code handles the y/n time duration
645
  int64_t numOfMonth = (unit == 'y') ? duration * 12 : duration;
9✔
646
  int64_t fraction = t % TSDB_TICK_PER_SECOND(precision);
9✔
647

648
  struct tm  tm;
649
  time_t     tt = (time_t)(t / TSDB_TICK_PER_SECOND(precision));
9✔
650
  if(taosLocalTime(&tt, &tm, NULL, 0, tz) == NULL) {
9✔
651
    uError("failed to convert time to gm time, code:%d", ERRNO);
×
652
    return t;
×
653
  }
654
  int32_t    mon = tm.tm_year * 12 + tm.tm_mon + (int32_t)numOfMonth;
9✔
655
  tm.tm_year = mon / 12;
9✔
656
  tm.tm_mon = mon % 12;
9✔
657
  int daysOfMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
9✔
658
  if (taosIsLeapYear(1900 + tm.tm_year)) {
9✔
659
    daysOfMonth[1] = 29;
3✔
660
  }
661
  if (tm.tm_mday > daysOfMonth[tm.tm_mon]) {
9✔
662
    tm.tm_mday = daysOfMonth[tm.tm_mon];
3✔
663
  }
664

665
  tt = taosMktime(&tm, tz);
9✔
666
  if (tt == -1){
9✔
667
    uError("failed to convert gm time to time, code:%d", ERRNO);
×
668
    return t;
×
669
  }
670
  return (int64_t)(tt * TSDB_TICK_PER_SECOND(precision) + fraction);
9✔
671
}
672

673
/**
674
 * @brief calc how many windows after filling between skey and ekey
675
 * @notes for asc order
676
 *     skey      --->       ekey
677
 *      ^                    ^
678
 * _____!_____.........._____|_____..
679
 *      |__1__)
680
 *            |__2__)...-->|_ret+1_)
681
 *      skey + ret * interval <= ekey
682
 *      skey + ret * interval + interval > ekey
683
 * ======> (ekey - skey - interval) / interval < ret <= (ekey - skey) / interval
684
 * For keys from blocks which do not need filling, skey + ret * interval == ekey.
685
 * For keys need filling, skey + ret * interval <= ekey.
686
 * Total num of windows is ret + 1(the last window)
687
 *
688
 *        for desc order
689
 *     skey       <---      ekey
690
 *      ^                    ^
691
 * _____|____..........______!____...
692
 *                           |_first_)
693
 *                     |__1__)
694
 *  |_ret_)<--...|__2__)
695
 *      skey >= ekey - ret * interval
696
 *      skey < ekey - ret * interval + interval
697
 *=======> (ekey - skey) / interval <= ret < (ekey - skey + interval) / interval
698
 * For keys from blocks which do not need filling, skey == ekey - ret * interval.
699
 * For keys need filling, skey >= ekey - ret * interval.
700
 * Total num of windows is ret + 1(the first window)
701
 */
702
int32_t taosTimeCountIntervalForFill(int64_t skey, int64_t ekey, int64_t interval, char unit, int32_t precision,
×
703
                                     int32_t order) {
704
  if (ekey < skey) {
×
705
    int64_t tmp = ekey;
×
706
    ekey = skey;
×
707
    skey = tmp;
×
708
  }
709
  int32_t ret = 0;
×
710

711
  if (unit != 'n' && unit != 'y') {
×
712
    ret = (int32_t)((ekey - skey) / interval);
×
713
    if (order == TSDB_ORDER_DESC && ret * interval < (ekey - skey)) ret += 1;
×
714
  } else {
715
    skey /= (int64_t)(TSDB_TICK_PER_SECOND(precision));
×
716
    ekey /= (int64_t)(TSDB_TICK_PER_SECOND(precision));
×
717

718
    struct tm  tm;
719
    time_t     t = (time_t)skey;
×
720
    if (taosLocalTime(&t, &tm, NULL, 0, NULL) == NULL) {
×
721
      uError("%s failed to convert time to local time, code:%d", __FUNCTION__, ERRNO);
×
722
      return ret;
×
723
    }
724
    int32_t    smon = tm.tm_year * 12 + tm.tm_mon;
×
725

726
    t = (time_t)ekey;
×
727
    if (taosLocalTime(&t, &tm, NULL, 0, NULL) == NULL) {
×
728
      uError("%s failed to convert time to local time, code:%d", __FUNCTION__, ERRNO);
×
729
      return ret;
×
730
    }
731
    int32_t emon = tm.tm_year * 12 + tm.tm_mon;
×
732

733
    if (unit == 'y') {
×
734
      interval *= 12;
×
735
    }
736
    ret = (emon - smon) / (int32_t)interval;
×
737
    if (order == TSDB_ORDER_DESC && ret * interval < (smon - emon)) ret += 1;
×
738
  }
739
  return ret + 1;
×
740
}
741

742
int64_t taosTimeTruncate(int64_t ts, const SInterval* pInterval) {
1,498✔
743
  if (pInterval->sliding == 0) {
1,498✔
744
    return ts;
×
745
  }
746

747
  int64_t start = ts;
1,498✔
748
  int32_t precision = pInterval->precision;
1,498✔
749

750
  if (IS_CALENDAR_TIME_DURATION(pInterval->slidingUnit)) {
1,498✔
751
    start /= (int64_t)(TSDB_TICK_PER_SECOND(precision));
×
752
    struct tm  tm;
753
    time_t     tt = (time_t)start;
×
754
    if (taosLocalTime(&tt, &tm, NULL, 0, pInterval->timezone) == NULL){
×
755
      uError("%s failed to convert time to local time, code:%d", __FUNCTION__, ERRNO);
×
756
      return ts;
×
757
    }
758
    tm.tm_sec = 0;
×
759
    tm.tm_min = 0;
×
760
    tm.tm_hour = 0;
×
761
    tm.tm_mday = 1;
×
762

763
    if (pInterval->slidingUnit == 'y') {
×
764
      tm.tm_mon = 0;
×
765
      tm.tm_year = (int32_t)(tm.tm_year / pInterval->sliding * pInterval->sliding);
×
766
    } else {
767
      int32_t mon = tm.tm_year * 12 + tm.tm_mon;
×
768
      mon = (int32_t)(mon / pInterval->sliding * pInterval->sliding);
×
769
      tm.tm_year = mon / 12;
×
770
      tm.tm_mon = mon % 12;
×
771
    }
772

773
    tt = taosMktime(&tm, pInterval->timezone);
×
774
    if (tt == -1){
×
775
      uError("%s failed to convert local time to time, code:%d", __FUNCTION__, ERRNO);
×
776
      return ts;
×
777
    }
778
    start = (int64_t)(tt * TSDB_TICK_PER_SECOND(precision));
×
779
  } else {
780
    if (IS_CALENDAR_TIME_DURATION(pInterval->intervalUnit)) {
1,498✔
781
      int64_t news = (ts / pInterval->sliding) * pInterval->sliding;
×
782
      if (pInterval->slidingUnit == 'd' || pInterval->slidingUnit == 'w') {
×
783
#if defined(WINDOWS) && _MSC_VER >= 1900
784
        int64_t timezone = _timezone;
785
#endif
786
        news += (int64_t)(timezone * TSDB_TICK_PER_SECOND(precision));
×
787
      }
788

789
      start = news;
×
790
      if (news <= ts) {
×
791
        int64_t prev = news;
×
792
        int64_t newe = taosTimeAdd(news, pInterval->interval, pInterval->intervalUnit, precision, pInterval->timezone) - 1;
×
793

794
        if (newe < ts) {  // move towards the greater endpoint
×
795
          while (newe < ts && news < ts) {
×
796
            news += pInterval->sliding;
×
797
            newe = taosTimeAdd(news, pInterval->interval, pInterval->intervalUnit, precision, pInterval->timezone) - 1;
×
798
          }
799

800
          prev = news;
×
801
        } else {
802
          while (newe >= ts) {
×
803
            prev = news;
×
804
            news -= pInterval->sliding;
×
805
            newe = taosTimeAdd(news, pInterval->interval, pInterval->intervalUnit, precision, pInterval->timezone) - 1;
×
806
          }
807
        }
808

809
        start = prev;
×
810
      }
811
    } else {
812
      int64_t delta = ts - pInterval->interval;
1,498✔
813
      int32_t factor = (delta >= 0) ? 1 : -1;
1,498✔
814

815
      start = (delta / pInterval->sliding + factor) * pInterval->sliding;
1,498✔
816

817
      if (pInterval->intervalUnit == 'd' || pInterval->intervalUnit == 'w') {
1,498✔
818
        /*
819
         * here we revised the start time of day according to the local time zone,
820
         * but in case of DST, the start time of one day need to be dynamically decided.
821
         */
822
        // todo refactor to extract function that is available for Linux/Windows/Mac platform
823
#if defined(WINDOWS) && _MSC_VER >= 1900
824
        // see
825
        // https://docs.microsoft.com/en-us/cpp/c-runtime-library/daylight-dstbias-timezone-and-tzname?view=vs-2019
826
        int64_t timezone = _timezone;
827
#endif
828

829
        start += (int64_t)(timezone * TSDB_TICK_PER_SECOND(precision));
18✔
830
      }
831

832
      int64_t end = 0;
1,498✔
833

834
      // not enough time range
835
      if (start < 0 || INT64_MAX - start > pInterval->interval - 1) {
1,498✔
836
        end = taosTimeAdd(start, pInterval->interval, pInterval->intervalUnit, precision, pInterval->timezone) - 1;
1,498✔
837
        while (end < ts) {  // move forward to the correct time window
1,516✔
838
          start += pInterval->sliding;
18✔
839

840
          if (start < 0 || INT64_MAX - start > pInterval->interval - 1) {
18✔
841
            end = start + pInterval->interval - 1;
18✔
842
          } else {
843
            end = INT64_MAX;
×
844
            break;
×
845
          }
846
        }
847
      } else {
848
        end = INT64_MAX;
×
849
      }
850
    }
851
  }
852

853
  if (pInterval->offset > 0) {
1,498✔
854
    // try to move current window to the left-hande-side, due to the offset effect.
855
    int64_t newe = taosTimeAdd(start, pInterval->interval, pInterval->intervalUnit, precision, pInterval->timezone) - 1;
12✔
856
    int64_t slidingStart = start;
12✔
857
    while (newe >= ts) {
36✔
858
      start = slidingStart;
24✔
859
      slidingStart = taosTimeAdd(slidingStart, -pInterval->sliding, pInterval->slidingUnit, precision, pInterval->timezone);
24✔
860
      int64_t news = taosTimeAdd(slidingStart, pInterval->offset, pInterval->offsetUnit, precision, pInterval->timezone);
24✔
861
      newe = taosTimeAdd(news, pInterval->interval, pInterval->intervalUnit, precision, pInterval->timezone) - 1;
24✔
862
    }
863
    start = taosTimeAdd(start, pInterval->offset, pInterval->offsetUnit, precision, pInterval->timezone);
12✔
864
  }
865

866
  return start;
1,498✔
867
}
868

869
// used together with taosTimeTruncate. when offset is great than zero, slide-start/slide-end is the anchor point
870
int64_t taosTimeGetIntervalEnd(int64_t intervalStart, const SInterval* pInterval) {
1,456✔
871
  return taosTimeAdd(intervalStart, pInterval->interval, pInterval->intervalUnit, pInterval->precision, pInterval->timezone) - 1;
1,456✔
872
}
873

874
void calcIntervalAutoOffset(SInterval* interval) {
1,616✔
875
  if (!interval || interval->offset != AUTO_DURATION_VALUE) {
1,616✔
876
    return;
1,616✔
877
  }
878

879
  interval->offset = 0;
×
880

881
  if (interval->timeRange.skey == INT64_MIN) {
×
882
    return;
×
883
  }
884

885
  TSKEY skey = interval->timeRange.skey;
×
886
  TSKEY start = taosTimeTruncate(skey, interval);
×
887
  TSKEY news = start;
×
888
  while (news <= skey) {
×
889
    start = news;
×
890
    news = taosTimeAdd(start, interval->sliding, interval->slidingUnit, interval->precision, interval->timezone);
×
891
    if (news < start) {
×
892
      // overflow happens
893
      uError("%s failed and skip, skey [%" PRId64 "], inter[%" PRId64 "(%c)], slid[%" PRId64 "(%c)], precision[%d]",
×
894
             __func__, skey, interval->interval, interval->intervalUnit, interval->sliding, interval->slidingUnit,
895
             interval->precision);
896
      return;
×
897
    }
898
  }
899
  interval->offset = skey - start;
×
900
}
901

902
// internal function, when program is paused in debugger,
903
// one can call this function from debugger to print a
904
// timestamp as human readable string, for example (gdb):
905
//     p fmtts(1593769722)
906
// outputs:
907
//     2020-07-03 17:48:42
908
// and the parameter can also be a variable.
909
const char* fmtts(int64_t ts) {
×
910
  static char buf[TD_TIME_STR_LEN] = {0};
911
  size_t      pos = 0;
×
912
  struct tm   tm;
913

914
  if (ts > -62135625943 && ts < 32503651200) {
×
915
    time_t t = (time_t)ts;
×
916
    if (taosLocalTime(&t, &tm, buf, sizeof(buf), NULL) == NULL) {
×
917
      return buf;
×
918
    }
919
    pos += taosStrfTime(buf + pos, sizeof(buf), "s=%Y-%m-%d %H:%M:%S", &tm);
×
920
  }
921

922
  if (ts > -62135625943000 && ts < 32503651200000) {
×
923
    time_t t = (time_t)(ts / 1000);
×
924
    if (taosLocalTime(&t, &tm, buf, sizeof(buf), NULL) == NULL) {
×
925
      return buf;
×
926
    }
927
    if (pos > 0) {
×
928
      buf[pos++] = ' ';
×
929
      buf[pos++] = '|';
×
930
      buf[pos++] = ' ';
×
931
    }
932
    pos += taosStrfTime(buf + pos, sizeof(buf), "ms=%Y-%m-%d %H:%M:%S", &tm);
×
933
    pos += sprintf(buf + pos, ".%03d", (int32_t)(ts % 1000));
×
934
  }
935

936
  {
937
    time_t t = (time_t)(ts / 1000000);
×
938
    if (taosLocalTime(&t, &tm, buf, sizeof(buf), NULL) == NULL) {
×
939
      return buf;
×
940
    }
941
    if (pos > 0) {
×
942
      buf[pos++] = ' ';
×
943
      buf[pos++] = '|';
×
944
      buf[pos++] = ' ';
×
945
    }
946
    pos += taosStrfTime(buf + pos, sizeof(buf), "us=%Y-%m-%d %H:%M:%S", &tm);
×
947
    pos += sprintf(buf + pos, ".%06d", (int32_t)(ts % 1000000));
×
948
  }
949

950
  return buf;
×
951
}
952

953
int32_t taosFormatUtcTime(char* buf, int32_t bufLen, int64_t t, int32_t precision) {
6✔
954
  char      ts[40] = {0};
6✔
955
  struct tm ptm;
956

957
  int32_t fractionLen;
958
  char*   format = NULL;
6✔
959
  time_t  quot = 0;
6✔
960
  long    mod = 0;
6✔
961

962
  switch (precision) {
6✔
963
    case TSDB_TIME_PRECISION_MILLI: {
4✔
964
      quot = t / 1000;
4✔
965
      fractionLen = 5;
4✔
966
      format = ".%03" PRId64;
4✔
967
      mod = t % 1000;
4✔
968
      break;
4✔
969
    }
970

971
    case TSDB_TIME_PRECISION_MICRO: {
1✔
972
      quot = t / 1000000;
1✔
973
      fractionLen = 8;
1✔
974
      format = ".%06" PRId64;
1✔
975
      mod = t % 1000000;
1✔
976
      break;
1✔
977
    }
978

979
    case TSDB_TIME_PRECISION_NANO: {
1✔
980
      quot = t / 1000000000;
1✔
981
      fractionLen = 11;
1✔
982
      format = ".%09" PRId64;
1✔
983
      mod = t % 1000000000;
1✔
984
      break;
1✔
985
    }
986

987
    default:
×
988
      fractionLen = 0;
×
989
      TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
990
  }
991

992
  if (NULL == taosLocalTime(&quot, &ptm, buf, bufLen, NULL)) {
6✔
993
    TAOS_RETURN(TAOS_SYSTEM_ERROR(ERRNO));
×
994
  }
995
  int32_t length = (int32_t)taosStrfTime(ts, 40, "%Y-%m-%dT%H:%M:%S", &ptm);
6✔
996
  length += tsnprintf(ts + length, fractionLen, format, mod);
6✔
997
  length += (int32_t)taosStrfTime(ts + length, 40 - length, "%z", &ptm);
6✔
998

999
  tstrncpy(buf, ts, bufLen);
6✔
1000
  TAOS_RETURN(TSDB_CODE_SUCCESS);
6✔
1001
}
1002

1003
char* formatTimestampLocal(char* buf, int64_t val, int precision) {
5,902✔
1004
  time_t tt;
1005
  if (precision == TSDB_TIME_PRECISION_MICRO) {
5,902✔
1006
    tt = (time_t)(val / 1000000);
×
1007
  }
1008
  if (precision == TSDB_TIME_PRECISION_NANO) {
5,902✔
1009
    tt = (time_t)(val / 1000000000);
×
1010
  } else {
1011
    tt = (time_t)(val / 1000);
5,902✔
1012
  }
1013

1014
  struct tm tm;
1015
  if (taosLocalTime(&tt, &tm, NULL, 0, NULL) == NULL) {
5,902✔
1016
    return NULL;
×
1017
  }
1018
  size_t pos = taosStrfTime(buf, 32, "%Y-%m-%d %H:%M:%S", &tm);
5,902✔
1019

1020
  if (precision == TSDB_TIME_PRECISION_MICRO) {
5,901✔
1021
    sprintf(buf + pos, ".%06d", (int)(val % 1000000));
×
1022
  } else if (precision == TSDB_TIME_PRECISION_NANO) {
5,901✔
1023
    sprintf(buf + pos, ".%09d", (int)(val % 1000000000));
×
1024
  } else {
1025
    sprintf(buf + pos, ".%03d", (int)(val % 1000));
5,901✔
1026
  }
1027

1028
  return buf;
5,901✔
1029
}
1030

1031
int32_t taosTs2Tm(int64_t ts, int32_t precision, struct STm* tm, timezone_t tz) {
49✔
1032
  tm->fsec = ts % TICK_PER_SECOND[precision] * (TICK_PER_SECOND[TSDB_TIME_PRECISION_NANO] / TICK_PER_SECOND[precision]);
49✔
1033
  time_t t = ts / TICK_PER_SECOND[precision];
49✔
1034
  if (NULL == taosLocalTime(&t, &tm->tm, NULL, 0, tz)) {
49✔
1035
    TAOS_RETURN(TAOS_SYSTEM_ERROR(ERRNO));
×
1036
  }
1037
  return TSDB_CODE_SUCCESS;
49✔
1038
}
1039

1040
int32_t taosTm2Ts(struct STm* tm, int64_t* ts, int32_t precision, timezone_t tz) {
37✔
1041
  *ts = taosMktime(&tm->tm, tz);
37✔
1042
  *ts *= TICK_PER_SECOND[precision];
37✔
1043
  *ts += tm->fsec / (TICK_PER_SECOND[TSDB_TIME_PRECISION_NANO] / TICK_PER_SECOND[precision]);
37✔
1044
  return TSDB_CODE_SUCCESS;
37✔
1045
}
1046

1047
typedef struct {
1048
  const char* name;
1049
  int         len;
1050
  int         id;
1051
  bool        isDigit;
1052
} TSFormatKeyWord;
1053

1054
typedef enum {
1055
  // TSFKW_AD,   // BC AD
1056
  // TSFKW_A_D,  // A.D. B.C.
1057
  TSFKW_AM,   // AM, PM
1058
  TSFKW_A_M,  // A.M., P.M.
1059
  // TSFKW_BC,   // BC AD
1060
  // TSFKW_B_C,  // B.C. A.D.
1061
  TSFKW_DAY,  // MONDAY, TUESDAY ...
1062
  TSFKW_DDD,  // Day of year 001-366
1063
  TSFKW_DD,   // Day of month 01-31
1064
  TSFKW_Day,  // Sunday, Monday
1065
  TSFKW_DY,   // MON, TUE
1066
  TSFKW_Dy,   // Mon, Tue
1067
  TSFKW_D,    // 1-7 -> Sunday(1) -> Saturday(7)
1068
  TSFKW_HH24,
1069
  TSFKW_HH12,
1070
  TSFKW_HH,
1071
  TSFKW_MI,  // minute
1072
  TSFKW_MM,
1073
  TSFKW_MONTH,  // JANUARY, FEBRUARY
1074
  TSFKW_MON,
1075
  TSFKW_Month,
1076
  TSFKW_Mon,
1077
  TSFKW_MS,
1078
  TSFKW_NS,
1079
  // TSFKW_OF,
1080
  TSFKW_PM,
1081
  TSFKW_P_M,
1082
  TSFKW_SS,
1083
  TSFKW_TZH,
1084
  // TSFKW_TZM,
1085
  // TSFKW_TZ,
1086
  TSFKW_US,
1087
  TSFKW_YYYY,
1088
  TSFKW_YYY,
1089
  TSFKW_YY,
1090
  TSFKW_Y,
1091
  // TSFKW_a_d,
1092
  // TSFKW_ad,
1093
  TSFKW_am,
1094
  TSFKW_a_m,
1095
  // TSFKW_b_c,
1096
  // TSFKW_bc,
1097
  TSFKW_day,
1098
  TSFKW_ddd,
1099
  TSFKW_dd,
1100
  TSFKW_dy,  // mon, tue
1101
  TSFKW_d,
1102
  TSFKW_hh24,
1103
  TSFKW_hh12,
1104
  TSFKW_hh,
1105
  TSFKW_mi,
1106
  TSFKW_mm,
1107
  TSFKW_month,
1108
  TSFKW_mon,
1109
  TSFKW_ms,
1110
  TSFKW_ns,
1111
  TSFKW_pm,
1112
  TSFKW_p_m,
1113
  TSFKW_ss,
1114
  TSFKW_tzh,
1115
  // TSFKW_tzm,
1116
  // TSFKW_tz,
1117
  TSFKW_us,
1118
  TSFKW_yyyy,
1119
  TSFKW_yyy,
1120
  TSFKW_yy,
1121
  TSFKW_y,
1122
  TSFKW_last_
1123
} TSFormatKeywordId;
1124

1125
// clang-format off
1126
static const TSFormatKeyWord formatKeyWords[] = {
1127
  //{"AD", 2, TSFKW_AD, false},
1128
  //{"A.D.", 4, TSFKW_A_D},
1129
  {"AM", 2, TSFKW_AM, false},
1130
  {"A.M.", 4, TSFKW_A_M, false},
1131
  //{"BC", 2, TSFKW_BC, false},
1132
  //{"B.C.", 4, TSFKW_B_C, false},
1133
  {"DAY", 3, TSFKW_DAY, false},
1134
  {"DDD", 3, TSFKW_DDD, true},
1135
  {"DD", 2, TSFKW_DD, true},
1136
  {"Day", 3, TSFKW_Day, false},
1137
  {"DY", 2, TSFKW_DY, false},
1138
  {"Dy", 2, TSFKW_Dy, false},
1139
  {"D", 1, TSFKW_D, true},
1140
  {"HH24", 4, TSFKW_HH24, true},
1141
  {"HH12", 4, TSFKW_HH12, true},
1142
  {"HH", 2, TSFKW_HH, true},
1143
  {"MI", 2, TSFKW_MI, true},
1144
  {"MM", 2, TSFKW_MM, true},
1145
  {"MONTH", 5, TSFKW_MONTH, false},
1146
  {"MON", 3, TSFKW_MON, false},
1147
  {"Month", 5, TSFKW_Month, false},
1148
  {"Mon", 3, TSFKW_Mon, false},
1149
  {"MS", 2, TSFKW_MS, true},
1150
  {"NS", 2, TSFKW_NS, true},
1151
  //{"OF", 2, TSFKW_OF, false},
1152
  {"PM", 2, TSFKW_PM, false},
1153
  {"P.M.", 4, TSFKW_P_M, false},
1154
  {"SS", 2, TSFKW_SS, true},
1155
  {"TZH", 3, TSFKW_TZH, false},
1156
  //{"TZM", 3, TSFKW_TZM},
1157
  //{"TZ", 2, TSFKW_TZ},
1158
  {"US", 2, TSFKW_US, true},
1159
  {"YYYY", 4, TSFKW_YYYY, true},
1160
  {"YYY", 3, TSFKW_YYY, true},
1161
  {"YY", 2, TSFKW_YY, true},
1162
  {"Y", 1, TSFKW_Y, true},
1163
  //{"a.d.", 4, TSFKW_a_d, false},
1164
  //{"ad", 2, TSFKW_ad, false},
1165
  {"am", 2, TSFKW_am, false},
1166
  {"a.m.", 4, TSFKW_a_m, false},
1167
  //{"b.c.", 4, TSFKW_b_c, false},
1168
  //{"bc", 2, TSFKW_bc, false},
1169
  {"day", 3, TSFKW_day, false},
1170
  {"ddd", 3, TSFKW_DDD, true},
1171
  {"dd", 2, TSFKW_DD, true},
1172
  {"dy", 2, TSFKW_dy, false},
1173
  {"d", 1, TSFKW_D, true},
1174
  {"hh24", 4, TSFKW_HH24, true},
1175
  {"hh12", 4, TSFKW_HH12, true},
1176
  {"hh", 2, TSFKW_HH, true},
1177
  {"mi", 2, TSFKW_MI, true},
1178
  {"mm", 2, TSFKW_MM, true},
1179
  {"month", 5, TSFKW_month, false},
1180
  {"mon", 3, TSFKW_mon, false},
1181
  {"ms", 2, TSFKW_MS, true},
1182
  {"ns", 2, TSFKW_NS, true},
1183
  //{"of", 2, TSFKW_OF, false},
1184
  {"pm", 2, TSFKW_pm, false},
1185
  {"p.m.", 4, TSFKW_p_m, false},
1186
  {"ss", 2, TSFKW_SS, true},
1187
  {"tzh", 3, TSFKW_TZH, false},
1188
  //{"tzm", 3, TSFKW_TZM},
1189
  //{"tz", 2, TSFKW_tz},
1190
  {"us", 2, TSFKW_US, true},
1191
  {"yyyy", 4, TSFKW_YYYY, true},
1192
  {"yyy", 3, TSFKW_YYY, true},
1193
  {"yy", 2, TSFKW_YY, true},
1194
  {"y", 1, TSFKW_Y, true},
1195
  {NULL, 0, 0}
1196
};
1197
// clang-format on
1198

1199
#define TS_FROMAT_KEYWORD_INDEX_SIZE ('z' - 'A' + 1)
1200
static const int TSFormatKeywordIndex[TS_FROMAT_KEYWORD_INDEX_SIZE] = {
1201
    /*A*/ TSFKW_AM,     -1, -1,
1202
    /*D*/ TSFKW_DAY,    -1, -1, -1,
1203
    /*H*/ TSFKW_HH24,   -1, -1, -1, -1,
1204
    /*M*/ TSFKW_MI,
1205
    /*N*/ TSFKW_NS,     -1,
1206
    /*P*/ TSFKW_PM,     -1, -1,
1207
    /*S*/ TSFKW_SS,
1208
    /*T*/ TSFKW_TZH,
1209
    /*U*/ TSFKW_US,     -1, -1, -1,
1210
    /*Y*/ TSFKW_YYYY,   -1,
1211
    /*[ \ ] ^ _ `*/ -1, -1, -1, -1, -1, -1,
1212
    /*a*/ TSFKW_am,     -1, -1,
1213
    /*d*/ TSFKW_day,    -1, -1, -1,
1214
    /*h*/ TSFKW_hh24,   -1, -1, -1, -1,
1215
    /*m*/ TSFKW_mi,
1216
    /*n*/ TSFKW_ns,     -1,
1217
    /*p*/ TSFKW_pm,     -1, -1,
1218
    /*s*/ TSFKW_ss,
1219
    /*t*/ TSFKW_tzh,
1220
    /*u*/ TSFKW_us,     -1, -1, -1,
1221
    /*y*/ TSFKW_yyyy,   -1};
1222

1223
typedef struct {
1224
  uint8_t                type;
1225
  const char*            c;
1226
  int32_t                len;
1227
  const TSFormatKeyWord* key;
1228
} TSFormatNode;
1229

1230
static const char* const weekDays[] = {"Sunday",   "Monday", "Tuesday",  "Wednesday",
1231
                                       "Thursday", "Friday", "Saturday", "NULL"};
1232
static const char* const shortWeekDays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "NULL"};
1233
static const char* const fullMonths[] = {"January", "February",  "March",   "April",    "May",      "June", "July",
1234
                                         "August",  "September", "October", "November", "December", NULL};
1235
static const char* const months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
1236
                                     "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
1237
#define A_M_STR "A.M."
1238
#define a_m_str "a.m."
1239
#define AM_STR  "AM"
1240
#define am_str  "am"
1241
#define P_M_STR "P.M."
1242
#define p_m_str "p.m."
1243
#define PM_STR  "PM"
1244
#define pm_str  "pm"
1245
static const char* const apms[] = {AM_STR, PM_STR, am_str, pm_str, NULL};
1246
static const char* const long_apms[] = {A_M_STR, P_M_STR, a_m_str, p_m_str, NULL};
1247

1248
#define TS_FORMAT_NODE_TYPE_KEYWORD   1
1249
#define TS_FORMAT_NODE_TYPE_SEPARATOR 2
1250
#define TS_FORMAT_NODE_TYPE_CHAR      3
1251

1252
static const TSFormatKeyWord* keywordSearch(const char* str) {
790✔
1253
  if (*str < 'A' || *str > 'z' || (*str > 'Z' && *str < 'a')) return NULL;
790✔
1254
  int32_t idx = TSFormatKeywordIndex[str[0] - 'A'];
422✔
1255
  if (idx < 0) return NULL;
422✔
1256
  const TSFormatKeyWord* key = &formatKeyWords[idx++];
422✔
1257
  while (key->name && str[0] == key->name[0]) {
851✔
1258
    if (0 == strncmp(key->name, str, key->len)) {
818✔
1259
      return key;
389✔
1260
    }
1261
    key = &formatKeyWords[idx++];
429✔
1262
  }
1263
  return NULL;
33✔
1264
}
1265

1266
static bool isSeperatorChar(char c) {
516✔
1267
  return (c > 0x20 && c < 0x7F && !(c >= 'A' && c <= 'Z') && !(c >= 'a' && c <= 'z') && !(c >= '0' && c <= '9'));
516✔
1268
}
1269

1270
static int32_t parseTsFormat(const char* formatStr, SArray* formats) {
59✔
1271
  TSFormatNode* lastOtherFormat = NULL;
59✔
1272
  while (*formatStr) {
849✔
1273
    const TSFormatKeyWord* key = keywordSearch(formatStr);
790✔
1274
    if (key) {
790✔
1275
      TSFormatNode format = {.key = key, .type = TS_FORMAT_NODE_TYPE_KEYWORD};
389✔
1276
      if (NULL == taosArrayPush(formats, &format)) TAOS_RETURN(terrno);
389✔
1277
      formatStr += key->len;
389✔
1278
      lastOtherFormat = NULL;
389✔
1279
    } else {
1280
      if (*formatStr == '"') {
401✔
1281
        lastOtherFormat = NULL;
6✔
1282
        // for double quoted string
1283
        formatStr++;
6✔
1284
        TSFormatNode* last = NULL;
6✔
1285
        while (*formatStr) {
92✔
1286
          if (*formatStr == '"') {
91✔
1287
            formatStr++;
5✔
1288
            break;
5✔
1289
          }
1290
          if (*formatStr == '\\' && *(formatStr + 1)) {
86✔
1291
            formatStr++;
×
1292
            last = NULL;  // stop expanding last format, create new format
×
1293
          }
1294
          if (last) {
86✔
1295
            // expand
1296
            last->len++;
80✔
1297
            formatStr++;
80✔
1298
          } else {
1299
            // create new
1300
            TSFormatNode format = {.type = TS_FORMAT_NODE_TYPE_CHAR, .key = NULL};
6✔
1301
            format.c = formatStr;
6✔
1302
            format.len = 1;
6✔
1303
            if (NULL == taosArrayPush(formats, &format)) TAOS_RETURN(terrno);
6✔
1304
            formatStr++;
6✔
1305
            last = taosArrayGetLast(formats);
6✔
1306
          }
1307
        }
1308
      } else {
1309
        // for other strings
1310
        if (*formatStr == '\\' && *(formatStr + 1)) {
395✔
1311
          formatStr++;
3✔
1312
          lastOtherFormat = NULL;  // stop expanding
3✔
1313
        } else {
1314
          if (lastOtherFormat && !isSeperatorChar(*formatStr)) {
392✔
1315
            // expanding
1316
          } else {
1317
            // create new
1318
            lastOtherFormat = NULL;
328✔
1319
          }
1320
        }
1321
        if (lastOtherFormat) {
395✔
1322
          lastOtherFormat->len++;
64✔
1323
          formatStr++;
64✔
1324
        } else {
1325
          TSFormatNode format = {
331✔
1326
              .type = isSeperatorChar(*formatStr) ? TS_FORMAT_NODE_TYPE_SEPARATOR : TS_FORMAT_NODE_TYPE_CHAR,
331✔
1327
              .key = NULL};
1328
          format.c = formatStr;
331✔
1329
          format.len = 1;
331✔
1330
          if (NULL == taosArrayPush(formats, &format)) TAOS_RETURN(terrno);
331✔
1331
          formatStr++;
331✔
1332
          if (format.type == TS_FORMAT_NODE_TYPE_CHAR) lastOtherFormat = taosArrayGetLast(formats);
331✔
1333
        }
1334
      }
1335
    }
1336
  }
1337
  TAOS_RETURN(TSDB_CODE_SUCCESS);
59✔
1338
}
1339

1340
static int32_t tm2char(const SArray* formats, const struct STm* tm, char* s, int32_t outLen) {
18✔
1341
  int32_t     size = taosArrayGetSize(formats);
18✔
1342
  const char* start = s;
18✔
1343
  for (int32_t i = 0; i < size; ++i) {
338✔
1344
    TSFormatNode* format = taosArrayGet(formats, i);
320✔
1345
    if (format->type != TS_FORMAT_NODE_TYPE_KEYWORD) {
320✔
1346
      if (s - start + format->len + 1 > outLen) break;
161✔
1347
      (void)strncpy(s, format->c, format->len);
161✔
1348
      s += format->len;
161✔
1349
      continue;
161✔
1350
    }
1351
    if (s - start + 16 > outLen) break;
159✔
1352

1353
    switch (format->key->id) {
159✔
1354
      case TSFKW_AM:
8✔
1355
      case TSFKW_PM:
1356
        (void)sprintf(s, tm->tm.tm_hour % 24 >= 12 ? "PM" : "AM");
8✔
1357
        s += 2;
8✔
1358
        break;
8✔
1359
      case TSFKW_A_M:
×
1360
      case TSFKW_P_M:
1361
        (void)sprintf(s, tm->tm.tm_hour % 24 >= 12 ? "P.M." : "A.M.");
×
1362
        s += 4;
×
1363
        break;
×
1364
      case TSFKW_am:
9✔
1365
      case TSFKW_pm:
1366
        (void)sprintf(s, tm->tm.tm_hour % 24 >= 12 ? "pm" : "am");
9✔
1367
        s += 2;
9✔
1368
        break;
9✔
1369
      case TSFKW_a_m:
×
1370
      case TSFKW_p_m:
1371
        (void)sprintf(s, tm->tm.tm_hour % 24 >= 12 ? "p.m." : "a.m.");
×
1372
        s += 4;
×
1373
        break;
×
1374
      case TSFKW_DDD:
2✔
1375
#ifdef WINDOWS
1376
        return TSDB_CODE_FUNC_TO_CHAR_NOT_SUPPORTED;
1377
#endif
1378
        (void)sprintf(s, "%03d", tm->tm.tm_yday + 1);
2✔
1379
        s += strlen(s);
2✔
1380
        break;
2✔
1381
      case TSFKW_DD:
16✔
1382
        (void)sprintf(s, "%02d", tm->tm.tm_mday);
16✔
1383
        s += 2;
16✔
1384
        break;
16✔
1385
      case TSFKW_D:
2✔
1386
        (void)sprintf(s, "%d", tm->tm.tm_wday + 1);
2✔
1387
        s += 1;
2✔
1388
        break;
2✔
1389
      case TSFKW_DAY: {
2✔
1390
        // MONDAY, TUESDAY...
1391
        const char* wd = weekDays[tm->tm.tm_wday];
2✔
1392
        char        buf[10] = {0};
2✔
1393
        for (int32_t i = 0; i < strlen(wd); ++i) buf[i] = toupper(wd[i]);
14✔
1394
        (void)sprintf(s, "%-9s", buf);
2✔
1395
        s += strlen(s);
2✔
1396
        break;
2✔
1397
      }
1398
      case TSFKW_Day:
4✔
1399
        // Monday, TuesDay...
1400
        (void)sprintf(s, "%-9s", weekDays[tm->tm.tm_wday]);
4✔
1401
        s += strlen(s);
4✔
1402
        break;
4✔
1403
      case TSFKW_day: {
1✔
1404
        const char* wd = weekDays[tm->tm.tm_wday];
1✔
1405
        char        buf[10] = {0};
1✔
1406
        for (int32_t i = 0; i < strlen(wd); ++i) buf[i] = tolower(wd[i]);
7✔
1407
        (void)sprintf(s, "%-9s", buf);
1✔
1408
        s += strlen(s);
1✔
1409
        break;
1✔
1410
      }
1411
      case TSFKW_DY: {
×
1412
        // MON, TUE
1413
        const char* wd = shortWeekDays[tm->tm.tm_wday];
×
1414
        char        buf[8] = {0};
×
1415
        for (int32_t i = 0; i < strlen(wd); ++i) buf[i] = toupper(wd[i]);
×
1416
        (void)sprintf(s, "%3s", buf);
×
1417
        s += 3;
×
1418
        break;
×
1419
      }
1420
      case TSFKW_Dy:
×
1421
        // Mon, Tue
1422
        (void)sprintf(s, "%3s", shortWeekDays[tm->tm.tm_wday]);
×
1423
        s += 3;
×
1424
        break;
×
1425
      case TSFKW_dy: {
×
1426
        // mon, tue
1427
        const char* wd = shortWeekDays[tm->tm.tm_wday];
×
1428
        char        buf[8] = {0};
×
1429
        for (int32_t i = 0; i < strlen(wd); ++i) buf[i] = tolower(wd[i]);
×
1430
        (void)sprintf(s, "%3s", buf);
×
1431
        s += 3;
×
1432
        break;
×
1433
      }
1434
      case TSFKW_HH24:
12✔
1435
        (void)sprintf(s, "%02d", tm->tm.tm_hour);
12✔
1436
        s += 2;
12✔
1437
        break;
12✔
1438
      case TSFKW_HH:
13✔
1439
      case TSFKW_HH12:
1440
        // 0 or 12 o'clock in 24H coresponds to 12 o'clock (AM/PM) in 12H
1441
        (void)sprintf(s, "%02d", tm->tm.tm_hour % 12 == 0 ? 12 : tm->tm.tm_hour % 12);
13✔
1442
        s += 2;
13✔
1443
        break;
13✔
1444
      case TSFKW_MI:
13✔
1445
        (void)sprintf(s, "%02d", tm->tm.tm_min);
13✔
1446
        s += 2;
13✔
1447
        break;
13✔
1448
      case TSFKW_MM:
11✔
1449
        (void)sprintf(s, "%02d", tm->tm.tm_mon + 1);
11✔
1450
        s += 2;
11✔
1451
        break;
11✔
1452
      case TSFKW_MONTH: {
1✔
1453
        const char* mon = fullMonths[tm->tm.tm_mon];
1✔
1454
        char        buf[10] = {0};
1✔
1455
        for (int32_t i = 0; i < strlen(mon); ++i) buf[i] = toupper(mon[i]);
8✔
1456
        (void)sprintf(s, "%-9s", buf);
1✔
1457
        s += strlen(s);
1✔
1458
        break;
1✔
1459
      }
1460
      case TSFKW_MON: {
1✔
1461
        const char* mon = months[tm->tm.tm_mon];
1✔
1462
        char        buf[10] = {0};
1✔
1463
        for (int32_t i = 0; i < strlen(mon); ++i) buf[i] = toupper(mon[i]);
4✔
1464
        (void)sprintf(s, "%s", buf);
1✔
1465
        s += strlen(s);
1✔
1466
        break;
1✔
1467
      }
1468
      case TSFKW_Month:
3✔
1469
        (void)sprintf(s, "%-9s", fullMonths[tm->tm.tm_mon]);
3✔
1470
        s += strlen(s);
3✔
1471
        break;
3✔
1472
      case TSFKW_month: {
1✔
1473
        const char* mon = fullMonths[tm->tm.tm_mon];
1✔
1474
        char        buf[10] = {0};
1✔
1475
        for (int32_t i = 0; i < strlen(mon); ++i) buf[i] = tolower(mon[i]);
8✔
1476
        (void)sprintf(s, "%-9s", buf);
1✔
1477
        s += strlen(s);
1✔
1478
        break;
1✔
1479
      }
1480
      case TSFKW_Mon:
1✔
1481
        (void)sprintf(s, "%s", months[tm->tm.tm_mon]);
1✔
1482
        s += strlen(s);
1✔
1483
        break;
1✔
1484
      case TSFKW_mon: {
1✔
1485
        const char* mon = months[tm->tm.tm_mon];
1✔
1486
        char        buf[10] = {0};
1✔
1487
        for (int32_t i = 0; i < strlen(mon); ++i) buf[i] = tolower(mon[i]);
4✔
1488
        (void)sprintf(s, "%s", buf);
1✔
1489
        s += strlen(s);
1✔
1490
        break;
1✔
1491
      }
1492
      case TSFKW_SS:
13✔
1493
        (void)sprintf(s, "%02d", tm->tm.tm_sec);
13✔
1494
        s += 2;
13✔
1495
        break;
13✔
1496
      case TSFKW_MS:
6✔
1497
        (void)sprintf(s, "%03" PRId64, tm->fsec / 1000000L);
6✔
1498
        s += 3;
6✔
1499
        break;
6✔
1500
      case TSFKW_US:
6✔
1501
        (void)sprintf(s, "%06" PRId64, tm->fsec / 1000L);
6✔
1502
        s += 6;
6✔
1503
        break;
6✔
1504
      case TSFKW_NS:
9✔
1505
        (void)sprintf(s, "%09" PRId64, tm->fsec);
9✔
1506
        s += 9;
9✔
1507
        break;
9✔
1508
      case TSFKW_TZH:{
3✔
1509
#ifdef WINDOWS
1510
        int32_t gmtoff = -_timezone;
1511
#elif defined(TD_ASTRA)
1512
        int32_t gmtoff = -timezone;
1513
#else
1514
        int32_t gmtoff = tm->tm.tm_gmtoff;
3✔
1515
#endif
1516
        (void)sprintf(s, "%c%02d", (gmtoff >= 0) ? '+' : '-',
3✔
1517
                      abs(gmtoff) / 3600);
3✔
1518
        s += strlen(s);
3✔
1519
        break;
3✔
1520
      }
1521
      case TSFKW_YYYY:
15✔
1522
        (void)sprintf(s, "%04d", tm->tm.tm_year + 1900);
15✔
1523
        s += strlen(s);
15✔
1524
        break;
15✔
1525
      case TSFKW_YYY:
2✔
1526
        (void)sprintf(s, "%03d", (tm->tm.tm_year + 1900) % 1000);
2✔
1527
        s += strlen(s);
2✔
1528
        break;
2✔
1529
      case TSFKW_YY:
2✔
1530
        (void)sprintf(s, "%02d", (tm->tm.tm_year + 1900) % 100);
2✔
1531
        s += strlen(s);
2✔
1532
        break;
2✔
1533
      case TSFKW_Y:
2✔
1534
        (void)sprintf(s, "%01d", (tm->tm.tm_year + 1900) % 10);
2✔
1535
        s += strlen(s);
2✔
1536
        break;
2✔
1537
      default:
×
1538
        break;
×
1539
    }
1540
  }
1541
  return TSDB_CODE_SUCCESS;
18✔
1542
}
1543

1544
/// @brief find s in arr case insensitively
1545
/// @retval the index in arr if found, -1 if not found
1546
static int32_t strArrayCaseSearch(const char* const* arr, const char* s) {
18✔
1547
  if (!*s) return -1;
18✔
1548
  const char* const* fmt = arr;
18✔
1549
  for (; *fmt; ++fmt) {
57✔
1550
    const char *l, *r;
1551
    for (l = fmt[0], r = s;; l++, r++) {
135✔
1552
      if (*l == '\0') return fmt - arr;
135✔
1553
      if (*r == '\0' || tolower(*l) != tolower(*r)) break;
118✔
1554
    }
1555
  }
1556
  return -1;
1✔
1557
}
1558

1559
static const char* tsFormatStr2Int32(int32_t* dest, const char* str, int32_t len, bool needMoreDigit) {
198✔
1560
  char*       last;
1561
  int64_t     res;
1562
  const char* s = str;
198✔
1563
  if ('\0' == str[0]) return NULL;
198✔
1564
  if (len <= 0) {
198✔
1565
    res = taosStr2Int64(s, &last, 10);
16✔
1566
    s = last;
16✔
1567
  } else {
1568
    char buf[16] = {0};
182✔
1569
    (void)strncpy(buf, s, len);
182✔
1570
    int32_t copiedLen = strlen(buf);
182✔
1571
    if (copiedLen < len) {
182✔
1572
      if (!needMoreDigit) {
4✔
1573
        // digits not enough, that's ok, cause we do not need more digits
1574
        // '2023-1' 'YYYY-MM'
1575
        // '202a' 'YYYY' -> 202
1576
        res = taosStr2Int64(s, &last, 10);
3✔
1577
        s += copiedLen;
3✔
1578
      } else {
1579
        // bytes not enough, and there are other digit formats to match
1580
        // '2023-1' 'YYYY-MMDD'
1581
        return NULL;
3✔
1582
      }
1583
    } else {
1584
      if (needMoreDigit) {
178✔
1585
        res = taosStr2Int64(buf, &last, 10);
14✔
1586
        // bytes enough, but digits not enough, like '202a12' 'YYYYMM', YYYY needs four digits
1587
        if (last - buf < len) return NULL;
14✔
1588
        s += last - buf;
12✔
1589
      } else {
1590
        res = taosStr2Int64(s, &last, 10);
164✔
1591
        s = last;
164✔
1592
      }
1593
    }
1594
  }
1595
  if (s == str) {
195✔
1596
    // no integers found
1597
    return NULL;
1✔
1598
  }
1599
  if (ERRNO == ERANGE || res > INT32_MAX || res < INT32_MIN) {
194✔
1600
    // out of range
1601
    return NULL;
1✔
1602
  }
1603
  *dest = res;
193✔
1604
  return s;
193✔
1605
}
1606

1607
static int32_t adjustYearTo2020(int32_t year) {
12✔
1608
  if (year < 70) return year + 2000;    // 2000 - 2069
12✔
1609
  if (year < 100) return year + 1900;   // 1970 - 1999
3✔
1610
  if (year < 520) return year + 2000;   // 2100 - 2519
3✔
1611
  if (year < 1000) return year + 1000;  // 1520 - 1999
×
1612
  return year;
×
1613
}
1614

1615
static bool checkTm(const struct tm* tm) {
33✔
1616
  if (tm->tm_mon < 0 || tm->tm_mon > 11) return false;
33✔
1617
  if (tm->tm_wday < 0 || tm->tm_wday > 6) return false;
32✔
1618
  if (tm->tm_yday < 0 || tm->tm_yday > 365) return false;
32✔
1619
  if (tm->tm_mday < 0 || tm->tm_mday > 31) return false;
32✔
1620
  if (tm->tm_hour < 0 || tm->tm_hour > 23) return false;
31✔
1621
  if (tm->tm_min < 0 || tm->tm_min > 59) return false;
31✔
1622
  if (tm->tm_sec < 0 || tm->tm_sec > 60) return false;
31✔
1623
  return true;
31✔
1624
}
1625

1626
static bool needMoreDigits(SArray* formats, int32_t curIdx) {
198✔
1627
  if (curIdx == taosArrayGetSize(formats) - 1) return false;
198✔
1628
  TSFormatNode* pNextNode = taosArrayGet(formats, curIdx + 1);
175✔
1629
  if (pNextNode->type == TS_FORMAT_NODE_TYPE_SEPARATOR) {
175✔
1630
    return false;
109✔
1631
  } else if (pNextNode->type == TS_FORMAT_NODE_TYPE_KEYWORD) {
66✔
1632
    return pNextNode->key->isDigit;
30✔
1633
  } else {
1634
    return isdigit(pNextNode->c[0]);
36✔
1635
  }
1636
}
1637

1638
/// @brief convert a formatted time str to timestamp
1639
/// @param[in] s the formatted timestamp str
1640
/// @param[in] formats array of TSFormatNode, output of parseTsFormat
1641
/// @param[out] ts output timestamp
1642
/// @param precision the timestamp precision to convert to, sec/milli/micro/nano
1643
/// @param[out] sErrPos if not NULL, when err occured, points to the failed position of s, only set when ret is -1
1644
/// @param[out] fErrIdx if not NULL, when err occured, the idx of the failed format idx, only set when ret is -1
1645
/// @retval 0 for success
1646
/// @retval -1 for format and s mismatch error
1647
/// @retval -2 if datetime err, like 2023-13-32 25:61:69
1648
/// @retval -3 if not supported
1649
static int32_t char2ts(const char* s, SArray* formats, int64_t* ts, int32_t precision, const char** sErrPos,
41✔
1650
                       int32_t* fErrIdx, timezone_t tz) {
1651
  int32_t size = taosArrayGetSize(formats);
41✔
1652
  int32_t pm = 0;      // default am
41✔
1653
  int32_t hour12 = 0;  // default HH24
41✔
1654
  int32_t year = 0, mon = 0, yd = 0, md = 1, wd = 0;
41✔
1655
  int32_t hour = 0, min = 0, sec = 0, us = 0, ms = 0, ns = 0;
41✔
1656
  int32_t tzHour = 0;
41✔
1657
  int32_t err = 0;
41✔
1658
  bool    withYD = false, withMD = false;
41✔
1659

1660
  for (int32_t i = 0; i < size && *s != '\0'; ++i) {
406✔
1661
    while (isspace(*s) && *s != '\0') {
452✔
1662
      s++;
80✔
1663
    }
1664
    if (!s) break;
372✔
1665
    TSFormatNode* node = taosArrayGet(formats, i);
372✔
1666
    if (node->type == TS_FORMAT_NODE_TYPE_SEPARATOR) {
372✔
1667
      // separator matches any character
1668
      if (isSeperatorChar(s[0])) s += node->len;
116✔
1669
      continue;
116✔
1670
    }
1671
    if (node->type == TS_FORMAT_NODE_TYPE_CHAR) {
256✔
1672
      int32_t pos = 0;
40✔
1673
      // skip leading spaces
1674
      while (isspace(node->c[pos]) && node->len > 0) pos++;
71✔
1675
      while (pos < node->len && *s != '\0') {
86✔
1676
        if (!isspace(node->c[pos++])) {
46✔
1677
          while (isspace(*s) && *s != '\0') s++;
46✔
1678
          if (*s != '\0') s++;  // forward together
36✔
1679
        }
1680
      }
1681
      continue;
40✔
1682
    }
1683
    switch (node->key->id) {
216✔
1684
      case TSFKW_A_M:
3✔
1685
      case TSFKW_P_M:
1686
      case TSFKW_a_m:
1687
      case TSFKW_p_m: {
1688
        int32_t idx = strArrayCaseSearch(long_apms, s);
3✔
1689
        if (idx >= 0) {
3✔
1690
          s += 4;
3✔
1691
          pm = idx % 2;
3✔
1692
          hour12 = 1;
3✔
1693
        } else {
1694
          err = -1;
×
1695
        }
1696
      } break;
3✔
1697
      case TSFKW_AM:
2✔
1698
      case TSFKW_PM:
1699
      case TSFKW_am:
1700
      case TSFKW_pm: {
1701
        int32_t idx = strArrayCaseSearch(apms, s);
2✔
1702
        if (idx >= 0) {
2✔
1703
          s += 2;
2✔
1704
          pm = idx % 2;
2✔
1705
          hour12 = 1;
2✔
1706
        } else {
1707
          err = -1;
×
1708
        }
1709
      } break;
2✔
1710
      case TSFKW_HH:
6✔
1711
      case TSFKW_HH12: {
1712
        const char* newPos = tsFormatStr2Int32(&hour, s, 2, needMoreDigits(formats, i));
6✔
1713
        if (NULL == newPos || hour > 12 || hour <= 0) {
6✔
1714
          err = -1;
1✔
1715
        } else {
1716
          hour12 = 1;
5✔
1717
          s = newPos;
5✔
1718
        }
1719
      } break;
6✔
1720
      case TSFKW_HH24: {
13✔
1721
        const char* newPos = tsFormatStr2Int32(&hour, s, 2, needMoreDigits(formats, i));
13✔
1722
        if (NULL == newPos) {
13✔
1723
          err = -1;
×
1724
        } else {
1725
          hour12 = 0;
13✔
1726
          s = newPos;
13✔
1727
        }
1728
      } break;
13✔
1729
      case TSFKW_MI: {
18✔
1730
        const char* newPos = tsFormatStr2Int32(&min, s, 2, needMoreDigits(formats, i));
18✔
1731
        if (NULL == newPos) {
18✔
1732
          err = -1;
×
1733
        } else {
1734
          s = newPos;
18✔
1735
        }
1736
      } break;
18✔
1737
      case TSFKW_SS: {
18✔
1738
        const char* newPos = tsFormatStr2Int32(&sec, s, 2, needMoreDigits(formats, i));
18✔
1739
        if (NULL == newPos)
18✔
1740
          err = -1;
×
1741
        else
1742
          s = newPos;
18✔
1743
      } break;
18✔
1744
      case TSFKW_MS: {
7✔
1745
        const char* newPos = tsFormatStr2Int32(&ms, s, 3, needMoreDigits(formats, i));
7✔
1746
        if (NULL == newPos)
7✔
1747
          err = -1;
×
1748
        else {
1749
          int32_t len = newPos - s;
7✔
1750
          ms *= len == 1 ? 100 : len == 2 ? 10 : 1;
7✔
1751
          s = newPos;
7✔
1752
        }
1753
      } break;
7✔
1754
      case TSFKW_US: {
12✔
1755
        const char* newPos = tsFormatStr2Int32(&us, s, 6, needMoreDigits(formats, i));
12✔
1756
        if (NULL == newPos)
12✔
1757
          err = -1;
×
1758
        else {
1759
          int32_t len = newPos - s;
12✔
1760
          us *= len == 1 ? 100000 : len == 2 ? 10000 : len == 3 ? 1000 : len == 4 ? 100 : len == 5 ? 10 : 1;
12✔
1761
          s = newPos;
12✔
1762
        }
1763
      } break;
12✔
1764
      case TSFKW_NS: {
5✔
1765
        const char* newPos = tsFormatStr2Int32(&ns, s, 9, needMoreDigits(formats, i));
5✔
1766
        if (NULL == newPos)
5✔
1767
          err = -1;
×
1768
        else {
1769
          int32_t len = newPos - s;
5✔
1770
          ns *= len == 1   ? 100000000
5✔
1771
                : len == 2 ? 10000000
10✔
1772
                : len == 3 ? 1000000
10✔
1773
                : len == 4 ? 100000
10✔
1774
                : len == 5 ? 10000
10✔
1775
                : len == 6 ? 1000
10✔
1776
                : len == 7 ? 100
10✔
1777
                : len == 8 ? 10
10✔
1778
                           : 1;
5✔
1779
          s = newPos;
5✔
1780
        }
1781
      } break;
5✔
1782
      case TSFKW_TZH: {
16✔
1783
        const char* newPos = tsFormatStr2Int32(&tzHour, s, -1, needMoreDigits(formats, i));
16✔
1784
        if (NULL == newPos)
16✔
1785
          err = -1;
×
1786
        else {
1787
          s = newPos;
16✔
1788
        }
1789
      } break;
16✔
1790
      case TSFKW_MONTH:
4✔
1791
      case TSFKW_Month:
1792
      case TSFKW_month: {
1793
        int32_t idx = strArrayCaseSearch(fullMonths, s);
4✔
1794
        if (idx >= 0) {
4✔
1795
          s += strlen(fullMonths[idx]);
4✔
1796
          mon = idx;
4✔
1797
        } else {
1798
          err = -1;
×
1799
        }
1800
      } break;
4✔
1801
      case TSFKW_MON:
3✔
1802
      case TSFKW_Mon:
1803
      case TSFKW_mon: {
1804
        int32_t idx = strArrayCaseSearch(months, s);
3✔
1805
        if (idx >= 0) {
3✔
1806
          s += strlen(months[idx]);
2✔
1807
          mon = idx;
2✔
1808
        } else {
1809
          err = -1;
1✔
1810
        }
1811
      } break;
3✔
1812
      case TSFKW_MM: {
26✔
1813
        const char* newPos = tsFormatStr2Int32(&mon, s, 2, needMoreDigits(formats, i));
26✔
1814
        if (NULL == newPos) {
26✔
1815
          err = -1;
3✔
1816
        } else {
1817
          s = newPos;
23✔
1818
          mon -= 1;
23✔
1819
        }
1820
      } break;
26✔
1821
      case TSFKW_DAY:
3✔
1822
      case TSFKW_Day:
1823
      case TSFKW_day: {
1824
        int32_t idx = strArrayCaseSearch(weekDays, s);
3✔
1825
        if (idx >= 0) {
3✔
1826
          s += strlen(weekDays[idx]);
3✔
1827
          wd = idx;
3✔
1828
        } else {
1829
          err = -1;
×
1830
        }
1831
      } break;
3✔
1832
      case TSFKW_DY:
3✔
1833
      case TSFKW_Dy:
1834
      case TSFKW_dy: {
1835
        int32_t idx = strArrayCaseSearch(shortWeekDays, s);
3✔
1836
        if (idx >= 0) {
3✔
1837
          s += strlen(shortWeekDays[idx]);
3✔
1838
          wd = idx;
3✔
1839
        } else {
1840
          err = -1;
×
1841
        }
1842
      } break;
3✔
1843
      case TSFKW_DDD: {
2✔
1844
        const char* newPos = tsFormatStr2Int32(&yd, s, 3, needMoreDigits(formats, i));
2✔
1845
        if (NULL == newPos) {
2✔
1846
          err = -1;
×
1847
        } else {
1848
          s = newPos;
2✔
1849
        }
1850
        withYD = true;
2✔
1851
      } break;
2✔
1852
      case TSFKW_DD: {
28✔
1853
        const char* newPos = tsFormatStr2Int32(&md, s, 2, needMoreDigits(formats, i));
28✔
1854
        if (NULL == newPos) {
28✔
1855
          err = -1;
×
1856
        } else {
1857
          s = newPos;
28✔
1858
        }
1859
        withMD = true;
28✔
1860
      } break;
28✔
1861
      case TSFKW_D: {
1✔
1862
        const char* newPos = tsFormatStr2Int32(&wd, s, 1, needMoreDigits(formats, i));
1✔
1863
        if (NULL == newPos) {
1✔
1864
          err = -1;
×
1865
        } else {
1866
          s = newPos;
1✔
1867
        }
1868
      } break;
1✔
1869
      case TSFKW_YYYY: {
34✔
1870
        const char* newPos = tsFormatStr2Int32(&year, s, 4, needMoreDigits(formats, i));
34✔
1871
        if (NULL == newPos) {
34✔
1872
          err = -1;
2✔
1873
        } else {
1874
          s = newPos;
32✔
1875
        }
1876
      } break;
34✔
1877
      case TSFKW_YYY: {
3✔
1878
        const char* newPos = tsFormatStr2Int32(&year, s, 3, needMoreDigits(formats, i));
3✔
1879
        if (NULL == newPos) {
3✔
1880
          err = -1;
×
1881
        } else {
1882
          year = adjustYearTo2020(year);
3✔
1883
          s = newPos;
3✔
1884
        }
1885
      } break;
3✔
1886
      case TSFKW_YY: {
8✔
1887
        const char* newPos = tsFormatStr2Int32(&year, s, 2, needMoreDigits(formats, i));
8✔
1888
        if (NULL == newPos) {
8✔
1889
          err = -1;
×
1890
        } else {
1891
          year = adjustYearTo2020(year);
8✔
1892
          s = newPos;
8✔
1893
        }
1894
      } break;
8✔
1895
      case TSFKW_Y: {
1✔
1896
        const char* newPos = tsFormatStr2Int32(&year, s, 1, needMoreDigits(formats, i));
1✔
1897
        if (NULL == newPos) {
1✔
1898
          err = -1;
×
1899
        } else {
1900
          year = adjustYearTo2020(year);
1✔
1901
          s = newPos;
1✔
1902
        }
1903
      } break;
1✔
1904
      default:
×
1905
        break;
×
1906
    }
1907
    if (err) {
216✔
1908
      if (sErrPos) *sErrPos = s;
7✔
1909
      if (fErrIdx) *fErrIdx = i;
7✔
1910
      return err;
7✔
1911
    }
1912
  }
1913
  if (!withMD) {
34✔
1914
    // yyyy-mm-DDD, currently, the c api can't convert to correct timestamp, return not supported
1915
    if (withYD) return -3;
7✔
1916
  }
1917
  struct STm tm = {0};
33✔
1918
  tm.tm.tm_year = year - 1900;
33✔
1919
  tm.tm.tm_mon = mon;
33✔
1920
  tm.tm.tm_yday = yd;
33✔
1921
  tm.tm.tm_mday = md;
33✔
1922
  tm.tm.tm_wday = wd;
33✔
1923
  if (hour12) {
33✔
1924
    if (pm && hour < 12)
5✔
1925
      tm.tm.tm_hour = hour + 12;
3✔
1926
    else if (!pm && hour == 12)
2✔
1927
      tm.tm.tm_hour = 0;
2✔
1928
    else
1929
      tm.tm.tm_hour = hour;
×
1930
  } else {
1931
    tm.tm.tm_hour = hour;
28✔
1932
  }
1933
  tm.tm.tm_min = min;
33✔
1934
  tm.tm.tm_sec = sec;
33✔
1935
  if (!checkTm(&tm.tm)) return -2;
33✔
1936
  if (tzHour < -13 || tzHour > 13) return -2;
31✔
1937
  tm.fsec = ms * 1000000 + us * 1000 + ns;
31✔
1938
  int32_t ret = taosTm2Ts(&tm, ts, precision, tz);
31✔
1939
  if (tzHour != 0) {
31✔
1940
#ifdef WINDOWS
1941
    int32_t gmtoff = -_timezone;
1942
#elif defined(TD_ASTRA)
1943
    int32_t gmtoff = -timezone;
1944
#else
1945
    int32_t gmtoff = tm.tm.tm_gmtoff;
13✔
1946
#endif
1947
    *ts += (gmtoff - tzHour * 3600) * TICK_PER_SECOND[precision];
13✔
1948
  }
1949
  return ret;
31✔
1950
}
1951

1952
int32_t taosTs2Char(const char* format, SArray** formats, int64_t ts, int32_t precision, char* out, int32_t outLen, timezone_t tz) {
2✔
1953
  if (!*formats) {
2✔
1954
    *formats = taosArrayInit(8, sizeof(TSFormatNode));
2✔
1955
    if (!*formats) {
2✔
1956
      TAOS_RETURN(terrno);
×
1957
    }
1958
    TAOS_CHECK_RETURN(parseTsFormat(format, *formats));
2✔
1959
  }
1960
  struct STm tm;
1961
  TAOS_CHECK_RETURN(taosTs2Tm(ts, precision, &tm, tz));
2✔
1962
  return tm2char(*formats, &tm, out, outLen);
2✔
1963
}
1964

1965
int32_t taosChar2Ts(const char* format, SArray** formats, const char* tsStr, int64_t* ts, int32_t precision,
3✔
1966
                    char* errMsg, int32_t errMsgLen, timezone_t tz) {
1967
  const char* sErrPos;
1968
  int32_t     fErrIdx;
1969
  if (!*formats) {
3✔
1970
    *formats = taosArrayInit(4, sizeof(TSFormatNode));
3✔
1971
    if (!*formats) {
3✔
1972
      TAOS_RETURN(terrno);
×
1973
    }
1974
    TAOS_CHECK_RETURN(parseTsFormat(format, *formats));
3✔
1975
  }
1976
  int32_t code = char2ts(tsStr, *formats, ts, precision, &sErrPos, &fErrIdx, tz);
3✔
1977
  if (code == -1) {
3✔
1978
    TSFormatNode* fNode = (taosArrayGet(*formats, fErrIdx));
×
1979
    snprintf(errMsg, errMsgLen, "mismatch format for: %s and %s", sErrPos,
×
1980
             fErrIdx < taosArrayGetSize(*formats) ? ((TSFormatNode*)taosArrayGet(*formats, fErrIdx))->key->name : "");
×
1981
    code = TSDB_CODE_FUNC_TO_TIMESTAMP_FAILED_FORMAT_ERR;
×
1982
  } else if (code == -2) {
3✔
1983
    snprintf(errMsg, errMsgLen, "timestamp format error: %s -> %s", tsStr, format);
×
1984
    code = TSDB_CODE_FUNC_TO_TIMESTAMP_FAILED_TS_ERR;
×
1985
  } else if (code == -3) {
3✔
1986
    snprintf(errMsg, errMsgLen, "timestamp format not supported");
×
1987
    code = TSDB_CODE_FUNC_TO_TIMESTAMP_FAILED_NOT_SUPPORTED;
×
1988
  }
1989
  TAOS_RETURN(code);
3✔
1990
}
1991

1992
int32_t TEST_ts2char(const char* format, int64_t ts, int32_t precision, char* out, int32_t outLen) {
16✔
1993
  int32_t code = TSDB_CODE_SUCCESS;
16✔
1994

1995
  SArray* formats = taosArrayInit(4, sizeof(TSFormatNode));
16✔
1996
  if (!formats) {
16✔
1997
    TAOS_RETURN(terrno);
×
1998
  }
1999
  TAOS_CHECK_RETURN(parseTsFormat(format, formats));
16✔
2000
  struct STm tm;
2001
  TAOS_CHECK_GOTO(taosTs2Tm(ts, precision, &tm, NULL), NULL, _exit);
16✔
2002
  TAOS_CHECK_GOTO(tm2char(formats, &tm, out, outLen), NULL, _exit);
16✔
2003

2004
_exit:
16✔
2005
  taosArrayDestroy(formats);
16✔
2006
  TAOS_RETURN(TSDB_CODE_SUCCESS);
16✔
2007
}
2008

2009
int32_t TEST_char2ts(const char* format, int64_t* ts, int32_t precision, const char* tsStr) {
38✔
2010
  const char* sErrPos;
2011
  int32_t     fErrIdx;
2012
  SArray*     formats = taosArrayInit(4, sizeof(TSFormatNode));
38✔
2013
  TAOS_CHECK_RETURN(parseTsFormat(format, formats));
38✔
2014
  int32_t code = char2ts(tsStr, formats, ts, precision, &sErrPos, &fErrIdx, NULL);
38✔
2015
  if (code == -1) {
38✔
2016
    (void)printf("failed position: %s\n", sErrPos);
7✔
2017
    (void)printf("failed format: %s\n", ((TSFormatNode*)taosArrayGet(formats, fErrIdx))->key->name);
7✔
2018
  }
2019
  taosArrayDestroy(formats);
38✔
2020
  return code;
38✔
2021
}
2022

2023
static int8_t UNIT_INDEX[26] = {/*a*/ 2,  0,  -1, 6,  -1, -1, -1,
2024
                                /*h*/ 5,  -1, -1, -1, -1, 4,  8,
2025
                                /*o*/ -1, -1, -1, -1, 3,  -1,
2026
                                /*u*/ 1,  -1, 7,  -1, 9,  -1};
2027

2028
#define GET_UNIT_INDEX(idx) UNIT_INDEX[(idx)-97]
2029

2030
// clang-format off
2031
static int64_t UNIT_MATRIX[10][11] = {     /*  ns,   us,   ms,    s,   min,   h,   d,   w, month, y*/
2032
                                /*ns*/ {   1, 1000,    0},
2033
                                /*us*/ {1000,    1, 1000,    0},
2034
                                /*ms*/ {   0, 1000,    1, 1000,    0},
2035
                                 /*s*/ {   0,    0, 1000,    1,   60,    0},
2036
                               /*min*/ {   0,    0,    0,   60,    1,   60,   0},
2037
                                 /*h*/ {   0,    0,    0,    0,   60,    1,   1,   0},
2038
                                 /*d*/ {   0,    0,    0,    0,    0,   24,   1,   7,   1,   0},
2039
                                 /*w*/ {   0,    0,    0,    0,    0,    0,   7,   1,  -1,   0},
2040
                               /*mon*/ {   0,    0,    0,    0,    0,    0,   0,   0,   1,  12,  0},
2041
                                 /*y*/ {   0,    0,    0,    0,    0,    0,   0,   0,   12,   1,  0}};
2042
// clang-format on
2043

2044
static bool recursiveTsmaCheckRecursive(int64_t baseInterval, int8_t baseIdx, int64_t interval, int8_t idx,
88✔
2045
                                        bool checkEq) {
2046
  if (UNIT_MATRIX[baseIdx][idx] == -1) return false;
88✔
2047
  if (baseIdx == idx) {
83✔
2048
    if (interval < baseInterval) return false;
29✔
2049
    if (checkEq && interval == baseInterval) return false;
29✔
2050
    return interval % baseInterval == 0;
24✔
2051
  }
2052
  int8_t  next = baseIdx + 1;
54✔
2053
  int64_t val = UNIT_MATRIX[baseIdx][next];
54✔
2054
  while (val != 0 && next <= idx) {
64✔
2055
    if (val == -1) {
58✔
2056
      next++;
2✔
2057
      val = UNIT_MATRIX[baseIdx][next];
2✔
2058
      continue;
2✔
2059
    }
2060
    if (val % baseInterval == 0 || baseInterval % val == 0) {
56✔
2061
      int8_t extra = baseInterval >= val ? 0 : 1;
45✔
2062
      bool   needCheckEq = baseInterval >= val && !(baseIdx < next && val == 1);
45✔
2063
      if (!recursiveTsmaCheckRecursive(baseInterval / val + extra, next, interval, idx, needCheckEq && checkEq)) {
45✔
2064
        next++;
8✔
2065
        val = UNIT_MATRIX[baseIdx][next];
8✔
2066
        continue;
8✔
2067
      } else {
2068
        return true;
37✔
2069
      }
2070
    } else {
2071
      return false;
11✔
2072
    }
2073
  }
2074
  return false;
6✔
2075
}
2076

2077
static bool recursiveTsmaCheckRecursiveReverse(int64_t baseInterval, int8_t baseIdx, int64_t interval, int8_t idx,
35✔
2078
                                               bool checkEq) {
2079
  if (UNIT_MATRIX[baseIdx][idx] == -1) return false;
35✔
2080

2081
  if (baseIdx == idx) {
35✔
2082
    if (interval < baseInterval) return false;
13✔
2083
    if (checkEq && interval == baseInterval) return false;
11✔
2084
    return interval % baseInterval == 0;
8✔
2085
  }
2086

2087
  int8_t  next = baseIdx - 1;
22✔
2088
  int64_t val = UNIT_MATRIX[baseIdx][next];
22✔
2089
  while (val != 0 && next >= 0) {
22✔
2090
    return recursiveTsmaCheckRecursiveReverse(baseInterval * val, next, interval, idx, checkEq);
20✔
2091
  }
2092
  return false;
2✔
2093
}
2094

2095
/*
2096
 * @breif check if tsma with param [interval], [unit] can create based on base tsma with baseInterval and baseUnit
2097
 * @param baseInterval, baseUnit, interval/unit of base tsma
2098
 * @param interval the tsma interval going to create. Not that if unit is not calander unit, then interval has already
2099
 * been translated to TICKS of [precision]
2100
 * @param unit the tsma unit going to create
2101
 * @param precision the precision of this db
2102
 * @param checkEq pass true if same interval is not acceptable, false if acceptable.
2103
 * @ret true the tsma can be created, else cannot
2104
 * */
2105
bool checkRecursiveTsmaInterval(int64_t baseInterval, int8_t baseUnit, int64_t interval, int8_t unit, int8_t precision,
58✔
2106
                                bool checkEq) {
2107
  bool baseIsCalendarDuration = IS_CALENDAR_TIME_DURATION(baseUnit);
58✔
2108
  if (!baseIsCalendarDuration) {
58✔
2109
    if (TSDB_CODE_SUCCESS != convertTimeFromPrecisionToUnit(baseInterval, precision, baseUnit, &baseInterval)) {
40✔
2110
      return false;
×
2111
    }
2112
  }
2113
  bool isCalendarDuration = IS_CALENDAR_TIME_DURATION(unit);
58✔
2114
  if (!isCalendarDuration) {
58✔
2115
    if (TSDB_CODE_SUCCESS != convertTimeFromPrecisionToUnit(interval, precision, unit, &interval)) {
32✔
2116
      return false;
×
2117
    }
2118
  }
2119

2120
  bool needCheckEq = baseIsCalendarDuration == isCalendarDuration && checkEq;
58✔
2121

2122
  int8_t baseIdx = GET_UNIT_INDEX(baseUnit), idx = GET_UNIT_INDEX(unit);
58✔
2123
  if (baseIdx <= idx) {
58✔
2124
    return recursiveTsmaCheckRecursive(baseInterval, baseIdx, interval, idx, needCheckEq);
43✔
2125
  } else {
2126
    return recursiveTsmaCheckRecursiveReverse(baseInterval, baseIdx, interval, idx, checkEq);
15✔
2127
  }
2128
  return true;
2129
}
2130

2131
int64_t taosGetTimestampToday(int32_t precision, timezone_t tz) {
552✔
2132
  int64_t   factor =  (precision == TSDB_TIME_PRECISION_SECONDS) ? 1
552✔
2133
                    : (precision == TSDB_TIME_PRECISION_MILLI)   ? 1000
552✔
2134
                    : (precision == TSDB_TIME_PRECISION_MICRO)   ? 1000000
2135
                    : 1000000000;
2136
  time_t    t;
2137
  int32_t code = taosTime(&t);
552✔
2138
  if (code != 0) {
552✔
2139
    return -1;
×
2140
  }
2141
  struct tm tm;
2142
  if (taosLocalTime(&t, &tm, NULL, 0,  tz) == NULL){
552✔
2143
    uError("%s failed to get local time, code:%d", __FUNCTION__, ERRNO);
×
2144
    return t;
×
2145
  }
2146
  tm.tm_hour = 0;
552✔
2147
  tm.tm_min = 0;
552✔
2148
  tm.tm_sec = 0;
552✔
2149

2150
  time_t tmp = taosMktime(&tm, tz);
552✔
2151
  if (tmp == (time_t)-1) {
552✔
2152
    uError("%s failed to get timestamp of today, code:%d", __FUNCTION__, ERRNO);
×
2153
    return t;
×
2154
  }
2155
  return (int64_t)tmp * factor;
552✔
2156
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc