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

taosdata / TDengine / #4720

08 Sep 2025 08:43AM UTC coverage: 58.139% (-0.6%) from 58.762%
#4720

push

travis-ci

web-flow
Merge pull request #32881 from taosdata/enh/add-new-windows-ci

fix(ci): update workflow reference to use new Windows CI YAML

133181 of 292179 branches covered (45.58%)

Branch coverage included in aggregate %.

201691 of 283811 relevant lines covered (71.07%)

5442780.71 hits per line

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

76.56
/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) {
818,924✔
36
  /* parse datatime string in with tz */
37
  if (strnchr(timestr, 'T', len, false) != NULL) {
818,924✔
38
    if (checkTzPresent(timestr, len)) {
263,494✔
39
      return parseTimeWithTz(timestr, utime, timePrec, 'T');
130,258✔
40
    } else {
41
      return parseLocaltimeDst((char*)timestr, len, utime, timePrec, 'T', tz);
133,287✔
42
    }
43
  } else {
44
    if (checkTzPresent(timestr, len)) {
554,490✔
45
      return parseTimeWithTz(timestr, utime, timePrec, 0);
126✔
46
    } else {
47
      return parseLocaltimeDst((char*)timestr, len, utime, timePrec, 0, tz);
555,279✔
48
    }
49
  }
50
}
51

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

56
  char* c = &seg[seg_len - 1];
818,607✔
57
  for (int32_t i = 0; i < seg_len; ++i) {
1,647,861✔
58
    if (0 == *c) {
959,638!
59
      break;
×
60
    }
61
    if (*c == 'Z' || *c == 'z' || *c == '+' || *c == '-') {
959,638!
62
      return true;
130,384✔
63
    }
64
    c--;
829,254✔
65
  }
66

67
  return false;
688,223✔
68
}
69

70
char* forwardToTimeStringEnd(char* str) {
944,868✔
71
  int32_t i = 0;
944,868✔
72
  int32_t numOfSep = 0;
944,868✔
73

74
  while (str[i] != 0 && numOfSep < 2) {
21,036,558✔
75
    if (str[i++] == ':') {
20,091,690✔
76
      numOfSep++;
945,052✔
77
    }
78
  }
79

80
  while (str[i] >= '0' && str[i] <= '9') {
1,889,997!
81
    i++;
945,129✔
82
  }
83

84
  return &str[i];
944,868✔
85
}
86

87
int32_t parseFraction(char* str, char** end, int32_t timePrec, int64_t* pFraction) {
228,322✔
88
  int32_t i = 0;
228,322✔
89
  int64_t fraction = 0;
228,322✔
90

91
  const int32_t MILLI_SEC_FRACTION_LEN = 3;
228,322✔
92
  const int32_t MICRO_SEC_FRACTION_LEN = 6;
228,322✔
93
  const int32_t NANO_SEC_FRACTION_LEN = 9;
228,322✔
94

95
  int32_t factor[9] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};
228,322✔
96
  int32_t times = 1;
228,322✔
97

98
  while (str[i] >= '0' && str[i] <= '9') {
814,753!
99
    i++;
586,431✔
100
  }
101

102
  int32_t totalLen = i;
228,322✔
103
  if (totalLen <= 0) {
228,322!
104
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
105
  }
106

107
  /* parse the fraction */
108
  if (timePrec == TSDB_TIME_PRECISION_MILLI) {
228,322✔
109
    /* only use the initial 3 bits */
110
    if (i >= MILLI_SEC_FRACTION_LEN) {
228,316✔
111
      i = MILLI_SEC_FRACTION_LEN;
129,754✔
112
    }
113

114
    times = MILLI_SEC_FRACTION_LEN - i;
228,316✔
115
  } else if (timePrec == TSDB_TIME_PRECISION_MICRO) {
6✔
116
    if (i >= MICRO_SEC_FRACTION_LEN) {
3!
117
      i = MICRO_SEC_FRACTION_LEN;
3✔
118
    }
119
    times = MICRO_SEC_FRACTION_LEN - i;
3✔
120
  } else if (timePrec == TSDB_TIME_PRECISION_NANO) {
3!
121
    if (i >= NANO_SEC_FRACTION_LEN) {
3!
122
      i = NANO_SEC_FRACTION_LEN;
3✔
123
    }
124
    times = NANO_SEC_FRACTION_LEN - i;
3✔
125
  } else {
126
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
127
  }
128

129
  fraction = strnatoi(str, i) * factor[times];
228,322✔
130
  *end = str + totalLen;
228,322✔
131
  *pFraction = fraction;
228,322✔
132

133
  TAOS_RETURN(TSDB_CODE_SUCCESS);
228,322✔
134
}
135

136
#define PARSE(str,len,result) \
137
  if (len != 2) {\
138
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);\
139
  }\
140
  result = strnatoi(str, len);
141

142
int32_t parseTimezone(char* str, int64_t* tzOffset) {
443,334✔
143
  int64_t hour = 0;
443,334✔
144
  int64_t minute = 0;
443,334✔
145

146
  int32_t i = 0;
443,334✔
147
  if (str[i] != '+' && str[i] != '-') {
443,334!
148
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
149
  }
150

151
  i++;
443,334✔
152

153
  int32_t j = i;
443,334✔
154
  while (str[j]) {
2,214,476✔
155
    if ((str[j] >= '0' && str[j] <= '9') || str[j] == ':') {
1,771,160!
156
      ++j;
1,771,142✔
157
      continue;
1,771,142✔
158
    }
159

160
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
18✔
161
  }
162

163
  char* sep = strchr(&str[i], ':');
443,316✔
164
  if (sep != NULL) {
443,316✔
165
    int32_t hourSize = (int32_t)(sep - &str[i]);
84✔
166
    PARSE(&str[i], hourSize, hour);
84✔
167

168
    i += hourSize + 1;
66✔
169
    size_t minSize = strlen(&str[i]);
66✔
170
    PARSE(&str[i], minSize, minute);
66✔
171
  } else {
172
    size_t hourSize = strlen(&str[i]);
443,232✔
173
    if (hourSize > 2){
443,232✔
174
      hourSize = 2;
442,694✔
175
    }
176
    PARSE(&str[i], hourSize, hour)
443,232✔
177
    i += hourSize;
442,681✔
178
    size_t minSize = strlen(&str[i]);
442,681✔
179
    if (minSize > 0){
442,681!
180
      PARSE(&str[i], minSize, minute);
442,683✔
181
    }
182
  }
183

184
  if (hour > 13 || hour < 0) {
442,623!
185
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
186
  }
187
  if (minute > 59 || minute < 0) {
442,668!
188
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
189
  }
190

191
  if (str[0] == '+') {
442,693✔
192
    *tzOffset = -(hour * 3600 + minute * 60);
442,631✔
193
  } else {
194
    *tzOffset = hour * 3600 + minute * 60;
62✔
195
  }
196

197
  TAOS_RETURN(TSDB_CODE_SUCCESS);
442,693✔
198
}
199

200
int32_t offsetOfTimezone(char* tzStr, int64_t* offset) {
316,503✔
201
  if (tzStr && (tzStr[0] == 'z' || tzStr[0] == 'Z')) {
316,503!
202
    *offset = 0;
11✔
203
    return TSDB_CODE_SUCCESS;
11✔
204
  }
205
  return parseTimezone(tzStr, offset);
316,492✔
206
}
207

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

224
  struct tm tm = {0};
130,384✔
225

226
  char* str;
227
  if (delim == 'T') {
130,384✔
228
    str = taosStrpTime(timestr, "%Y-%m-%dT%H:%M:%S", &tm);
130,258✔
229
  } else if (delim == 0) {
126!
230
    str = taosStrpTime(timestr, "%Y-%m-%d %H:%M:%S", &tm);
126✔
231
  } else {
232
    str = NULL;
×
233
  }
234

235
  if (str == NULL) {
130,384✔
236
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
3,535✔
237
  }
238

239
/* mktime will be affected by TZ, set by using taos_options */
240
#if defined(WINDOWS) || defined(TD_ASTRA) 
241
  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);
242
  // int64_t seconds = gmtime(&tm);
243
#else
244
  int64_t seconds = taosTimeGm(&tm);
126,849✔
245
  if (seconds == -1){
126,849!
246
    TAOS_RETURN(TAOS_SYSTEM_ERROR(ERRNO));
×
247
  }
248
#endif
249

250
  int64_t fraction = 0;
126,849✔
251
  str = forwardToTimeStringEnd((char*)timestr);
126,849✔
252

253
  if ((str[0] == 'Z' || str[0] == 'z') && str[1] == '\0') {
126,849!
254
    /* utc time, no millisecond, return directly*/
255
    *time = seconds * factor;
×
256
  } else if (str[0] == '.') {
126,849✔
257
    str += 1;
126,739✔
258
    TAOS_CHECK_RETURN(parseFraction(str, &str, timePrec, &fraction));
126,739!
259

260
    *time = seconds * factor + fraction;
126,739✔
261

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

271
      *time += tzOffset * factor;
126,739✔
272
    }
273

274
  } else if (str[0] == '+' || str[0] == '-') {
110!
275
    *time = seconds * factor + fraction;
110✔
276

277
    // parse the timezone
278
    TAOS_CHECK_RETURN(parseTimezone(str, &tzOffset));
110✔
279

280
    *time += tzOffset * factor;
60✔
281
  } else {
282
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
283
  }
284

285
  TAOS_RETURN(TSDB_CODE_SUCCESS);
126,799✔
286
}
287

288
static FORCE_INLINE bool validateTm(struct tm* pTm) {
289
  if (pTm == NULL) {
215,490!
290
    return false;
×
291
  }
292

293
  int32_t dayOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
215,490✔
294

295
  int32_t leapYearMonthDay = 29;
215,490✔
296
  int32_t year = pTm->tm_year + 1900;
215,490✔
297
  bool    isLeapYear = ((year % 100) == 0) ? ((year % 400) == 0) : ((year % 4) == 0);
215,490✔
298

299
  if (isLeapYear && (pTm->tm_mon == 1)) {
215,490✔
300
    if (pTm->tm_mday > leapYearMonthDay) {
256!
301
      return false;
×
302
    }
303
  } else {
304
    if (pTm->tm_mday > dayOfMonth[pTm->tm_mon]) {
215,234!
305
      return false;
×
306
    }
307
  }
308

309
  return true;
215,490✔
310
}
311

312
int32_t parseLocaltimeDst(char* timestr, int32_t len, int64_t* utime, int32_t timePrec, char delim, timezone_t tz) {
688,341✔
313
  *utime = 0;
688,341✔
314
  struct tm tm = {0};
688,341✔
315
  tm.tm_isdst = -1;
688,341✔
316

317
  char* str;
318
  if (delim == 'T') {
688,341✔
319
    str = taosStrpTime(timestr, "%Y-%m-%dT%H:%M:%S", &tm);
133,293✔
320
  } else if (delim == 0) {
555,048!
321
    str = taosStrpTime(timestr, "%Y-%m-%d %H:%M:%S", &tm);
555,303✔
322
  } else {
323
    str = NULL;
×
324
  }
325

326
  if (str == NULL || (((str - timestr) < len) && (*str != '.')) || !validateTm(&tm)) {
903,832!
327
    // if parse failed, try "%Y-%m-%d" format
328
    str = taosStrpTime(timestr, "%Y-%m-%d", &tm);
472,980✔
329
    if (str == NULL || (((str - timestr) < len) && (*str != '.')) || !validateTm(&tm)) {
472,952!
330
      TAOS_RETURN(TSDB_CODE_INVALID_PARA);
472,824✔
331
    }
332
  }
333

334
  int64_t seconds = taosMktime(&tm, tz);
215,490✔
335

336
  int64_t fraction = 0;
215,246✔
337
  if (*str == '.') {
215,246✔
338
    /* parse the second fraction part */
339
    TAOS_CHECK_RETURN(parseFraction(str + 1, &str, timePrec, &fraction));
101,583✔
340
  }
341

342
  *utime = TSDB_TICK_PER_SECOND(timePrec) * seconds + fraction;
215,232!
343
  TAOS_RETURN(TSDB_CODE_SUCCESS);
215,232✔
344
}
345

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

358
int64_t convertTimePrecision(int64_t utime, int32_t fromPrecision, int32_t toPrecision) {
38,581✔
359
  switch (fromPrecision) {
38,581!
360
    case TSDB_TIME_PRECISION_MILLI: {
30,646✔
361
      switch (toPrecision) {
30,646!
362
        case TSDB_TIME_PRECISION_MILLI:
24,228✔
363
          return utime;
24,228✔
364
        case TSDB_TIME_PRECISION_MICRO:
111✔
365
          if (utime > INT64_MAX / 1000) {
111!
366
            return INT64_MAX;
×
367
          }
368
          return utime * 1000;
111✔
369
        case TSDB_TIME_PRECISION_NANO:
6,307✔
370
          if (utime > INT64_MAX / 1000000) {
6,307!
371
            return INT64_MAX;
×
372
          }
373
          return utime * 1000000;
6,307✔
374
        default:
×
375
          return utime;
×
376
      }
377
    }  // end from milli
378
    case TSDB_TIME_PRECISION_MICRO: {
21✔
379
      switch (toPrecision) {
21!
380
        case TSDB_TIME_PRECISION_MILLI:
21✔
381
          return utime / 1000;
21✔
382
        case TSDB_TIME_PRECISION_MICRO:
×
383
          return utime;
×
384
        case TSDB_TIME_PRECISION_NANO:
×
385
          if (utime > INT64_MAX / 1000) {
×
386
            return INT64_MAX;
×
387
          }
388
          return utime * 1000;
×
389
        default:
×
390
          return utime;
×
391
      }
392
    }  // end from micro
393
    case TSDB_TIME_PRECISION_NANO: {
7,921✔
394
      switch (toPrecision) {
7,921!
395
        case TSDB_TIME_PRECISION_MILLI:
6,949✔
396
          return utime / 1000000;
6,949✔
397
        case TSDB_TIME_PRECISION_MICRO:
×
398
          return utime / 1000;
×
399
        case TSDB_TIME_PRECISION_NANO:
972✔
400
          return utime;
972✔
401
        default:
×
402
          return utime;
×
403
      }
404
    }  // end from nano
405
    default: {
×
406
      return utime;  // only to pass windows compilation
×
407
    }
408
  }  // end switch fromPrecision
409

410
  return utime;
411
}
412

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

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

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

492
  TAOS_RETURN(TSDB_CODE_SUCCESS);
904✔
493
}
494

495
int32_t convertStringToTimestamp(int16_t type, char* inputData, int64_t timePrec, int64_t* timeVal, timezone_t tz, void* charsetCxt) {
716,731✔
496
  int32_t charLen = varDataLen(inputData);
716,731✔
497
  char*   newColData;
498
  if (type == TSDB_DATA_TYPE_BINARY || type == TSDB_DATA_TYPE_VARBINARY) {
957,676✔
499
    newColData = taosMemoryCalloc(1, charLen + 1);
398,709!
500
    if (NULL == newColData) {
398,983!
501
      TAOS_RETURN(terrno);
×
502
    }
503
    (void)memcpy(newColData, varDataVal(inputData), charLen);
398,983✔
504
    int32_t ret = taosParseTime(newColData, timeVal, charLen, (int32_t)timePrec, tz);
398,983✔
505
    if (ret != TSDB_CODE_SUCCESS) {
398,735✔
506
      taosMemoryFree(newColData);
158,039!
507
      TAOS_RETURN(TSDB_CODE_INVALID_TIMESTAMP);
158,198✔
508
    }
509
    taosMemoryFree(newColData);
240,696!
510
  } else if (type == TSDB_DATA_TYPE_NCHAR) {
318,022!
511
    newColData = taosMemoryCalloc(1, charLen + TSDB_NCHAR_SIZE);
318,026!
512
    if (NULL == newColData) {
318,289!
513
      TAOS_RETURN(terrno);
×
514
    }
515
    int len = taosUcs4ToMbs((TdUcs4*)varDataVal(inputData), charLen, newColData, charsetCxt);
318,289✔
516
    if (len < 0) {
318,671!
517
      taosMemoryFree(newColData);
×
518
      TAOS_RETURN(TSDB_CODE_FAILED);
×
519
    }
520
    newColData[len] = 0;
318,671✔
521
    int32_t ret = taosParseTime(newColData, timeVal, len, (int32_t)timePrec, tz);
318,671✔
522
    if (ret != TSDB_CODE_SUCCESS) {
318,213!
523
      taosMemoryFree(newColData);
318,214!
524
      TAOS_RETURN(ret);
318,325✔
525
    }
526
    taosMemoryFree(newColData);
×
527
  } else {
528
    TAOS_RETURN(TSDB_CODE_FAILED);
×
529
  }
530
  TAOS_RETURN(TSDB_CODE_SUCCESS);
240,945✔
531
}
532

533
int32_t getDuration(int64_t val, char unit, int64_t* result, int32_t timePrecision) {
6,371✔
534
  switch (unit) {
6,371!
535
    case 's':
4,124✔
536
      if (val > INT64_MAX / MILLISECOND_PER_SECOND) {
4,124!
537
        TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
538
      }
539
      (*result) = convertTimePrecision(val * MILLISECOND_PER_SECOND, TSDB_TIME_PRECISION_MILLI, timePrecision);
4,124✔
540
      break;
4,124✔
541
    case 'm':
492✔
542
      if (val > INT64_MAX / MILLISECOND_PER_MINUTE) {
492!
543
        TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
544
      }
545
      (*result) = convertTimePrecision(val * MILLISECOND_PER_MINUTE, TSDB_TIME_PRECISION_MILLI, timePrecision);
492✔
546
      break;
492✔
547
    case 'h':
246✔
548
      if (val > INT64_MAX / MILLISECOND_PER_MINUTE) {
246!
549
        TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
550
      }
551
      (*result) = convertTimePrecision(val * MILLISECOND_PER_HOUR, TSDB_TIME_PRECISION_MILLI, timePrecision);
246✔
552
      break;
246✔
553
    case 'd':
1,125✔
554
      if (val > INT64_MAX / MILLISECOND_PER_DAY) {
1,125!
555
        TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
556
      }
557
      (*result) = convertTimePrecision(val * MILLISECOND_PER_DAY, TSDB_TIME_PRECISION_MILLI, timePrecision);
1,125✔
558
      break;
1,125✔
559
    case 'w':
110✔
560
      if (val > INT64_MAX / MILLISECOND_PER_WEEK) {
110!
561
        TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
562
      }
563
      (*result) = convertTimePrecision(val * MILLISECOND_PER_WEEK, TSDB_TIME_PRECISION_MILLI, timePrecision);
110✔
564
      break;
110✔
565
    case 'a':
232✔
566
      (*result) = convertTimePrecision(val, TSDB_TIME_PRECISION_MILLI, timePrecision);
232✔
567
      break;
232✔
568
    case 'u':
21✔
569
      (*result) = convertTimePrecision(val, TSDB_TIME_PRECISION_MICRO, timePrecision);
21✔
570
      break;
21✔
571
    case 'b':
21✔
572
      (*result) = convertTimePrecision(val, TSDB_TIME_PRECISION_NANO, timePrecision);
21✔
573
      break;
21✔
574
    default: {
×
575
      TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
576
    }
577
  }
578
  TAOS_RETURN(TSDB_CODE_SUCCESS);
6,371✔
579
}
580

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

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

606
  /* natual month/year are not allowed in absolute duration */
607
  *unit = token[tokenlen - 1];
485✔
608
  if (*unit == 'n' || *unit == 'y') {
485!
609
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
610
  }
611

612
  return getDuration(timestamp, *unit, duration, timePrecision);
485✔
613
}
614

615
int32_t parseNatualDuration(const char* token, int32_t tokenLen, int64_t* duration, char* unit, int32_t timePrecision,
6,025✔
616
                            bool negativeAllow) {
617
  SET_ERRNO(0);
6,025✔
618

619
  /* get the basic numeric value */
620
  *duration = taosStr2Int64(token, NULL, 10);
6,025✔
621
  if ((*duration < 0 && !negativeAllow) || ERRNO != 0) {
6,025!
622
    TAOS_RETURN(TAOS_SYSTEM_ERROR(ERRNO));
×
623
  }
624

625
  *unit = token[tokenLen - 1];
6,025✔
626
  if (*unit == 'n' || *unit == 'y') {
6,025✔
627
    TAOS_RETURN(TSDB_CODE_SUCCESS);
355✔
628
  }
629
  if (isdigit(*unit)) {
5,670✔
630
    *unit = getPrecisionUnit(timePrecision);
6✔
631
  }
632

633
  return getDuration(*duration, *unit, duration, timePrecision);
5,670✔
634
}
635

636
static bool taosIsLeapYear(int32_t year) { return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); }
18,929!
637

638
int64_t taosTimeAdd(int64_t t, int64_t duration, char unit, int32_t precision, timezone_t tz) {
225,795,146✔
639
  if (duration == 0) {
225,795,146✔
640
    return t;
95,234,395✔
641
  }
642

643
  if (!IS_CALENDAR_TIME_DURATION(unit)) {
130,560,751!
644
    double tmp = t;
131,597,345✔
645
    if (tmp + duration >= (double)INT64_MAX || tmp + duration <= (double)INT64_MIN) {
131,597,345!
646
      uError("time overflow, t:%" PRId64 ", duration:%" PRId64 ", unit:%c, precision:%d", t, duration, unit, precision);
×
647
      return t;
268✔
648
    } else {
649
      return t + duration;
131,727,421✔
650
    }
651
  }
652

653
  // The following code handles the y/n time duration
654
  int64_t numOfMonth = (unit == 'y') ? duration * 12 : duration;
×
655
  int64_t fraction = t % TSDB_TICK_PER_SECOND(precision);
×
656

657
  struct tm  tm;
658
  time_t     tt = (time_t)(t / TSDB_TICK_PER_SECOND(precision));
×
659
  if(taosLocalTime(&tt, &tm, NULL, 0, tz) == NULL) {
×
660
    uError("failed to convert time to gm time, code:%d", ERRNO);
×
661
    return t;
×
662
  }
663
  int32_t    mon = tm.tm_year * 12 + tm.tm_mon + (int32_t)numOfMonth;
18,930✔
664
  tm.tm_year = mon / 12;
18,930✔
665
  tm.tm_mon = mon % 12;
18,930✔
666
  int daysOfMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
18,930✔
667
  if (taosIsLeapYear(1900 + tm.tm_year)) {
18,930✔
668
    daysOfMonth[1] = 29;
14,097✔
669
  }
670
  if (tm.tm_mday > daysOfMonth[tm.tm_mon]) {
18,931✔
671
    tm.tm_mday = daysOfMonth[tm.tm_mon];
217✔
672
  }
673

674
  tt = taosMktime(&tm, tz);
18,931✔
675
  if (tt == -1){
18,932!
676
    uError("failed to convert gm time to time, code:%d", ERRNO);
×
677
    return t;
×
678
  }
679
  return (int64_t)(tt * TSDB_TICK_PER_SECOND(precision) + fraction);
18,932✔
680
}
681

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

720
  if (unit != 'n' && unit != 'y') {
3,007!
721
    ret = (int32_t)((ekey - skey) / interval);
3,007✔
722
    if (order == TSDB_ORDER_DESC && ret * interval < (ekey - skey)) ret += 1;
3,007!
723
  } else {
724
    skey /= (int64_t)(TSDB_TICK_PER_SECOND(precision));
×
725
    ekey /= (int64_t)(TSDB_TICK_PER_SECOND(precision));
×
726

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

735
    t = (time_t)ekey;
×
736
    if (taosLocalTime(&t, &tm, NULL, 0, NULL) == NULL) {
×
737
      uError("%s failed to convert time to local time, code:%d", __FUNCTION__, ERRNO);
×
738
      return ret;
×
739
    }
740
    int32_t emon = tm.tm_year * 12 + tm.tm_mon;
×
741

742
    if (unit == 'y') {
×
743
      interval *= 12;
×
744
    }
745
    ret = (emon - smon) / (int32_t)interval;
×
746
    if (order == TSDB_ORDER_DESC && ret * interval < (smon - emon)) ret += 1;
×
747
  }
748
  return ret + 1;
3,007✔
749
}
750

751
int64_t taosTimeTruncate(int64_t ts, const SInterval* pInterval) {
963,995✔
752
  if (ts <= INT64_MIN || ts >= INT64_MAX) {
963,995!
753
    return ts;
×
754
  }
755
  if (pInterval->sliding == 0) {
964,173!
756
    return ts;
×
757
  }
758

759
  int64_t start = ts;
964,173✔
760
  int32_t precision = pInterval->precision;
964,173✔
761

762
  if (IS_CALENDAR_TIME_DURATION(pInterval->slidingUnit)) {
964,173!
763
    start /= (int64_t)(TSDB_TICK_PER_SECOND(precision));
1,715✔
764
    struct tm  tm;
765
    time_t     tt = (time_t)start;
1,715✔
766
    if (taosLocalTime(&tt, &tm, NULL, 0, pInterval->timezone) == NULL){
1,715!
767
      uError("%s failed to convert time to local time, code:%d", __FUNCTION__, ERRNO);
×
768
      return ts;
×
769
    }
770
    tm.tm_sec = 0;
1,724✔
771
    tm.tm_min = 0;
1,724✔
772
    tm.tm_hour = 0;
1,724✔
773
    tm.tm_mday = 1;
1,724✔
774

775
    if (pInterval->slidingUnit == 'y') {
1,724✔
776
      tm.tm_mon = 0;
292✔
777
      tm.tm_year = (int32_t)(tm.tm_year / pInterval->sliding * pInterval->sliding);
292✔
778
    } else {
779
      int32_t mon = tm.tm_year * 12 + tm.tm_mon;
1,432✔
780
      mon = (int32_t)(mon / pInterval->sliding * pInterval->sliding);
1,432✔
781
      tm.tm_year = mon / 12;
1,432✔
782
      tm.tm_mon = mon % 12;
1,432✔
783
    }
784

785
    tt = taosMktime(&tm, pInterval->timezone);
1,724✔
786
    if (tt == -1){
1,725✔
787
      uError("%s failed to convert local time to time, code:%d", __FUNCTION__, ERRNO);
1!
788
      return ts;
×
789
    }
790
    start = (int64_t)(tt * TSDB_TICK_PER_SECOND(precision));
1,724✔
791
  } else {
792
    if (IS_CALENDAR_TIME_DURATION(pInterval->intervalUnit)) {
963,744!
793
      int64_t news = (ts / pInterval->sliding) * pInterval->sliding;
1,286✔
794
      if (pInterval->slidingUnit == 'd' || pInterval->slidingUnit == 'w') {
1,286!
795
#if defined(WINDOWS) && _MSC_VER >= 1900
796
        int64_t timezone = _timezone;
797
#endif
798
        news += (int64_t)(timezone * TSDB_TICK_PER_SECOND(precision));
1,289!
799
      }
800

801
      start = news;
1,286✔
802
      if (news <= ts) {
1,286!
803
        int64_t prev = news;
1,289✔
804
        int64_t newe = taosTimeAdd(news, pInterval->interval, pInterval->intervalUnit, precision, pInterval->timezone) - 1;
1,289✔
805

806
        if (newe < ts) {  // move towards the greater endpoint
1,289!
807
          while (newe < ts && news < ts) {
×
808
            news += pInterval->sliding;
×
809
            newe = taosTimeAdd(news, pInterval->interval, pInterval->intervalUnit, precision, pInterval->timezone) - 1;
×
810
          }
811

812
          prev = news;
×
813
        } else {
814
          while (newe >= ts) {
4,937✔
815
            prev = news;
3,648✔
816
            news -= pInterval->sliding;
3,648✔
817
            newe = taosTimeAdd(news, pInterval->interval, pInterval->intervalUnit, precision, pInterval->timezone) - 1;
3,648✔
818
          }
819
        }
820

821
        start = prev;
1,289✔
822
      }
823
    } else {
824
      int64_t delta = ts - pInterval->interval;
961,172✔
825
      int32_t factor = (delta >= 0) ? 1 : -1;
961,172✔
826

827
      start = (delta / pInterval->sliding + factor) * pInterval->sliding;
961,172✔
828

829
      if (pInterval->intervalUnit == 'd' || pInterval->intervalUnit == 'w') {
961,172✔
830
        /*
831
         * here we revised the start time of day according to the local time zone,
832
         * but in case of DST, the start time of one day need to be dynamically decided.
833
         */
834
        // todo refactor to extract function that is available for Linux/Windows/Mac platform
835
#if defined(WINDOWS) && _MSC_VER >= 1900
836
        // see
837
        // https://docs.microsoft.com/en-us/cpp/c-runtime-library/daylight-dstbias-timezone-and-tzname?view=vs-2019
838
        int64_t timezone = _timezone;
839
#endif
840

841
        start += (int64_t)(timezone * TSDB_TICK_PER_SECOND(precision));
50,756!
842
      }
843

844
      int64_t end = 0;
961,172✔
845

846
      // not enough time range
847
      if (start < 0 || INT64_MAX - start > pInterval->interval - 1) {
961,172!
848
        end = taosTimeAdd(start, pInterval->interval, pInterval->intervalUnit, precision, pInterval->timezone) - 1;
961,172✔
849
        while (end < ts) {  // move forward to the correct time window
977,707✔
850
          start += pInterval->sliding;
16,548✔
851

852
          if (start < 0 || INT64_MAX - start > pInterval->interval - 1) {
16,548!
853
            end = start + pInterval->interval - 1;
16,548✔
854
          } else {
855
            end = INT64_MAX;
×
856
            break;
×
857
          }
858
        }
859
      } else {
860
        end = INT64_MAX;
×
861
      }
862
    }
863
  }
864

865
  if (pInterval->offset > 0) {
964,169✔
866
    // try to move current window to the left-hande-side, due to the offset effect.
867
    int64_t newe = taosTimeAdd(start, pInterval->interval, pInterval->intervalUnit, precision, pInterval->timezone) - 1;
4,401✔
868
    int64_t slidingStart = start;
4,401✔
869
    while (newe >= ts) {
11,160✔
870
      start = slidingStart;
6,759✔
871
      slidingStart = taosTimeAdd(slidingStart, -pInterval->sliding, pInterval->slidingUnit, precision, pInterval->timezone);
6,759✔
872
      int64_t news = taosTimeAdd(slidingStart, pInterval->offset, pInterval->offsetUnit, precision, pInterval->timezone);
6,759✔
873
      newe = taosTimeAdd(news, pInterval->interval, pInterval->intervalUnit, precision, pInterval->timezone) - 1;
6,759✔
874
    }
875
    start = taosTimeAdd(start, pInterval->offset, pInterval->offsetUnit, precision, pInterval->timezone);
4,401✔
876
  }
877

878
  return start;
964,077✔
879
}
880

881
// used together with taosTimeTruncate. when offset is great than zero, slide-start/slide-end is the anchor point
882
int64_t taosTimeGetIntervalEnd(int64_t intervalStart, const SInterval* pInterval) {
2,056,638✔
883
  return taosTimeAdd(intervalStart, pInterval->interval, pInterval->intervalUnit, pInterval->precision, pInterval->timezone) - 1;
2,056,638✔
884
}
885

886
void calcIntervalAutoOffset(SInterval* interval) {
1,008,744✔
887
  if (!interval || interval->offset != AUTO_DURATION_VALUE) {
1,008,744!
888
    return;
1,006,591✔
889
  }
890

891
  interval->offset = 0;
2,153✔
892

893
  if (interval->timeRange.skey == INT64_MIN) {
2,153!
894
    return;
×
895
  }
896

897
  TSKEY skey = interval->timeRange.skey;
2,153✔
898
  TSKEY start = taosTimeTruncate(skey, interval);
2,153✔
899
  TSKEY news = start;
2,151✔
900
  while (news <= skey) {
5,827✔
901
    start = news;
3,674✔
902
    news = taosTimeAdd(start, interval->sliding, interval->slidingUnit, interval->precision, interval->timezone);
3,674✔
903
    if (news < start) {
3,676!
904
      // overflow happens
905
      uError("%s failed and skip, skey [%" PRId64 "], inter[%" PRId64 "(%c)], slid[%" PRId64 "(%c)], precision[%d]",
×
906
             __func__, skey, interval->interval, interval->intervalUnit, interval->sliding, interval->slidingUnit,
907
             interval->precision);
908
      return;
×
909
    }
910
  }
911
  interval->offset = skey - start;
2,153✔
912
}
913

914
// internal function, when program is paused in debugger,
915
// one can call this function from debugger to print a
916
// timestamp as human readable string, for example (gdb):
917
//     p fmtts(1593769722)
918
// outputs:
919
//     2020-07-03 17:48:42
920
// and the parameter can also be a variable.
921
const char* fmtts(int64_t ts) {
×
922
  static char buf[TD_TIME_STR_LEN] = {0};
923
  size_t      pos = 0;
×
924
  struct tm   tm;
925

926
  if (ts > -62135625943 && ts < 32503651200) {
×
927
    time_t t = (time_t)ts;
×
928
    if (taosLocalTime(&t, &tm, buf, sizeof(buf), NULL) == NULL) {
×
929
      return buf;
×
930
    }
931
    pos += taosStrfTime(buf + pos, sizeof(buf), "s=%Y-%m-%d %H:%M:%S", &tm);
×
932
  }
933

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

948
  {
949
    time_t t = (time_t)(ts / 1000000);
×
950
    if (taosLocalTime(&t, &tm, buf, sizeof(buf), NULL) == NULL) {
×
951
      return buf;
×
952
    }
953
    if (pos > 0) {
×
954
      buf[pos++] = ' ';
×
955
      buf[pos++] = '|';
×
956
      buf[pos++] = ' ';
×
957
    }
958
    pos += taosStrfTime(buf + pos, sizeof(buf), "us=%Y-%m-%d %H:%M:%S", &tm);
×
959
    pos += sprintf(buf + pos, ".%06d", (int32_t)(ts % 1000000));
×
960
  }
961

962
  return buf;
×
963
}
964

965
int32_t taosFormatUtcTime(char* buf, int32_t bufLen, int64_t t, int32_t precision) {
19✔
966
  char      ts[40] = {0};
19✔
967
  struct tm ptm;
968

969
  int32_t fractionLen;
970
  char*   format = NULL;
19✔
971
  time_t  quot = 0;
19✔
972
  long    mod = 0;
19✔
973

974
  switch (precision) {
19!
975
    case TSDB_TIME_PRECISION_MILLI: {
13✔
976
      quot = t / 1000;
13✔
977
      fractionLen = 5;
13✔
978
      format = ".%03" PRId64;
13✔
979
      mod = t % 1000;
13✔
980
      break;
13✔
981
    }
982

983
    case TSDB_TIME_PRECISION_MICRO: {
3✔
984
      quot = t / 1000000;
3✔
985
      fractionLen = 8;
3✔
986
      format = ".%06" PRId64;
3✔
987
      mod = t % 1000000;
3✔
988
      break;
3✔
989
    }
990

991
    case TSDB_TIME_PRECISION_NANO: {
3✔
992
      quot = t / 1000000000;
3✔
993
      fractionLen = 11;
3✔
994
      format = ".%09" PRId64;
3✔
995
      mod = t % 1000000000;
3✔
996
      break;
3✔
997
    }
998

999
    default:
×
1000
      fractionLen = 0;
×
1001
      TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
1002
  }
1003

1004
  if (NULL == taosLocalTime(&quot, &ptm, buf, bufLen, NULL)) {
19!
1005
    TAOS_RETURN(TAOS_SYSTEM_ERROR(ERRNO));
×
1006
  }
1007
  int32_t length = (int32_t)taosStrfTime(ts, 40, "%Y-%m-%dT%H:%M:%S", &ptm);
19✔
1008
  length += tsnprintf(ts + length, fractionLen, format, mod);
19✔
1009
  length += (int32_t)taosStrfTime(ts + length, 40 - length, "%z", &ptm);
19✔
1010

1011
  tstrncpy(buf, ts, bufLen);
19✔
1012
  TAOS_RETURN(TSDB_CODE_SUCCESS);
19✔
1013
}
1014

1015
char* formatTimestampLocal(char* buf, int64_t val, int precision) {
251,283✔
1016
  time_t tt;
1017
  if (precision == TSDB_TIME_PRECISION_MICRO) {
251,283!
1018
    tt = (time_t)(val / 1000000);
×
1019
  } else if (precision == TSDB_TIME_PRECISION_NANO) {
251,283!
1020
    tt = (time_t)(val / 1000000000);
×
1021
  } else {
1022
    tt = (time_t)(val / 1000);
251,283✔
1023
  }
1024

1025
  struct tm tm;
1026
  if (taosLocalTime(&tt, &tm, NULL, 0, NULL) == NULL) {
251,283!
1027
    return NULL;
×
1028
  }
1029
  size_t pos = taosStrfTime(buf, 32, "%Y-%m-%d %H:%M:%S", &tm);
251,292✔
1030

1031
  if (precision == TSDB_TIME_PRECISION_MICRO) {
251,298!
1032
    sprintf(buf + pos, ".%06d", (int)(val % 1000000));
×
1033
  } else if (precision == TSDB_TIME_PRECISION_NANO) {
251,298!
1034
    sprintf(buf + pos, ".%09d", (int)(val % 1000000000));
×
1035
  } else {
1036
    sprintf(buf + pos, ".%03d", (int)(val % 1000));
251,298✔
1037
  }
1038

1039
  return buf;
251,298✔
1040
}
1041

1042
int32_t taosTs2Tm(int64_t ts, int32_t precision, struct STm* tm, timezone_t tz) {
818,591✔
1043
  tm->fsec = ts % TICK_PER_SECOND[precision] * (TICK_PER_SECOND[TSDB_TIME_PRECISION_NANO] / TICK_PER_SECOND[precision]);
818,591✔
1044
  time_t t = ts / TICK_PER_SECOND[precision];
818,591✔
1045
  if (NULL == taosLocalTime(&t, &tm->tm, NULL, 0, tz)) {
818,591!
1046
    TAOS_RETURN(TAOS_SYSTEM_ERROR(ERRNO));
×
1047
  }
1048
  return TSDB_CODE_SUCCESS;
818,593✔
1049
}
1050

1051
int32_t taosTm2Ts(struct STm* tm, int64_t* ts, int32_t precision, timezone_t tz) {
846✔
1052
  *ts = taosMktime(&tm->tm, tz);
846✔
1053
  *ts *= TICK_PER_SECOND[precision];
846✔
1054
  *ts += tm->fsec / (TICK_PER_SECOND[TSDB_TIME_PRECISION_NANO] / TICK_PER_SECOND[precision]);
846✔
1055
  return TSDB_CODE_SUCCESS;
846✔
1056
}
1057

1058
typedef struct {
1059
  const char* name;
1060
  int         len;
1061
  int         id;
1062
  bool        isDigit;
1063
} TSFormatKeyWord;
1064

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

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

1210
#define TS_FROMAT_KEYWORD_INDEX_SIZE ('z' - 'A' + 1)
1211
static const int TSFormatKeywordIndex[TS_FROMAT_KEYWORD_INDEX_SIZE] = {
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
    /*[ \ ] ^ _ `*/ -1, -1, -1, -1, -1, -1,
1223
    /*a*/ TSFKW_am,     -1, -1,
1224
    /*d*/ TSFKW_day,    -1, -1, -1,
1225
    /*h*/ TSFKW_hh24,   -1, -1, -1, -1,
1226
    /*m*/ TSFKW_mi,
1227
    /*n*/ TSFKW_ns,     -1,
1228
    /*p*/ TSFKW_pm,     -1, -1,
1229
    /*s*/ TSFKW_ss,
1230
    /*t*/ TSFKW_tzh,
1231
    /*u*/ TSFKW_us,     -1, -1, -1,
1232
    /*y*/ TSFKW_yyyy,   -1};
1233

1234
typedef struct {
1235
  uint8_t                type;
1236
  const char*            c;
1237
  int32_t                len;
1238
  const TSFormatKeyWord* key;
1239
} TSFormatNode;
1240

1241
static const char* const weekDays[] = {"Sunday",   "Monday", "Tuesday",  "Wednesday",
1242
                                       "Thursday", "Friday", "Saturday", "NULL"};
1243
static const char* const shortWeekDays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "NULL"};
1244
static const char* const fullMonths[] = {"January", "February",  "March",   "April",    "May",      "June", "July",
1245
                                         "August",  "September", "October", "November", "December", NULL};
1246
static const char* const months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
1247
                                     "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
1248
#define A_M_STR "A.M."
1249
#define a_m_str "a.m."
1250
#define AM_STR  "AM"
1251
#define am_str  "am"
1252
#define P_M_STR "P.M."
1253
#define p_m_str "p.m."
1254
#define PM_STR  "PM"
1255
#define pm_str  "pm"
1256
static const char* const apms[] = {AM_STR, PM_STR, am_str, pm_str, NULL};
1257
static const char* const long_apms[] = {A_M_STR, P_M_STR, a_m_str, p_m_str, NULL};
1258

1259
#define TS_FORMAT_NODE_TYPE_KEYWORD   1
1260
#define TS_FORMAT_NODE_TYPE_SEPARATOR 2
1261
#define TS_FORMAT_NODE_TYPE_CHAR      3
1262

1263
static const TSFormatKeyWord* keywordSearch(const char* str) {
14,770✔
1264
  if (*str < 'A' || *str > 'z' || (*str > 'Z' && *str < 'a')) return NULL;
14,770!
1265
  int32_t idx = TSFormatKeywordIndex[str[0] - 'A'];
8,518✔
1266
  if (idx < 0) return NULL;
8,518!
1267
  const TSFormatKeyWord* key = &formatKeyWords[idx++];
8,518✔
1268
  while (key->name && str[0] == key->name[0]) {
22,240✔
1269
    if (0 == strncmp(key->name, str, key->len)) {
21,843✔
1270
      return key;
8,121✔
1271
    }
1272
    key = &formatKeyWords[idx++];
13,722✔
1273
  }
1274
  return NULL;
397✔
1275
}
1276

1277
static bool isSeperatorChar(char c) {
9,405✔
1278
  return (c > 0x20 && c < 0x7F && !(c >= 'A' && c <= 'Z') && !(c >= 'a' && c <= 'z') && !(c >= '0' && c <= '9'));
9,405!
1279
}
1280

1281
static int32_t parseTsFormat(const char* formatStr, SArray* formats) {
1,478✔
1282
  TSFormatNode* lastOtherFormat = NULL;
1,478✔
1283
  while (*formatStr) {
16,249✔
1284
    const TSFormatKeyWord* key = keywordSearch(formatStr);
14,771✔
1285
    if (key) {
14,771✔
1286
      TSFormatNode format = {.key = key, .type = TS_FORMAT_NODE_TYPE_KEYWORD};
8,121✔
1287
      if (NULL == taosArrayPush(formats, &format)) TAOS_RETURN(terrno);
8,121!
1288
      formatStr += key->len;
8,121✔
1289
      lastOtherFormat = NULL;
8,121✔
1290
    } else {
1291
      if (*formatStr == '"') {
6,650✔
1292
        lastOtherFormat = NULL;
18✔
1293
        // for double quoted string
1294
        formatStr++;
18✔
1295
        TSFormatNode* last = NULL;
18✔
1296
        while (*formatStr) {
276✔
1297
          if (*formatStr == '"') {
273✔
1298
            formatStr++;
15✔
1299
            break;
15✔
1300
          }
1301
          if (*formatStr == '\\' && *(formatStr + 1)) {
258!
1302
            formatStr++;
×
1303
            last = NULL;  // stop expanding last format, create new format
×
1304
          }
1305
          if (last) {
258✔
1306
            // expand
1307
            last->len++;
240✔
1308
            formatStr++;
240✔
1309
          } else {
1310
            // create new
1311
            TSFormatNode format = {.type = TS_FORMAT_NODE_TYPE_CHAR, .key = NULL};
18✔
1312
            format.c = formatStr;
18✔
1313
            format.len = 1;
18✔
1314
            if (NULL == taosArrayPush(formats, &format)) TAOS_RETURN(terrno);
18!
1315
            formatStr++;
18✔
1316
            last = taosArrayGetLast(formats);
18✔
1317
          }
1318
        }
1319
      } else {
1320
        // for other strings
1321
        if (*formatStr == '\\' && *(formatStr + 1)) {
6,632!
1322
          formatStr++;
9✔
1323
          lastOtherFormat = NULL;  // stop expanding
9✔
1324
        } else {
1325
          if (lastOtherFormat && !isSeperatorChar(*formatStr)) {
6,623✔
1326
            // expanding
1327
          } else {
1328
            // create new
1329
            lastOtherFormat = NULL;
6,415✔
1330
          }
1331
        }
1332
        if (lastOtherFormat) {
6,632✔
1333
          lastOtherFormat->len++;
208✔
1334
          formatStr++;
208✔
1335
        } else {
1336
          TSFormatNode format = {
6,424✔
1337
              .type = isSeperatorChar(*formatStr) ? TS_FORMAT_NODE_TYPE_SEPARATOR : TS_FORMAT_NODE_TYPE_CHAR,
6,424✔
1338
              .key = NULL};
1339
          format.c = formatStr;
6,424✔
1340
          format.len = 1;
6,424✔
1341
          if (NULL == taosArrayPush(formats, &format)) TAOS_RETURN(terrno);
6,424!
1342
          formatStr++;
6,424✔
1343
          if (format.type == TS_FORMAT_NODE_TYPE_CHAR) lastOtherFormat = taosArrayGetLast(formats);
6,424✔
1344
        }
1345
      }
1346
    }
1347
  }
1348
  TAOS_RETURN(TSDB_CODE_SUCCESS);
1,478✔
1349
}
1350

1351
static int32_t tm2char(const SArray* formats, const struct STm* tm, char* s, int32_t outLen) {
5,546✔
1352
  int32_t     size = taosArrayGetSize(formats);
5,546✔
1353
  const char* start = s;
5,546✔
1354
  for (int32_t i = 0; i < size; ++i) {
61,403✔
1355
    TSFormatNode* format = taosArrayGet(formats, i);
55,857✔
1356
    if (format->type != TS_FORMAT_NODE_TYPE_KEYWORD) {
55,857✔
1357
      if (s - start + format->len + 1 > outLen) break;
24,354!
1358
      (void)strncpy(s, format->c, format->len);
24,354✔
1359
      s += format->len;
24,354✔
1360
      continue;
24,354✔
1361
    }
1362
    if (s - start + 16 > outLen) break;
31,503!
1363

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

1552
/// @brief find s in arr case insensitively
1553
/// @retval the index in arr if found, -1 if not found
1554
static int32_t strArrayCaseSearch(const char* const* arr, const char* s) {
882✔
1555
  if (!*s) return -1;
882!
1556
  const char* const* fmt = arr;
882✔
1557
  for (; *fmt; ++fmt) {
4,987✔
1558
    const char *l, *r;
1559
    for (l = fmt[0], r = s;; l++, r++) {
8,409✔
1560
      if (*l == '\0') return fmt - arr;
8,409✔
1561
      if (*r == '\0' || tolower(*l) != tolower(*r)) break;
7,530!
1562
    }
1563
  }
1564
  return -1;
3✔
1565
}
1566

1567
static const char* tsFormatStr2Int32(int32_t* dest, const char* str, int32_t len, bool needMoreDigit) {
3,568✔
1568
  char*       last;
1569
  int64_t     res;
1570
  const char* s = str;
3,568✔
1571
  if ('\0' == str[0]) return NULL;
3,568!
1572
  if (len <= 0) {
3,568✔
1573
    res = taosStr2Int64(s, &last, 10);
214✔
1574
    s = last;
214✔
1575
  } else {
1576
    char buf[16] = {0};
3,354✔
1577
    (void)strncpy(buf, s, len);
3,354✔
1578
    int32_t copiedLen = strlen(buf);
3,354✔
1579
    if (copiedLen < len) {
3,354✔
1580
      if (!needMoreDigit) {
12✔
1581
        // digits not enough, that's ok, cause we do not need more digits
1582
        // '2023-1' 'YYYY-MM'
1583
        // '202a' 'YYYY' -> 202
1584
        res = taosStr2Int64(s, &last, 10);
9✔
1585
        s += copiedLen;
9✔
1586
      } else {
1587
        // bytes not enough, and there are other digit formats to match
1588
        // '2023-1' 'YYYY-MMDD'
1589
        return NULL;
9✔
1590
      }
1591
    } else {
1592
      if (needMoreDigit) {
3,342✔
1593
        res = taosStr2Int64(buf, &last, 10);
42✔
1594
        // bytes enough, but digits not enough, like '202a12' 'YYYYMM', YYYY needs four digits
1595
        if (last - buf < len) return NULL;
42✔
1596
        s += last - buf;
36✔
1597
      } else {
1598
        res = taosStr2Int64(s, &last, 10);
3,300✔
1599
        s = last;
3,300✔
1600
      }
1601
    }
1602
  }
1603
  if (s == str) {
3,559✔
1604
    // no integers found
1605
    return NULL;
3✔
1606
  }
1607
  if (ERRNO == ERANGE || res > INT32_MAX || res < INT32_MIN) {
3,556!
1608
    // out of range
1609
    return NULL;
3✔
1610
  }
1611
  *dest = res;
3,553✔
1612
  return s;
3,553✔
1613
}
1614

1615
static int32_t adjustYearTo2020(int32_t year) {
204✔
1616
  if (year < 70) return year + 2000;    // 2000 - 2069
204✔
1617
  if (year < 100) return year + 1900;   // 1970 - 1999
9!
1618
  if (year < 520) return year + 2000;   // 2100 - 2519
9!
1619
  if (year < 1000) return year + 1000;  // 1520 - 1999
×
1620
  return year;
×
1621
}
1622

1623
static bool checkTm(const struct tm* tm) {
834✔
1624
  if (tm->tm_mon < 0 || tm->tm_mon > 11) return false;
834!
1625
  if (tm->tm_wday < 0 || tm->tm_wday > 6) return false;
831!
1626
  if (tm->tm_yday < 0 || tm->tm_yday > 365) return false;
831!
1627
  if (tm->tm_mday < 0 || tm->tm_mday > 31) return false;
831!
1628
  if (tm->tm_hour < 0 || tm->tm_hour > 23) return false;
828!
1629
  if (tm->tm_min < 0 || tm->tm_min > 59) return false;
828!
1630
  if (tm->tm_sec < 0 || tm->tm_sec > 60) return false;
828!
1631
  return true;
828✔
1632
}
1633

1634
static bool needMoreDigits(SArray* formats, int32_t curIdx) {
3,568✔
1635
  if (curIdx == taosArrayGetSize(formats) - 1) return false;
3,568✔
1636
  TSFormatNode* pNextNode = taosArrayGet(formats, curIdx + 1);
3,116✔
1637
  if (pNextNode->type == TS_FORMAT_NODE_TYPE_SEPARATOR) {
3,116✔
1638
    return false;
2,179✔
1639
  } else if (pNextNode->type == TS_FORMAT_NODE_TYPE_KEYWORD) {
937✔
1640
    return pNextNode->key->isDigit;
258✔
1641
  } else {
1642
    return isdigit(pNextNode->c[0]);
679✔
1643
  }
1644
}
1645

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

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

1960
int32_t taosTs2Char(const char* format, SArray** formats, int64_t ts, int32_t precision, char* out, int32_t outLen, timezone_t tz) {
5,495✔
1961
  if (!*formats) {
5,495✔
1962
    *formats = taosArrayInit(8, sizeof(TSFormatNode));
910✔
1963
    if (!*formats) {
910!
1964
      TAOS_RETURN(terrno);
×
1965
    }
1966
    TAOS_CHECK_RETURN(parseTsFormat(format, *formats));
910!
1967
  }
1968
  struct STm tm;
1969
  TAOS_CHECK_RETURN(taosTs2Tm(ts, precision, &tm, tz));
5,495!
1970
  return tm2char(*formats, &tm, out, outLen);
5,498✔
1971
}
1972

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

2000
int32_t TEST_ts2char(const char* format, int64_t ts, int32_t precision, char* out, int32_t outLen) {
48✔
2001
  int32_t code = TSDB_CODE_SUCCESS;
48✔
2002

2003
  SArray* formats = taosArrayInit(4, sizeof(TSFormatNode));
48✔
2004
  if (!formats) {
48!
2005
    TAOS_RETURN(terrno);
×
2006
  }
2007
  TAOS_CHECK_RETURN(parseTsFormat(format, formats));
48!
2008
  struct STm tm;
2009
  TAOS_CHECK_GOTO(taosTs2Tm(ts, precision, &tm, NULL), NULL, _exit);
48!
2010
  TAOS_CHECK_GOTO(tm2char(formats, &tm, out, outLen), NULL, _exit);
48!
2011

2012
_exit:
48✔
2013
  taosArrayDestroy(formats);
48✔
2014
  TAOS_RETURN(TSDB_CODE_SUCCESS);
48✔
2015
}
2016

2017
int32_t TEST_char2ts(const char* format, int64_t* ts, int32_t precision, const char* tsStr) {
114✔
2018
  const char* sErrPos;
2019
  int32_t     fErrIdx;
2020
  SArray*     formats = taosArrayInit(4, sizeof(TSFormatNode));
114✔
2021
  TAOS_CHECK_RETURN(parseTsFormat(format, formats));
114!
2022
  int32_t code = char2ts(tsStr, formats, ts, precision, &sErrPos, &fErrIdx, NULL);
114✔
2023
  if (code == -1) {
114✔
2024
    (void)printf("failed position: %s\n", sErrPos);
21✔
2025
    (void)printf("failed format: %s\n", ((TSFormatNode*)taosArrayGet(formats, fErrIdx))->key->name);
21✔
2026
  }
2027
  taosArrayDestroy(formats);
114✔
2028
  return code;
114✔
2029
}
2030

2031
static int8_t UNIT_INDEX[26] = {/*a*/ 2,  0,  -1, 6,  -1, -1, -1,
2032
                                /*h*/ 5,  -1, -1, -1, -1, 4,  8,
2033
                                /*o*/ -1, -1, -1, -1, 3,  -1,
2034
                                /*u*/ 1,  -1, 7,  -1, 9,  -1};
2035

2036
#define GET_UNIT_INDEX(idx) UNIT_INDEX[(idx)-97]
2037

2038
// clang-format off
2039
static int64_t UNIT_MATRIX[10][11] = {     /*  ns,   us,   ms,    s,   min,   h,   d,   w, month, y*/
2040
                                /*ns*/ {   1, 1000,    0},
2041
                                /*us*/ {1000,    1, 1000,    0},
2042
                                /*ms*/ {   0, 1000,    1, 1000,    0},
2043
                                 /*s*/ {   0,    0, 1000,    1,   60,    0},
2044
                               /*min*/ {   0,    0,    0,   60,    1,   60,   0},
2045
                                 /*h*/ {   0,    0,    0,    0,   60,    1,   1,   0},
2046
                                 /*d*/ {   0,    0,    0,    0,    0,   24,   1,   7,   1,   0},
2047
                                 /*w*/ {   0,    0,    0,    0,    0,    0,   7,   1,  -1,   0},
2048
                               /*mon*/ {   0,    0,    0,    0,    0,    0,   0,   0,   1,  12,  0},
2049
                                 /*y*/ {   0,    0,    0,    0,    0,    0,   0,   0,   12,   1,  0}};
2050
// clang-format on
2051

2052
static bool recursiveTsmaCheckRecursive(int64_t baseInterval, int8_t baseIdx, int64_t interval, int8_t idx,
264✔
2053
                                        bool checkEq) {
2054
  if (UNIT_MATRIX[baseIdx][idx] == -1) return false;
264✔
2055
  if (baseIdx == idx) {
249✔
2056
    if (interval < baseInterval) return false;
87!
2057
    if (checkEq && interval == baseInterval) return false;
87✔
2058
    return interval % baseInterval == 0;
72✔
2059
  }
2060
  int8_t  next = baseIdx + 1;
162✔
2061
  int64_t val = UNIT_MATRIX[baseIdx][next];
162✔
2062
  while (val != 0 && next <= idx) {
192!
2063
    if (val == -1) {
174✔
2064
      next++;
6✔
2065
      val = UNIT_MATRIX[baseIdx][next];
6✔
2066
      continue;
6✔
2067
    }
2068
    if (val % baseInterval == 0 || baseInterval % val == 0) {
168✔
2069
      int8_t extra = baseInterval >= val ? 0 : 1;
135✔
2070
      bool   needCheckEq = baseInterval >= val && !(baseIdx < next && val == 1);
135!
2071
      if (!recursiveTsmaCheckRecursive(baseInterval / val + extra, next, interval, idx, needCheckEq && checkEq)) {
135!
2072
        next++;
24✔
2073
        val = UNIT_MATRIX[baseIdx][next];
24✔
2074
        continue;
24✔
2075
      } else {
2076
        return true;
111✔
2077
      }
2078
    } else {
2079
      return false;
33✔
2080
    }
2081
  }
2082
  return false;
18✔
2083
}
2084

2085
static bool recursiveTsmaCheckRecursiveReverse(int64_t baseInterval, int8_t baseIdx, int64_t interval, int8_t idx,
105✔
2086
                                               bool checkEq) {
2087
  if (UNIT_MATRIX[baseIdx][idx] == -1) return false;
105!
2088

2089
  if (baseIdx == idx) {
105✔
2090
    if (interval < baseInterval) return false;
39✔
2091
    if (checkEq && interval == baseInterval) return false;
33!
2092
    return interval % baseInterval == 0;
24✔
2093
  }
2094

2095
  int8_t  next = baseIdx - 1;
66✔
2096
  int64_t val = UNIT_MATRIX[baseIdx][next];
66✔
2097
  while (val != 0 && next >= 0) {
66!
2098
    return recursiveTsmaCheckRecursiveReverse(baseInterval * val, next, interval, idx, checkEq);
60✔
2099
  }
2100
  return false;
6✔
2101
}
2102

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

2128
  bool needCheckEq = baseIsCalendarDuration == isCalendarDuration && checkEq;
174!
2129

2130
  int8_t baseIdx = GET_UNIT_INDEX(baseUnit), idx = GET_UNIT_INDEX(unit);
174✔
2131
  if (baseIdx <= idx) {
174✔
2132
    return recursiveTsmaCheckRecursive(baseInterval, baseIdx, interval, idx, needCheckEq);
129✔
2133
  } else {
2134
    return recursiveTsmaCheckRecursiveReverse(baseInterval, baseIdx, interval, idx, checkEq);
45✔
2135
  }
2136
  return true;
2137
}
2138

2139
int64_t taosGetTimestampToday(int32_t precision, timezone_t tz) {
8,714✔
2140
  int64_t   factor =  (precision == TSDB_TIME_PRECISION_SECONDS) ? 1
8,714✔
2141
                    : (precision == TSDB_TIME_PRECISION_MILLI)   ? 1000
8,714!
2142
                    : (precision == TSDB_TIME_PRECISION_MICRO)   ? 1000000
2143
                    : 1000000000;
2144
  time_t    t;
2145
  int32_t code = taosTime(&t);
8,714✔
2146
  if (code != 0) {
8,714!
2147
    return -1;
×
2148
  }
2149
  struct tm tm;
2150
  if (taosLocalTime(&t, &tm, NULL, 0,  tz) == NULL){
8,714!
2151
    uError("%s failed to get local time, code:%d", __FUNCTION__, ERRNO);
×
2152
    return t;
×
2153
  }
2154
  tm.tm_hour = 0;
8,714✔
2155
  tm.tm_min = 0;
8,714✔
2156
  tm.tm_sec = 0;
8,714✔
2157

2158
  time_t tmp = taosMktime(&tm, tz);
8,714✔
2159
  if (tmp == (time_t)-1) {
8,714!
2160
    uError("%s failed to get timestamp of today, code:%d", __FUNCTION__, ERRNO);
×
2161
    return t;
×
2162
  }
2163
  return (int64_t)tmp * factor;
8,714✔
2164
}
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

© 2025 Coveralls, Inc