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

taosdata / TDengine / #4308

14 Jun 2025 02:06PM UTC coverage: 62.454% (-0.3%) from 62.777%
#4308

push

travis-ci

web-flow
fix: taosdump windows pthread_mutex_unlock crash(3.0) (#31357)

* fix: windows pthread_mutex_unlock crash

* enh: sync from main fix taosdump crash windows

* fix: restore .github action branch to main

153985 of 315105 branches covered (48.87%)

Branch coverage included in aggregate %.

238120 of 312727 relevant lines covered (76.14%)

6462519.65 hits per line

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

79.96
/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) {
2,404,506✔
36
  /* parse datatime string in with tz */
37
  if (strnchr(timestr, 'T', len, false) != NULL) {
2,404,506✔
38
    if (checkTzPresent(timestr, len)) {
516,880✔
39
      return parseTimeWithTz(timestr, utime, timePrec, 'T');
281,523✔
40
    } else {
41
      return parseLocaltimeDst((char*)timestr, len, utime, timePrec, 'T', tz);
235,529✔
42
    }
43
  } else {
44
    if (checkTzPresent(timestr, len)) {
1,885,601✔
45
      return parseTimeWithTz(timestr, utime, timePrec, 0);
117✔
46
    } else {
47
      return parseLocaltimeDst((char*)timestr, len, utime, timePrec, 0, tz);
1,887,424✔
48
    }
49
  }
50
}
51

52
bool checkTzPresent(const char* str, int32_t len) {
2,402,641✔
53
  char*   seg = forwardToTimeStringEnd((char*)str);
2,402,641✔
54
  int32_t seg_len = len - (int32_t)(seg - str);
2,403,598✔
55

56
  char* c = &seg[seg_len - 1];
2,403,598✔
57
  for (int32_t i = 0; i < seg_len; ++i) {
7,105,333✔
58
    if (*c == 'Z' || *c == 'z' || *c == '+' || *c == '-') {
4,983,375!
59
      return true;
281,640✔
60
    }
61
    c--;
4,701,735✔
62
  }
63

64
  return false;
2,121,958✔
65
}
66

67
char* forwardToTimeStringEnd(char* str) {
2,677,796✔
68
  int32_t i = 0;
2,677,796✔
69
  int32_t numOfSep = 0;
2,677,796✔
70

71
  while (str[i] != 0 && numOfSep < 2) {
54,660,927✔
72
    if (str[i++] == ':') {
51,983,131✔
73
      numOfSep++;
3,455,168✔
74
    }
75
  }
76

77
  while (str[i] >= '0' && str[i] <= '9') {
6,132,818✔
78
    i++;
3,455,022✔
79
  }
80

81
  return &str[i];
2,677,796✔
82
}
83

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

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

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

95
  while (str[i] >= '0' && str[i] <= '9') {
4,642,071✔
96
    i++;
3,461,321✔
97
  }
98

99
  int32_t totalLen = i;
1,180,750✔
100
  if (totalLen <= 0) {
1,180,750!
101
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
102
  }
103

104
  /* parse the fraction */
105
  if (timePrec == TSDB_TIME_PRECISION_MILLI) {
1,180,750✔
106
    /* only use the initial 3 bits */
107
    if (i >= MILLI_SEC_FRACTION_LEN) {
913,235✔
108
      i = MILLI_SEC_FRACTION_LEN;
813,868✔
109
    }
110

111
    times = MILLI_SEC_FRACTION_LEN - i;
913,235✔
112
  } else if (timePrec == TSDB_TIME_PRECISION_MICRO) {
267,515✔
113
    if (i >= MICRO_SEC_FRACTION_LEN) {
180✔
114
      i = MICRO_SEC_FRACTION_LEN;
109✔
115
    }
116
    times = MICRO_SEC_FRACTION_LEN - i;
180✔
117
  } else if (timePrec == TSDB_TIME_PRECISION_NANO) {
267,335!
118
    if (i >= NANO_SEC_FRACTION_LEN) {
267,335✔
119
      i = NANO_SEC_FRACTION_LEN;
53✔
120
    }
121
    times = NANO_SEC_FRACTION_LEN - i;
267,335✔
122
  } else {
123
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
124
  }
125

126
  fraction = strnatoi(str, i) * factor[times];
1,180,750✔
127
  *end = str + totalLen;
1,180,749✔
128
  *pFraction = fraction;
1,180,749✔
129

130
  TAOS_RETURN(TSDB_CODE_SUCCESS);
1,180,749✔
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) {
754,608✔
140
  int64_t hour = 0;
754,608✔
141
  int64_t minute = 0;
754,608✔
142

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

148
  i++;
754,608✔
149

150
  int32_t j = i;
754,608✔
151
  while (str[j]) {
3,779,637✔
152
    if ((str[j] >= '0' && str[j] <= '9') || str[j] == ':') {
3,025,041!
153
      ++j;
3,025,029✔
154
      continue;
3,025,029✔
155
    }
156

157
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
12✔
158
  }
159

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

165
    i += hourSize + 1;
7,218✔
166
    size_t minSize = strlen(&str[i]);
7,218✔
167
    PARSE(&str[i], minSize, minute);
7,218✔
168
  } else {
169
    size_t hourSize = strlen(&str[i]);
747,369✔
170
    if (hourSize > 2){
747,369✔
171
      hourSize = 2;
747,239✔
172
    }
173
    PARSE(&str[i], hourSize, hour)
747,369✔
174
    i += hourSize;
747,267✔
175
    size_t minSize = strlen(&str[i]);
747,267✔
176
    if (minSize > 0){
747,267✔
177
      PARSE(&str[i], minSize, minute);
747,242✔
178
    }
179
  }
180

181
  if (hour > 13 || hour < 0) {
754,407!
182
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
3✔
183
  }
184
  if (minute > 59 || minute < 0) {
754,404!
185
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
186
  }
187

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

194
  TAOS_RETURN(TSDB_CODE_SUCCESS);
754,440✔
195
}
196

197
int32_t offsetOfTimezone(char* tzStr, int64_t* offset) {
480,111✔
198
  if (tzStr && (tzStr[0] == 'z' || tzStr[0] == 'Z')) {
480,111!
199
    *offset = 0;
10✔
200
    return TSDB_CODE_SUCCESS;
10✔
201
  }
202
  return parseTimezone(tzStr, offset);
480,101✔
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) {
281,640✔
218
  int64_t factor = TSDB_TICK_PER_SECOND(timePrec);
281,640✔
219
  int64_t tzOffset = 0;
281,640✔
220

221
  struct tm tm = {0};
281,640✔
222

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

232
  if (str == NULL) {
281,640✔
233
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
6,412✔
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);
275,228✔
242
  if (seconds == -1){
275,228!
243
    TAOS_RETURN(TAOS_SYSTEM_ERROR(ERRNO));
×
244
  }
245
#endif
246

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

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

257
    *time = seconds * factor + fraction;
267,349✔
258

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

268
      *time += tzOffset * factor;
267,338✔
269
    }
270

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

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

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

282
  TAOS_RETURN(TSDB_CODE_SUCCESS);
275,195✔
283
}
284

285
static FORCE_INLINE bool validateTm(struct tm* pTm) {
286
  if (pTm == NULL) {
1,170,904!
287
    return false;
×
288
  }
289

290
  int32_t dayOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
1,170,904✔
291

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

296
  if (isLeapYear && (pTm->tm_mon == 1)) {
1,170,904✔
297
    if (pTm->tm_mday > leapYearMonthDay) {
6,707!
298
      return false;
16✔
299
    }
300
  } else {
301
    if (pTm->tm_mday > dayOfMonth[pTm->tm_mon]) {
1,164,197!
302
      return false;
×
303
    }
304
  }
305

306
  return true;
1,170,888✔
307
}
308

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

314
  char* str;
315
  if (delim == 'T') {
2,122,319✔
316
    str = taosStrpTime(timestr, "%Y-%m-%dT%H:%M:%S", &tm);
235,540✔
317
  } else if (delim == 0) {
1,886,779!
318
    str = taosStrpTime(timestr, "%Y-%m-%d %H:%M:%S", &tm);
1,887,489✔
319
  } else {
320
    str = NULL;
×
321
  }
322

323
  if (str == NULL || (((str - timestr) < len) && (*str != '.')) || !validateTm(&tm)) {
3,292,873!
324
    // if parse failed, try "%Y-%m-%d" format
325
    str = taosStrpTime(timestr, "%Y-%m-%d", &tm);
951,635✔
326
    if (str == NULL || (((str - timestr) < len) && (*str != '.')) || !validateTm(&tm)) {
951,932!
327
      TAOS_RETURN(TSDB_CODE_INVALID_PARA);
951,378✔
328
    }
329
  }
330

331
  int64_t seconds = taosMktime(&tm, tz);
1,170,888✔
332

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

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

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

355
int64_t convertTimePrecision(int64_t utime, int32_t fromPrecision, int32_t toPrecision) {
10,566,526✔
356
  switch (fromPrecision) {
10,566,526!
357
    case TSDB_TIME_PRECISION_MILLI: {
10,558,450✔
358
      switch (toPrecision) {
10,558,450!
359
        case TSDB_TIME_PRECISION_MILLI:
10,550,500✔
360
          return utime;
10,550,500✔
361
        case TSDB_TIME_PRECISION_MICRO:
2,680✔
362
          if (utime > INT64_MAX / 1000) {
2,680!
363
            return INT64_MAX;
×
364
          }
365
          return utime * 1000;
2,680✔
366
        case TSDB_TIME_PRECISION_NANO:
5,276✔
367
          if (utime > INT64_MAX / 1000000) {
5,276!
368
            return INT64_MAX;
×
369
          }
370
          return utime * 1000000;
5,276✔
371
        default:
×
372
          return utime;
×
373
      }
374
    }  // end from milli
375
    case TSDB_TIME_PRECISION_MICRO: {
2,046✔
376
      switch (toPrecision) {
2,046!
377
        case TSDB_TIME_PRECISION_MILLI:
1,200✔
378
          return utime / 1000;
1,200✔
379
        case TSDB_TIME_PRECISION_MICRO:
427✔
380
          return utime;
427✔
381
        case TSDB_TIME_PRECISION_NANO:
419✔
382
          if (utime > INT64_MAX / 1000) {
419!
383
            return INT64_MAX;
×
384
          }
385
          return utime * 1000;
419✔
386
        default:
×
387
          return utime;
×
388
      }
389
    }  // end from micro
390
    case TSDB_TIME_PRECISION_NANO: {
6,037✔
391
      switch (toPrecision) {
6,037!
392
        case TSDB_TIME_PRECISION_MILLI:
3,674✔
393
          return utime / 1000000;
3,674✔
394
        case TSDB_TIME_PRECISION_MICRO:
147✔
395
          return utime / 1000;
147✔
396
        case TSDB_TIME_PRECISION_NANO:
2,216✔
397
          return utime;
2,216✔
398
        default:
×
399
          return utime;
×
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) {
30,132✔
420
  if (fromPrecision != TSDB_TIME_PRECISION_MILLI && fromPrecision != TSDB_TIME_PRECISION_MICRO &&
30,132!
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};
30,132✔
426
  double  tmp = time;
30,132✔
427
  switch (toUnit) {
30,132!
428
    case 's': {
8,574✔
429
      time /= (NANOSECOND_PER_SEC / factors[fromPrecision]);
8,574✔
430
      tmp = (double)time;
8,574✔
431
      break;
8,574✔
432
    }
433
    case 'm':
7,284✔
434
      time /= (NANOSECOND_PER_MINUTE / factors[fromPrecision]);
7,284✔
435
      tmp = (double)time;
7,284✔
436
      break;
7,284✔
437
    case 'h':
3,286✔
438
      time /= (NANOSECOND_PER_HOUR / factors[fromPrecision]);
3,286✔
439
      tmp = (double)time;
3,286✔
440
      break;
3,286✔
441
    case 'd':
2,757✔
442
      time /= (NANOSECOND_PER_DAY / factors[fromPrecision]);
2,757✔
443
      tmp = (double)time;
2,757✔
444
      break;
2,757✔
445
    case 'w':
308✔
446
      time /= (NANOSECOND_PER_WEEK / factors[fromPrecision]);
308✔
447
      tmp = (double)time;
308✔
448
      break;
308✔
449
    case 'a':
7,825✔
450
      time /= (NANOSECOND_PER_MSEC / factors[fromPrecision]);
7,825✔
451
      tmp = (double)time;
7,825✔
452
      break;
7,825✔
453
    case 'u':
73!
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: {
48✔
462
          time /= 1;
48✔
463
          tmp = (double)time;
48✔
464
          break;
48✔
465
        }
466
        case TSDB_TIME_PRECISION_NANO: {
24✔
467
          time /= 1000;
24✔
468
          tmp = (double)time;
24✔
469
          break;
24✔
470
        }
471
      }
472
      break;
73✔
473
    case 'b':
25✔
474
      tmp *= factors[fromPrecision];
25✔
475
      time *= factors[fromPrecision];
25✔
476
      break;
25✔
477
    default: {
×
478
      TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
479
    }
480
  }
481
  if (tmp >= (double)INT64_MAX) {
30,132!
482
    *pRes = INT64_MAX;
×
483
  } else if (tmp <= (double)INT64_MIN) {
30,132!
484
    *pRes = INT64_MIN;
×
485
  } else {
486
    *pRes = time;
30,132✔
487
  }
488

489
  TAOS_RETURN(TSDB_CODE_SUCCESS);
30,132✔
490
}
491

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

530
int32_t getDuration(int64_t val, char unit, int64_t* result, int32_t timePrecision) {
10,533,977✔
531
  switch (unit) {
10,533,977!
532
    case 's':
10,409,706✔
533
      if (val > INT64_MAX / MILLISECOND_PER_SECOND) {
10,409,706!
534
        TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
535
      }
536
      (*result) = convertTimePrecision(val * MILLISECOND_PER_SECOND, TSDB_TIME_PRECISION_MILLI, timePrecision);
10,409,706✔
537
      break;
10,409,709✔
538
    case 'm':
30,602✔
539
      if (val > INT64_MAX / MILLISECOND_PER_MINUTE) {
30,602!
540
        TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
541
      }
542
      (*result) = convertTimePrecision(val * MILLISECOND_PER_MINUTE, TSDB_TIME_PRECISION_MILLI, timePrecision);
30,602✔
543
      break;
30,602✔
544
    case 'h':
23,792✔
545
      if (val > INT64_MAX / MILLISECOND_PER_MINUTE) {
23,792!
546
        TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
547
      }
548
      (*result) = convertTimePrecision(val * MILLISECOND_PER_HOUR, TSDB_TIME_PRECISION_MILLI, timePrecision);
23,792✔
549
      break;
23,793✔
550
    case 'd':
24,953✔
551
      if (val > INT64_MAX / MILLISECOND_PER_DAY) {
24,953!
552
        TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
553
      }
554
      (*result) = convertTimePrecision(val * MILLISECOND_PER_DAY, TSDB_TIME_PRECISION_MILLI, timePrecision);
24,953✔
555
      break;
24,953✔
556
    case 'w':
3,436✔
557
      if (val > INT64_MAX / MILLISECOND_PER_WEEK) {
3,436!
558
        TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
559
      }
560
      (*result) = convertTimePrecision(val * MILLISECOND_PER_WEEK, TSDB_TIME_PRECISION_MILLI, timePrecision);
3,436✔
561
      break;
3,436✔
562
    case 'a':
39,067✔
563
      (*result) = convertTimePrecision(val, TSDB_TIME_PRECISION_MILLI, timePrecision);
39,067✔
564
      break;
39,067✔
565
    case 'u':
1,211✔
566
      (*result) = convertTimePrecision(val, TSDB_TIME_PRECISION_MICRO, timePrecision);
1,211✔
567
      break;
1,211✔
568
    case 'b':
1,210✔
569
      (*result) = convertTimePrecision(val, TSDB_TIME_PRECISION_NANO, timePrecision);
1,210✔
570
      break;
1,210✔
571
    default: {
×
572
      TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
573
    }
574
  }
575
  TAOS_RETURN(TSDB_CODE_SUCCESS);
10,533,981✔
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,
10,373,484✔
593
                              int32_t timePrecision) {
594
  SET_ERRNO(0);
10,373,484✔
595
  char* endPtr = NULL;
10,373,484✔
596

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

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

609
  return getDuration(timestamp, *unit, duration, timePrecision);
10,373,411✔
610
}
611

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

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

622
  *unit = token[tokenLen - 1];
162,534✔
623
  if (*unit == 'n' || *unit == 'y') {
162,534✔
624
    TAOS_RETURN(TSDB_CODE_SUCCESS);
2,035✔
625
  }
626
  if (isdigit(*unit)) {
160,499✔
627
    *unit = getPrecisionUnit(timePrecision);
44✔
628
  }
629

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

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

635
int64_t taosTimeAdd(int64_t t, int64_t duration, char unit, int32_t precision, timezone_t tz) {
499,793,303✔
636
  if (duration == 0) {
499,793,303✔
637
    return t;
217,037,583✔
638
  }
639

640
  if (!IS_CALENDAR_TIME_DURATION(unit)) {
282,755,720!
641
    return t + duration;
287,780,446✔
642
  }
643

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

648
  struct tm  tm;
649
  time_t     tt = (time_t)(t / TSDB_TICK_PER_SECOND(precision));
×
650
  if(taosLocalTime(&tt, &tm, NULL, 0, tz) == NULL) {
×
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;
48,868✔
655
  tm.tm_year = mon / 12;
48,868✔
656
  tm.tm_mon = mon % 12;
48,868✔
657
  int daysOfMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
48,868✔
658
  if (taosIsLeapYear(1900 + tm.tm_year)) {
48,868✔
659
    daysOfMonth[1] = 29;
14,647✔
660
  }
661
  if (tm.tm_mday > daysOfMonth[tm.tm_mon]) {
48,868✔
662
    tm.tm_mday = daysOfMonth[tm.tm_mon];
3✔
663
  }
664

665
  tt = taosMktime(&tm, tz);
48,868✔
666
  if (tt == -1){
48,869!
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);
48,869✔
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,
3,463✔
703
                                     int32_t order) {
704
  if (ekey < skey) {
3,463✔
705
    int64_t tmp = ekey;
2,559✔
706
    ekey = skey;
2,559✔
707
    skey = tmp;
2,559✔
708
  }
709
  int32_t ret = 0;
3,463✔
710

711
  if (unit != 'n' && unit != 'y') {
3,463!
712
    ret = (int32_t)((ekey - skey) / interval);
3,463✔
713
    if (order == TSDB_ORDER_DESC && ret * interval < (ekey - skey)) ret += 1;
3,463!
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;
3,463✔
740
}
741

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

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

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

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

773
    tt = taosMktime(&tm, pInterval->timezone);
4,851✔
774
    if (tt == -1){
4,851!
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));
4,851✔
779
  } else {
780
    if (IS_CALENDAR_TIME_DURATION(pInterval->intervalUnit)) {
1,683,010!
781
      int64_t news = (ts / pInterval->sliding) * pInterval->sliding;
497✔
782
      if (pInterval->slidingUnit == 'd' || pInterval->slidingUnit == 'w') {
497!
783
#if defined(WINDOWS) && _MSC_VER >= 1900
784
        int64_t timezone = _timezone;
785
#endif
786
        news += (int64_t)(timezone * TSDB_TICK_PER_SECOND(precision));
570!
787
      }
788

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

794
        if (newe < ts) {  // move towards the greater endpoint
570!
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) {
17,088✔
803
            prev = news;
16,518✔
804
            news -= pInterval->sliding;
16,518✔
805
            newe = taosTimeAdd(news, pInterval->interval, pInterval->intervalUnit, precision, pInterval->timezone) - 1;
16,518✔
806
          }
807
        }
808

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

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

817
      if (pInterval->intervalUnit == 'd' || pInterval->intervalUnit == 'w') {
1,682,016✔
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));
41,907!
830
      }
831

832
      int64_t end = 0;
1,682,016✔
833

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

840
          if (start < 0 || INT64_MAX - start > pInterval->interval - 1) {
2,926!
841
            end = start + pInterval->interval - 1;
2,926✔
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,687,612✔
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;
1,412✔
856
    int64_t slidingStart = start;
1,412✔
857
    while (newe >= ts) {
3,279✔
858
      start = slidingStart;
1,867✔
859
      slidingStart = taosTimeAdd(slidingStart, -pInterval->sliding, pInterval->slidingUnit, precision, pInterval->timezone);
1,867✔
860
      int64_t news = taosTimeAdd(slidingStart, pInterval->offset, pInterval->offsetUnit, precision, pInterval->timezone);
1,867✔
861
      newe = taosTimeAdd(news, pInterval->interval, pInterval->intervalUnit, precision, pInterval->timezone) - 1;
1,867✔
862
    }
863
    start = taosTimeAdd(start, pInterval->offset, pInterval->offsetUnit, precision, pInterval->timezone);
1,412✔
864
  }
865

866
  return start;
1,687,530✔
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,654,417✔
871
  return taosTimeAdd(intervalStart, pInterval->interval, pInterval->intervalUnit, pInterval->precision, pInterval->timezone) - 1;
1,654,417✔
872
}
873

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

879
  interval->offset = 0;
134✔
880

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

885
  TSKEY skey = interval->timeRange.skey;
134✔
886
  TSKEY start = taosTimeTruncate(skey, interval);
134✔
887
  TSKEY news = start;
134✔
888
  while (news <= skey) {
318✔
889
    start = news;
184✔
890
    news = taosTimeAdd(start, interval->sliding, interval->slidingUnit, interval->precision, interval->timezone);
184✔
891
    if (news < start) {
184!
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;
134✔
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) {
8,481✔
954
  char      ts[40] = {0};
8,481✔
955
  struct tm ptm;
956

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

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

971
    case TSDB_TIME_PRECISION_MICRO: {
121✔
972
      quot = t / 1000000;
121✔
973
      fractionLen = 8;
121✔
974
      format = ".%06" PRId64;
121✔
975
      mod = t % 1000000;
121✔
976
      break;
121✔
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)) {
8,481!
993
    TAOS_RETURN(TAOS_SYSTEM_ERROR(ERRNO));
×
994
  }
995
  int32_t length = (int32_t)taosStrfTime(ts, 40, "%Y-%m-%dT%H:%M:%S", &ptm);
8,481✔
996
  length += tsnprintf(ts + length, fractionLen, format, mod);
8,481✔
997
  length += (int32_t)taosStrfTime(ts + length, 40 - length, "%z", &ptm);
8,481✔
998

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

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

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

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

1028
  return buf;
416,049✔
1029
}
1030

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

1040
int32_t taosTm2Ts(struct STm* tm, int64_t* ts, int32_t precision, timezone_t tz) {
721,557✔
1041
  *ts = taosMktime(&tm->tm, tz);
721,557✔
1042
  *ts *= TICK_PER_SECOND[precision];
721,558✔
1043
  *ts += tm->fsec / (TICK_PER_SECOND[TSDB_TIME_PRECISION_NANO] / TICK_PER_SECOND[precision]);
721,558✔
1044
  return TSDB_CODE_SUCCESS;
721,558✔
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) {
1,943,353✔
1253
  if (*str < 'A' || *str > 'z' || (*str > 'Z' && *str < 'a')) return NULL;
1,943,353!
1254
  int32_t idx = TSFormatKeywordIndex[str[0] - 'A'];
792,289✔
1255
  if (idx < 0) return NULL;
792,289!
1256
  const TSFormatKeyWord* key = &formatKeyWords[idx++];
792,289✔
1257
  while (key->name && str[0] == key->name[0]) {
1,246,835!
1258
    if (0 == strncmp(key->name, str, key->len)) {
1,246,564✔
1259
      return key;
792,018✔
1260
    }
1261
    key = &formatKeyWords[idx++];
454,546✔
1262
  }
1263
  return NULL;
271✔
1264
}
1265

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

1270
static int32_t parseTsFormat(const char* formatStr, SArray* formats) {
131,169✔
1271
  TSFormatNode* lastOtherFormat = NULL;
131,169✔
1272
  while (*formatStr) {
2,074,642✔
1273
    const TSFormatKeyWord* key = keywordSearch(formatStr);
1,943,292✔
1274
    if (key) {
1,943,369✔
1275
      TSFormatNode format = {.key = key, .type = TS_FORMAT_NODE_TYPE_KEYWORD};
791,960✔
1276
      if (NULL == taosArrayPush(formats, &format)) TAOS_RETURN(terrno);
792,022!
1277
      formatStr += key->len;
792,022✔
1278
      lastOtherFormat = NULL;
792,022✔
1279
    } else {
1280
      if (*formatStr == '"') {
1,151,409✔
1281
        lastOtherFormat = NULL;
18✔
1282
        // for double quoted string
1283
        formatStr++;
18✔
1284
        TSFormatNode* last = NULL;
18✔
1285
        while (*formatStr) {
140✔
1286
          if (*formatStr == '"') {
139✔
1287
            formatStr++;
17✔
1288
            break;
17✔
1289
          }
1290
          if (*formatStr == '\\' && *(formatStr + 1)) {
122!
1291
            formatStr++;
×
1292
            last = NULL;  // stop expanding last format, create new format
×
1293
          }
1294
          if (last) {
122✔
1295
            // expand
1296
            last->len++;
104✔
1297
            formatStr++;
104✔
1298
          } else {
1299
            // create new
1300
            TSFormatNode format = {.type = TS_FORMAT_NODE_TYPE_CHAR, .key = NULL};
18✔
1301
            format.c = formatStr;
18✔
1302
            format.len = 1;
18✔
1303
            if (NULL == taosArrayPush(formats, &format)) TAOS_RETURN(terrno);
18!
1304
            formatStr++;
18✔
1305
            last = taosArrayGetLast(formats);
18✔
1306
          }
1307
        }
1308
      } else {
1309
        // for other strings
1310
        if (*formatStr == '\\' && *(formatStr + 1)) {
1,151,391!
1311
          formatStr++;
3✔
1312
          lastOtherFormat = NULL;  // stop expanding
3✔
1313
        } else {
1314
          if (lastOtherFormat && !isSeperatorChar(*formatStr)) {
1,151,388✔
1315
            // expanding
1316
          } else {
1317
            // create new
1318
            lastOtherFormat = NULL;
660,383✔
1319
          }
1320
        }
1321
        if (lastOtherFormat) {
1,151,395✔
1322
          lastOtherFormat->len++;
491,015✔
1323
          formatStr++;
491,015✔
1324
        } else {
1325
          TSFormatNode format = {
660,417✔
1326
              .type = isSeperatorChar(*formatStr) ? TS_FORMAT_NODE_TYPE_SEPARATOR : TS_FORMAT_NODE_TYPE_CHAR,
660,380✔
1327
              .key = NULL};
1328
          format.c = formatStr;
660,417✔
1329
          format.len = 1;
660,417✔
1330
          if (NULL == taosArrayPush(formats, &format)) TAOS_RETURN(terrno);
660,420!
1331
          formatStr++;
660,420✔
1332
          if (format.type == TS_FORMAT_NODE_TYPE_CHAR) lastOtherFormat = taosArrayGetLast(formats);
660,420✔
1333
        }
1334
      }
1335
    }
1336
  }
1337
  TAOS_RETURN(TSDB_CODE_SUCCESS);
131,350✔
1338
}
1339

1340
static int32_t tm2char(const SArray* formats, const struct STm* tm, char* s, int32_t outLen) {
811,837✔
1341
  int32_t     size = taosArrayGetSize(formats);
811,837✔
1342
  const char* start = s;
811,094✔
1343
  for (int32_t i = 0; i < size; ++i) {
9,875,225✔
1344
    TSFormatNode* format = taosArrayGet(formats, i);
9,148,854✔
1345
    if (format->type != TS_FORMAT_NODE_TYPE_KEYWORD) {
9,064,131✔
1346
      if (s - start + format->len + 1 > outLen) break;
4,656,071!
1347
      (void)strncpy(s, format->c, format->len);
4,656,071✔
1348
      s += format->len;
4,656,071✔
1349
      continue;
4,656,071✔
1350
    }
1351
    if (s - start + 16 > outLen) break;
4,408,060!
1352

1353
    switch (format->key->id) {
4,408,060!
1354
      case TSFKW_AM:
16✔
1355
      case TSFKW_PM:
1356
        (void)sprintf(s, tm->tm.tm_hour % 24 >= 12 ? "PM" : "AM");
16✔
1357
        s += 2;
16✔
1358
        break;
16✔
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:
13✔
1365
      case TSFKW_pm:
1366
        (void)sprintf(s, tm->tm.tm_hour % 24 >= 12 ? "pm" : "am");
13✔
1367
        s += 2;
13✔
1368
        break;
13✔
1369
      case TSFKW_a_m:
460✔
1370
      case TSFKW_p_m:
1371
        (void)sprintf(s, tm->tm.tm_hour % 24 >= 12 ? "p.m." : "a.m.");
460✔
1372
        s += 4;
460✔
1373
        break;
460✔
1374
      case TSFKW_DDD:
866✔
1375
#ifdef WINDOWS
1376
        return TSDB_CODE_FUNC_TO_CHAR_NOT_SUPPORTED;
1377
#endif
1378
        (void)sprintf(s, "%03d", tm->tm.tm_yday + 1);
866✔
1379
        s += strlen(s);
866✔
1380
        break;
866✔
1381
      case TSFKW_DD:
805,577✔
1382
        (void)sprintf(s, "%02d", tm->tm.tm_mday);
805,577✔
1383
        s += 2;
805,577✔
1384
        break;
805,577✔
1385
      case TSFKW_D:
754✔
1386
        (void)sprintf(s, "%d", tm->tm.tm_wday + 1);
754✔
1387
        s += 1;
754✔
1388
        break;
754✔
1389
      case TSFKW_DAY: {
34✔
1390
        // MONDAY, TUESDAY...
1391
        const char* wd = weekDays[tm->tm.tm_wday];
34✔
1392
        char        buf[10] = {0};
34✔
1393
        for (int32_t i = 0; i < strlen(wd); ++i) buf[i] = toupper(wd[i]);
238✔
1394
        (void)sprintf(s, "%-9s", buf);
34✔
1395
        s += strlen(s);
34✔
1396
        break;
34✔
1397
      }
1398
      case TSFKW_Day:
20✔
1399
        // Monday, TuesDay...
1400
        (void)sprintf(s, "%-9s", weekDays[tm->tm.tm_wday]);
20✔
1401
        s += strlen(s);
20✔
1402
        break;
20✔
1403
      case TSFKW_day: {
33✔
1404
        const char* wd = weekDays[tm->tm.tm_wday];
33✔
1405
        char        buf[10] = {0};
33✔
1406
        for (int32_t i = 0; i < strlen(wd); ++i) buf[i] = tolower(wd[i]);
231✔
1407
        (void)sprintf(s, "%-9s", buf);
33✔
1408
        s += strlen(s);
33✔
1409
        break;
33✔
1410
      }
1411
      case TSFKW_DY: {
656✔
1412
        // MON, TUE
1413
        const char* wd = shortWeekDays[tm->tm.tm_wday];
656✔
1414
        char        buf[8] = {0};
656✔
1415
        for (int32_t i = 0; i < strlen(wd); ++i) buf[i] = toupper(wd[i]);
2,624✔
1416
        (void)sprintf(s, "%3s", buf);
656✔
1417
        s += 3;
656✔
1418
        break;
656✔
1419
      }
1420
      case TSFKW_Dy:
32✔
1421
        // Mon, Tue
1422
        (void)sprintf(s, "%3s", shortWeekDays[tm->tm.tm_wday]);
32✔
1423
        s += 3;
32✔
1424
        break;
32✔
1425
      case TSFKW_dy: {
393,327✔
1426
        // mon, tue
1427
        const char* wd = shortWeekDays[tm->tm.tm_wday];
393,327✔
1428
        char        buf[8] = {0};
393,327✔
1429
        for (int32_t i = 0; i < strlen(wd); ++i) buf[i] = tolower(wd[i]);
1,572,732✔
1430
        (void)sprintf(s, "%3s", buf);
393,327✔
1431
        s += 3;
393,327✔
1432
        break;
393,327✔
1433
      }
1434
      case TSFKW_HH24:
405,369✔
1435
        (void)sprintf(s, "%02d", tm->tm.tm_hour);
405,369✔
1436
        s += 2;
405,369✔
1437
        break;
405,369✔
1438
      case TSFKW_HH:
398,757✔
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);
398,757✔
1442
        s += 2;
398,757✔
1443
        break;
398,757✔
1444
      case TSFKW_MI:
803,555✔
1445
        (void)sprintf(s, "%02d", tm->tm.tm_min);
803,555✔
1446
        s += 2;
803,555✔
1447
        break;
803,555✔
1448
      case TSFKW_MM:
409,264✔
1449
        (void)sprintf(s, "%02d", tm->tm.tm_mon + 1);
409,264✔
1450
        s += 2;
409,264✔
1451
        break;
409,264✔
1452
      case TSFKW_MONTH: {
33✔
1453
        const char* mon = fullMonths[tm->tm.tm_mon];
33✔
1454
        char        buf[10] = {0};
33✔
1455
        for (int32_t i = 0; i < strlen(mon); ++i) buf[i] = toupper(mon[i]);
232✔
1456
        (void)sprintf(s, "%-9s", buf);
33✔
1457
        s += strlen(s);
33✔
1458
        break;
33✔
1459
      }
1460
      case TSFKW_MON: {
865✔
1461
        const char* mon = months[tm->tm.tm_mon];
865✔
1462
        char        buf[10] = {0};
865✔
1463
        for (int32_t i = 0; i < strlen(mon); ++i) buf[i] = toupper(mon[i]);
3,460✔
1464
        (void)sprintf(s, "%s", buf);
865✔
1465
        s += strlen(s);
865✔
1466
        break;
865✔
1467
      }
1468
      case TSFKW_Month:
19✔
1469
        (void)sprintf(s, "%-9s", fullMonths[tm->tm.tm_mon]);
19✔
1470
        s += strlen(s);
19✔
1471
        break;
19✔
1472
      case TSFKW_month: {
37✔
1473
        const char* mon = fullMonths[tm->tm.tm_mon];
37✔
1474
        char        buf[10] = {0};
37✔
1475
        for (int32_t i = 0; i < strlen(mon); ++i) buf[i] = tolower(mon[i]);
264✔
1476
        (void)sprintf(s, "%-9s", buf);
37✔
1477
        s += strlen(s);
37✔
1478
        break;
37✔
1479
      }
1480
      case TSFKW_Mon:
17✔
1481
        (void)sprintf(s, "%s", months[tm->tm.tm_mon]);
17✔
1482
        s += strlen(s);
17✔
1483
        break;
17✔
1484
      case TSFKW_mon: {
396,657✔
1485
        const char* mon = months[tm->tm.tm_mon];
396,657✔
1486
        char        buf[10] = {0};
396,657✔
1487
        for (int32_t i = 0; i < strlen(mon); ++i) buf[i] = tolower(mon[i]);
1,585,170✔
1488
        (void)sprintf(s, "%s", buf);
396,657✔
1489
        s += strlen(s);
396,657✔
1490
        break;
396,657✔
1491
      }
1492
      case TSFKW_SS:
803,076✔
1493
        (void)sprintf(s, "%02d", tm->tm.tm_sec);
803,076✔
1494
        s += 2;
803,076✔
1495
        break;
803,076✔
1496
      case TSFKW_MS:
8,764✔
1497
        (void)sprintf(s, "%03" PRId64, tm->fsec / 1000000L);
8,764✔
1498
        s += 3;
8,764✔
1499
        break;
8,764✔
1500
      case TSFKW_US:
66✔
1501
        (void)sprintf(s, "%06" PRId64, tm->fsec / 1000L);
66✔
1502
        s += 6;
66✔
1503
        break;
66✔
1504
      case TSFKW_NS:
85✔
1505
        (void)sprintf(s, "%09" PRId64, tm->fsec);
85✔
1506
        s += 9;
85✔
1507
        break;
85✔
1508
      case TSFKW_TZH:{
459✔
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;
459✔
1515
#endif
1516
        (void)sprintf(s, "%c%02d", (gmtoff >= 0) ? '+' : '-',
459✔
1517
                      abs(gmtoff) / 3600);
459!
1518
        s += strlen(s);
459✔
1519
        break;
459✔
1520
      }
1521
      case TSFKW_YYYY:
410,330✔
1522
        (void)sprintf(s, "%04d", tm->tm.tm_year + 1900);
410,330✔
1523
        s += strlen(s);
410,330✔
1524
        break;
410,330✔
1525
      case TSFKW_YYY:
98✔
1526
        (void)sprintf(s, "%03d", (tm->tm.tm_year + 1900) % 1000);
98✔
1527
        s += strlen(s);
98✔
1528
        break;
98✔
1529
      case TSFKW_YY:
398,147✔
1530
        (void)sprintf(s, "%02d", (tm->tm.tm_year + 1900) % 100);
398,147✔
1531
        s += strlen(s);
398,147✔
1532
        break;
398,147✔
1533
      case TSFKW_Y:
114✔
1534
        (void)sprintf(s, "%01d", (tm->tm.tm_year + 1900) % 10);
114✔
1535
        s += strlen(s);
114✔
1536
        break;
114✔
1537
      default:
×
1538
        break;
×
1539
    }
1540
  }
1541
  return TSDB_CODE_SUCCESS;
726,371✔
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) {
801,342✔
1547
  if (!*s) return -1;
801,342!
1548
  const char* const* fmt = arr;
801,342✔
1549
  for (; *fmt; ++fmt) {
5,679,331!
1550
    const char *l, *r;
1551
    for (l = fmt[0], r = s;; l++, r++) {
8,199,386✔
1552
      if (*l == '\0') return fmt - arr;
8,199,386✔
1553
      if (*r == '\0' || tolower(*l) != tolower(*r)) break;
7,398,043!
1554
    }
1555
  }
1556
  return -1;
×
1557
}
1558

1559
static const char* tsFormatStr2Int32(int32_t* dest, const char* str, int32_t len, bool needMoreDigit) {
3,926,640✔
1560
  char*       last;
1561
  int64_t     res;
1562
  const char* s = str;
3,926,640✔
1563
  if ('\0' == str[0]) return NULL;
3,926,640!
1564
  if (len <= 0) {
3,926,640✔
1565
    res = taosStr2Int64(s, &last, 10);
224✔
1566
    s = last;
224✔
1567
  } else {
1568
    char buf[16] = {0};
3,926,416✔
1569
    (void)strncpy(buf, s, len);
3,926,416✔
1570
    int32_t copiedLen = strlen(buf);
3,926,416✔
1571
    if (copiedLen < len) {
3,926,416✔
1572
      if (!needMoreDigit) {
320,008✔
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);
320,007✔
1577
        s += copiedLen;
320,007✔
1578
      } else {
1579
        // bytes not enough, and there are other digit formats to match
1580
        // '2023-1' 'YYYY-MMDD'
1581
        return NULL;
7✔
1582
      }
1583
    } else {
1584
      if (needMoreDigit) {
3,606,408✔
1585
        res = taosStr2Int64(buf, &last, 10);
30✔
1586
        // bytes enough, but digits not enough, like '202a12' 'YYYYMM', YYYY needs four digits
1587
        if (last - buf < len) return NULL;
30✔
1588
        s += last - buf;
24✔
1589
      } else {
1590
        res = taosStr2Int64(s, &last, 10);
3,606,378✔
1591
        s = last;
3,606,425✔
1592
      }
1593
    }
1594
  }
1595
  if (s == str) {
3,926,680✔
1596
    // no integers found
1597
    return NULL;
1✔
1598
  }
1599
  if (ERRNO == ERANGE || res > INT32_MAX || res < INT32_MIN) {
3,926,679!
1600
    // out of range
1601
    return NULL;
2✔
1602
  }
1603
  *dest = res;
3,926,677✔
1604
  return s;
3,926,677✔
1605
}
1606

1607
static int32_t adjustYearTo2020(int32_t year) {
400,403✔
1608
  if (year < 70) return year + 2000;    // 2000 - 2069
400,403✔
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) {
721,562✔
1616
  if (tm->tm_mon < 0 || tm->tm_mon > 11) return false;
721,562!
1617
  if (tm->tm_wday < 0 || tm->tm_wday > 6) return false;
721,553!
1618
  if (tm->tm_yday < 0 || tm->tm_yday > 365) return false;
721,552!
1619
  if (tm->tm_mday < 0 || tm->tm_mday > 31) return false;
721,552!
1620
  if (tm->tm_hour < 0 || tm->tm_hour > 23) return false;
721,552!
1621
  if (tm->tm_min < 0 || tm->tm_min > 59) return false;
721,551!
1622
  if (tm->tm_sec < 0 || tm->tm_sec > 60) return false;
721,552!
1623
  return true;
721,552✔
1624
}
1625

1626
static bool needMoreDigits(SArray* formats, int32_t curIdx) {
3,926,659✔
1627
  if (curIdx == taosArrayGetSize(formats) - 1) return false;
3,926,659✔
1628
  TSFormatNode* pNextNode = taosArrayGet(formats, curIdx + 1);
3,605,716✔
1629
  if (pNextNode->type == TS_FORMAT_NODE_TYPE_SEPARATOR) {
3,605,714✔
1630
    return false;
2,484,243✔
1631
  } else if (pNextNode->type == TS_FORMAT_NODE_TYPE_KEYWORD) {
1,121,471✔
1632
    return pNextNode->key->isDigit;
282✔
1633
  } else {
1634
    return isdigit(pNextNode->c[0]);
1,121,189✔
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,
721,604✔
1650
                       int32_t* fErrIdx, timezone_t tz) {
1651
  int32_t size = taosArrayGetSize(formats);
721,604✔
1652
  int32_t pm = 0;      // default am
721,604✔
1653
  int32_t hour12 = 0;  // default HH24
721,604✔
1654
  int32_t year = 0, mon = 0, yd = 0, md = 1, wd = 0;
721,604✔
1655
  int32_t hour = 0, min = 0, sec = 0, us = 0, ms = 0, ns = 0;
721,604✔
1656
  int32_t tzHour = 0;
721,604✔
1657
  int32_t err = 0;
721,604✔
1658
  bool    withYD = false, withMD = false;
721,604✔
1659

1660
  for (int32_t i = 0; i < size && *s != '\0'; ++i) {
9,455,757✔
1661
    while (isspace(*s) && *s != '\0') {
9,855,697✔
1662
      s++;
1,121,594✔
1663
    }
1664
    if (!s) break;
8,734,103!
1665
    TSFormatNode* node = taosArrayGet(formats, i);
8,734,103✔
1666
    if (node->type == TS_FORMAT_NODE_TYPE_SEPARATOR) {
8,734,104✔
1667
      // separator matches any character
1668
      if (isSeperatorChar(s[0])) s += node->len;
2,884,834✔
1669
      continue;
2,884,885✔
1670
    }
1671
    if (node->type == TS_FORMAT_NODE_TYPE_CHAR) {
5,849,270✔
1672
      int32_t pos = 0;
1,121,277✔
1673
      // skip leading spaces
1674
      while (isspace(node->c[pos]) && node->len > 0) pos++;
2,242,389!
1675
      while (pos < node->len && *s != '\0') {
1,137,835✔
1676
        if (!isspace(node->c[pos++])) {
16,558✔
1677
          while (isspace(*s) && *s != '\0') s++;
16,550!
1678
          if (*s != '\0') s++;  // forward together
16,540✔
1679
        }
1680
      }
1681
      continue;
1,121,277✔
1682
    }
1683
    switch (node->key->id) {
4,727,993!
1684
      case TSFKW_A_M:
219✔
1685
      case TSFKW_P_M:
1686
      case TSFKW_a_m:
1687
      case TSFKW_p_m: {
1688
        int32_t idx = strArrayCaseSearch(long_apms, s);
219✔
1689
        if (idx >= 0) {
219!
1690
          s += 4;
219✔
1691
          pm = idx % 2;
219✔
1692
          hour12 = 1;
219✔
1693
        } else {
1694
          err = -1;
×
1695
        }
1696
      } break;
219✔
1697
      case TSFKW_AM:
26✔
1698
      case TSFKW_PM:
1699
      case TSFKW_am:
1700
      case TSFKW_pm: {
1701
        int32_t idx = strArrayCaseSearch(apms, s);
26✔
1702
        if (idx >= 0) {
26!
1703
          s += 2;
26✔
1704
          pm = idx % 2;
26✔
1705
          hour12 = 1;
26✔
1706
        } else {
1707
          err = -1;
×
1708
        }
1709
      } break;
26✔
1710
      case TSFKW_HH:
106✔
1711
      case TSFKW_HH12: {
1712
        const char* newPos = tsFormatStr2Int32(&hour, s, 2, needMoreDigits(formats, i));
106✔
1713
        if (NULL == newPos || hour > 12 || hour <= 0) {
106!
1714
          err = -1;
1✔
1715
        } else {
1716
          hour12 = 1;
105✔
1717
          s = newPos;
105✔
1718
        }
1719
      } break;
106✔
1720
      case TSFKW_HH24: {
720,554✔
1721
        const char* newPos = tsFormatStr2Int32(&hour, s, 2, needMoreDigits(formats, i));
720,554✔
1722
        if (NULL == newPos) {
720,555!
1723
          err = -1;
×
1724
        } else {
1725
          hour12 = 0;
720,555✔
1726
          s = newPos;
720,555✔
1727
        }
1728
      } break;
720,555✔
1729
      case TSFKW_MI: {
720,660✔
1730
        const char* newPos = tsFormatStr2Int32(&min, s, 2, needMoreDigits(formats, i));
720,660✔
1731
        if (NULL == newPos) {
720,660!
1732
          err = -1;
×
1733
        } else {
1734
          s = newPos;
720,660✔
1735
        }
1736
      } break;
720,660✔
1737
      case TSFKW_SS: {
720,659✔
1738
        const char* newPos = tsFormatStr2Int32(&sec, s, 2, needMoreDigits(formats, i));
720,659✔
1739
        if (NULL == newPos)
720,661!
1740
          err = -1;
×
1741
        else
1742
          s = newPos;
720,661✔
1743
      } break;
720,661✔
1744
      case TSFKW_MS: {
419✔
1745
        const char* newPos = tsFormatStr2Int32(&ms, s, 3, needMoreDigits(formats, i));
419✔
1746
        if (NULL == newPos)
419!
1747
          err = -1;
×
1748
        else {
1749
          int32_t len = newPos - s;
419✔
1750
          ms *= len == 1 ? 100 : len == 2 ? 10 : 1;
419!
1751
          s = newPos;
419✔
1752
        }
1753
      } break;
419✔
1754
      case TSFKW_US: {
52✔
1755
        const char* newPos = tsFormatStr2Int32(&us, s, 6, needMoreDigits(formats, i));
52✔
1756
        if (NULL == newPos)
52!
1757
          err = -1;
×
1758
        else {
1759
          int32_t len = newPos - s;
52✔
1760
          us *= len == 1 ? 100000 : len == 2 ? 10000 : len == 3 ? 1000 : len == 4 ? 100 : len == 5 ? 10 : 1;
52!
1761
          s = newPos;
52✔
1762
        }
1763
      } break;
52✔
1764
      case TSFKW_NS: {
61✔
1765
        const char* newPos = tsFormatStr2Int32(&ns, s, 9, needMoreDigits(formats, i));
61✔
1766
        if (NULL == newPos)
61!
1767
          err = -1;
×
1768
        else {
1769
          int32_t len = newPos - s;
61✔
1770
          ns *= len == 1   ? 100000000
61✔
1771
                : len == 2 ? 10000000
122!
1772
                : len == 3 ? 1000000
122!
1773
                : len == 4 ? 100000
122!
1774
                : len == 5 ? 10000
122!
1775
                : len == 6 ? 1000
122!
1776
                : len == 7 ? 100
122!
1777
                : len == 8 ? 10
122!
1778
                           : 1;
61✔
1779
          s = newPos;
61✔
1780
        }
1781
      } break;
61✔
1782
      case TSFKW_TZH: {
224✔
1783
        const char* newPos = tsFormatStr2Int32(&tzHour, s, -1, needMoreDigits(formats, i));
224✔
1784
        if (NULL == newPos)
224!
1785
          err = -1;
×
1786
        else {
1787
          s = newPos;
224✔
1788
        }
1789
      } break;
224✔
1790
      case TSFKW_MONTH:
44✔
1791
      case TSFKW_Month:
1792
      case TSFKW_month: {
1793
        int32_t idx = strArrayCaseSearch(fullMonths, s);
44✔
1794
        if (idx >= 0) {
44!
1795
          s += strlen(fullMonths[idx]);
44✔
1796
          mon = idx;
44✔
1797
        } else {
1798
          err = -1;
×
1799
        }
1800
      } break;
44✔
1801
      case TSFKW_MON:
400,512✔
1802
      case TSFKW_Mon:
1803
      case TSFKW_mon: {
1804
        int32_t idx = strArrayCaseSearch(months, s);
400,512✔
1805
        if (idx >= 0) {
400,514✔
1806
          s += strlen(months[idx]);
400,513✔
1807
          mon = idx;
400,513✔
1808
        } else {
1809
          err = -1;
1✔
1810
        }
1811
      } break;
400,514✔
1812
      case TSFKW_MM: {
320,650✔
1813
        const char* newPos = tsFormatStr2Int32(&mon, s, 2, needMoreDigits(formats, i));
320,650✔
1814
        if (NULL == newPos) {
320,651✔
1815
          err = -1;
3✔
1816
        } else {
1817
          s = newPos;
320,648✔
1818
          mon -= 1;
320,648✔
1819
        }
1820
      } break;
320,651✔
1821
      case TSFKW_DAY:
35✔
1822
      case TSFKW_Day:
1823
      case TSFKW_day: {
1824
        int32_t idx = strArrayCaseSearch(weekDays, s);
35✔
1825
        if (idx >= 0) {
35!
1826
          s += strlen(weekDays[idx]);
35✔
1827
          wd = idx;
35✔
1828
        } else {
1829
          err = -1;
×
1830
        }
1831
      } break;
35✔
1832
      case TSFKW_DY:
400,505✔
1833
      case TSFKW_Dy:
1834
      case TSFKW_dy: {
1835
        int32_t idx = strArrayCaseSearch(shortWeekDays, s);
400,505✔
1836
        if (idx >= 0) {
400,507!
1837
          s += strlen(shortWeekDays[idx]);
400,507✔
1838
          wd = idx;
400,507✔
1839
        } else {
1840
          err = -1;
×
1841
        }
1842
      } break;
400,507✔
1843
      case TSFKW_DDD: {
354✔
1844
        const char* newPos = tsFormatStr2Int32(&yd, s, 3, needMoreDigits(formats, i));
354✔
1845
        if (NULL == newPos) {
354!
1846
          err = -1;
×
1847
        } else {
1848
          s = newPos;
354✔
1849
        }
1850
        withYD = true;
354✔
1851
      } break;
354✔
1852
      case TSFKW_DD: {
721,107✔
1853
        const char* newPos = tsFormatStr2Int32(&md, s, 2, needMoreDigits(formats, i));
721,107✔
1854
        if (NULL == newPos) {
721,109!
1855
          err = -1;
×
1856
        } else {
1857
          s = newPos;
721,109✔
1858
        }
1859
        withMD = true;
721,109✔
1860
      } break;
721,109✔
1861
      case TSFKW_D: {
321✔
1862
        const char* newPos = tsFormatStr2Int32(&wd, s, 1, needMoreDigits(formats, i));
321✔
1863
        if (NULL == newPos) {
321!
1864
          err = -1;
×
1865
        } else {
1866
          s = newPos;
321✔
1867
        }
1868
      } break;
321✔
1869
      case TSFKW_YYYY: {
321,110✔
1870
        const char* newPos = tsFormatStr2Int32(&year, s, 4, needMoreDigits(formats, i));
321,110✔
1871
        if (NULL == newPos) {
321,110✔
1872
          err = -1;
6✔
1873
        } else {
1874
          s = newPos;
321,104✔
1875
        }
1876
      } break;
321,110✔
1877
      case TSFKW_YYY: {
67✔
1878
        const char* newPos = tsFormatStr2Int32(&year, s, 3, needMoreDigits(formats, i));
67✔
1879
        if (NULL == newPos) {
67!
1880
          err = -1;
×
1881
        } else {
1882
          year = adjustYearTo2020(year);
67✔
1883
          s = newPos;
67✔
1884
        }
1885
      } break;
67✔
1886
      case TSFKW_YY: {
400,271✔
1887
        const char* newPos = tsFormatStr2Int32(&year, s, 2, needMoreDigits(formats, i));
400,271✔
1888
        if (NULL == newPos) {
400,271!
1889
          err = -1;
×
1890
        } else {
1891
          year = adjustYearTo2020(year);
400,271✔
1892
          s = newPos;
400,270✔
1893
        }
1894
      } break;
400,270✔
1895
      case TSFKW_Y: {
65✔
1896
        const char* newPos = tsFormatStr2Int32(&year, s, 1, needMoreDigits(formats, i));
65✔
1897
        if (NULL == newPos) {
65!
1898
          err = -1;
×
1899
        } else {
1900
          year = adjustYearTo2020(year);
65✔
1901
          s = newPos;
65✔
1902
        }
1903
      } break;
65✔
1904
      default:
×
1905
        break;
×
1906
    }
1907
    if (err) {
4,728,002✔
1908
      if (sErrPos) *sErrPos = s;
11!
1909
      if (fErrIdx) *fErrIdx = i;
11!
1910
      return err;
11✔
1911
    }
1912
  }
1913
  if (!withMD) {
721,654✔
1914
    // yyyy-mm-DDD, currently, the c api can't convert to correct timestamp, return not supported
1915
    if (withYD) return -3;
487✔
1916
  }
1917
  struct STm tm = {0};
721,621✔
1918
  tm.tm.tm_year = year - 1900;
721,621✔
1919
  tm.tm.tm_mon = mon;
721,621✔
1920
  tm.tm.tm_yday = yd;
721,621✔
1921
  tm.tm.tm_mday = md;
721,621✔
1922
  tm.tm.tm_wday = wd;
721,621✔
1923
  if (hour12) {
721,621✔
1924
    if (pm && hour < 12)
273!
1925
      tm.tm.tm_hour = hour + 12;
27✔
1926
    else if (!pm && hour == 12)
246!
1927
      tm.tm.tm_hour = 0;
34✔
1928
    else
1929
      tm.tm.tm_hour = hour;
212✔
1930
  } else {
1931
    tm.tm.tm_hour = hour;
721,348✔
1932
  }
1933
  tm.tm.tm_min = min;
721,621✔
1934
  tm.tm.tm_sec = sec;
721,621✔
1935
  if (!checkTm(&tm.tm)) return -2;
721,621✔
1936
  if (tzHour < -13 || tzHour > 13) return -2;
721,552!
1937
  tm.fsec = ms * 1000000 + us * 1000 + ns;
721,552✔
1938
  int32_t ret = taosTm2Ts(&tm, ts, precision, tz);
721,552✔
1939
  if (tzHour != 0) {
721,552✔
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;
189✔
1946
#endif
1947
    *ts += (gmtoff - tzHour * 3600) * TICK_PER_SECOND[precision];
189✔
1948
  }
1949
  return ret;
721,552✔
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) {
805,611✔
1953
  if (!*formats) {
805,611✔
1954
    *formats = taosArrayInit(8, sizeof(TSFormatNode));
9,927✔
1955
    if (!*formats) {
9,927!
1956
      TAOS_RETURN(terrno);
×
1957
    }
1958
    TAOS_CHECK_RETURN(parseTsFormat(format, *formats));
9,927!
1959
  }
1960
  struct STm tm;
1961
  TAOS_CHECK_RETURN(taosTs2Tm(ts, precision, &tm, tz));
805,611!
1962
  return tm2char(*formats, &tm, out, outLen);
812,181✔
1963
}
1964

1965
int32_t taosChar2Ts(const char* format, SArray** formats, const char* tsStr, int64_t* ts, int32_t precision,
721,567✔
1966
                    char* errMsg, int32_t errMsgLen, timezone_t tz) {
1967
  const char* sErrPos;
1968
  int32_t     fErrIdx;
1969
  if (!*formats) {
721,567✔
1970
    *formats = taosArrayInit(4, sizeof(TSFormatNode));
121,188✔
1971
    if (!*formats) {
121,188!
1972
      TAOS_RETURN(terrno);
×
1973
    }
1974
    TAOS_CHECK_RETURN(parseTsFormat(format, *formats));
121,188!
1975
  }
1976
  int32_t code = char2ts(tsStr, *formats, ts, precision, &sErrPos, &fErrIdx, tz);
721,567✔
1977
  if (code == -1) {
721,568✔
1978
    TSFormatNode* fNode = (taosArrayGet(*formats, fErrIdx));
4✔
1979
    snprintf(errMsg, errMsgLen, "mismatch format for: %s and %s", sErrPos,
4!
1980
             fErrIdx < taosArrayGetSize(*formats) ? ((TSFormatNode*)taosArrayGet(*formats, fErrIdx))->key->name : "");
8✔
1981
    code = TSDB_CODE_FUNC_TO_TIMESTAMP_FAILED_FORMAT_ERR;
4✔
1982
  } else if (code == -2) {
721,564✔
1983
    snprintf(errMsg, errMsgLen, "timestamp format error: %s -> %s", tsStr, format);
8✔
1984
    code = TSDB_CODE_FUNC_TO_TIMESTAMP_FAILED_TS_ERR;
8✔
1985
  } else if (code == -3) {
721,556✔
1986
    snprintf(errMsg, errMsgLen, "timestamp format not supported");
32✔
1987
    code = TSDB_CODE_FUNC_TO_TIMESTAMP_FAILED_NOT_SUPPORTED;
32✔
1988
  }
1989
  TAOS_RETURN(code);
721,568✔
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,
5,244✔
2045
                                        bool checkEq) {
2046
  if (UNIT_MATRIX[baseIdx][idx] == -1) return false;
5,244✔
2047
  if (baseIdx == idx) {
4,935✔
2048
    if (interval < baseInterval) return false;
2,531✔
2049
    if (checkEq && interval == baseInterval) return false;
2,527✔
2050
    return interval % baseInterval == 0;
2,518✔
2051
  }
2052
  int8_t  next = baseIdx + 1;
2,404✔
2053
  int64_t val = UNIT_MATRIX[baseIdx][next];
2,404✔
2054
  while (val != 0 && next <= idx) {
3,274!
2055
    if (val == -1) {
2,986✔
2056
      next++;
276✔
2057
      val = UNIT_MATRIX[baseIdx][next];
276✔
2058
      continue;
276✔
2059
    }
2060
    if (val % baseInterval == 0 || baseInterval % val == 0) {
2,710✔
2061
      int8_t extra = baseInterval >= val ? 0 : 1;
2,683✔
2062
      bool   needCheckEq = baseInterval >= val && !(baseIdx < next && val == 1);
2,683!
2063
      if (!recursiveTsmaCheckRecursive(baseInterval / val + extra, next, interval, idx, needCheckEq && checkEq)) {
2,683!
2064
        next++;
594✔
2065
        val = UNIT_MATRIX[baseIdx][next];
594✔
2066
        continue;
594✔
2067
      } else {
2068
        return true;
2,089✔
2069
      }
2070
    } else {
2071
      return false;
27✔
2072
    }
2073
  }
2074
  return false;
288✔
2075
}
2076

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

2081
  if (baseIdx == idx) {
1,327✔
2082
    if (interval < baseInterval) return false;
263✔
2083
    if (checkEq && interval == baseInterval) return false;
99✔
2084
    return interval % baseInterval == 0;
96✔
2085
  }
2086

2087
  int8_t  next = baseIdx - 1;
1,064✔
2088
  int64_t val = UNIT_MATRIX[baseIdx][next];
1,064✔
2089
  while (val != 0 && next >= 0) {
1,064!
2090
    return recursiveTsmaCheckRecursiveReverse(baseInterval * val, next, interval, idx, checkEq);
534✔
2091
  }
2092
  return false;
530✔
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,
3,354✔
2106
                                bool checkEq) {
2107
  bool baseIsCalendarDuration = IS_CALENDAR_TIME_DURATION(baseUnit);
3,354!
2108
  if (!baseIsCalendarDuration) {
3,354✔
2109
    if (TSDB_CODE_SUCCESS != convertTimeFromPrecisionToUnit(baseInterval, precision, baseUnit, &baseInterval)) {
1,982!
2110
      return false;
×
2111
    }
2112
  }
2113
  bool isCalendarDuration = IS_CALENDAR_TIME_DURATION(unit);
3,354!
2114
  if (!isCalendarDuration) {
3,354✔
2115
    if (TSDB_CODE_SUCCESS != convertTimeFromPrecisionToUnit(interval, precision, unit, &interval)) {
1,916!
2116
      return false;
×
2117
    }
2118
  }
2119

2120
  bool needCheckEq = baseIsCalendarDuration == isCalendarDuration && checkEq;
3,354✔
2121

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

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

2150
  time_t tmp = taosMktime(&tm, tz);
63,069✔
2151
  if (tmp == (time_t)-1) {
63,069!
2152
    uError("%s failed to get timestamp of today, code:%d", __FUNCTION__, ERRNO);
×
2153
    return t;
×
2154
  }
2155
  return (int64_t)tmp * factor;
63,069✔
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