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

taosdata / TDengine / #3543

29 Nov 2024 02:58AM UTC coverage: 60.842% (+0.02%) from 60.819%
#3543

push

travis-ci

web-flow
Merge pull request #28973 from taosdata/merge/mainto3.0

merge: from main to 3.0

120460 of 253224 branches covered (47.57%)

Branch coverage included in aggregate %.

706 of 908 new or added lines in 18 files covered. (77.75%)

2401 existing lines in 137 files now uncovered.

201633 of 276172 relevant lines covered (73.01%)

19045673.23 hits per line

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

76.79
/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
// ==== mktime() kernel code =================//
29
static int64_t m_deltaUtc = 0;
30

31
void deltaToUtcInitOnce() {
4,109✔
32
  struct tm tm = {0};
4,109✔
33
  if (taosStrpTime("1970-01-01 00:00:00", (const char*)("%Y-%m-%d %H:%M:%S"), &tm) == NULL) {
4,109!
34
    uError("failed to parse time string");
×
35
  }
36
  m_deltaUtc = (int64_t)taosMktime(&tm);
4,109✔
37
  // printf("====delta:%lld\n\n", seconds);
38
}
4,109✔
39

40
static int32_t parseFraction(char* str, char** end, int32_t timePrec, int64_t* pFraction);
41
static int32_t parseTimeWithTz(const char* timestr, int64_t* time, int32_t timePrec, char delim);
42
static int32_t parseLocaltime(char* timestr, int32_t len, int64_t* utime, int32_t timePrec, char delim);
43
static int32_t parseLocaltimeDst(char* timestr, int32_t len, int64_t* utime, int32_t timePrec, char delim);
44
static char*   forwardToTimeStringEnd(char* str);
45
static bool    checkTzPresent(const char* str, int32_t len);
46
static int32_t parseTimezone(char* str, int64_t* tzOffset);
47

48
static int32_t (*parseLocaltimeFp[])(char* timestr, int32_t len, int64_t* utime, int32_t timePrec, char delim) = {
49
    parseLocaltime, parseLocaltimeDst};
50

51
int32_t taosParseTime(const char* timestr, int64_t* utime, int32_t len, int32_t timePrec, int8_t day_light) {
1,249,135✔
52
  /* parse datatime string in with tz */
53
  if (strnchr(timestr, 'T', len, false) != NULL) {
1,249,135✔
54
    if (checkTzPresent(timestr, len)) {
305,822✔
55
      return parseTimeWithTz(timestr, utime, timePrec, 'T');
141,844✔
56
    } else {
57
      return parseLocaltimeDst((char*)timestr, len, utime, timePrec, 'T');
163,974✔
58
    }
59
  } else {
60
    if (checkTzPresent(timestr, len)) {
942,063✔
61
      return parseTimeWithTz(timestr, utime, timePrec, 0);
54✔
62
    } else {
63
      return parseLocaltimeDst((char*)timestr, len, utime, timePrec, 0);
943,436✔
64
    }
65
  }
66
}
67

68
bool checkTzPresent(const char* str, int32_t len) {
1,247,908✔
69
  char*   seg = forwardToTimeStringEnd((char*)str);
1,247,908✔
70
  int32_t seg_len = len - (int32_t)(seg - str);
1,248,171✔
71

72
  char* c = &seg[seg_len - 1];
1,248,171✔
73
  for (int32_t i = 0; i < seg_len; ++i) {
2,692,974✔
74
    if (*c == 'Z' || *c == 'z' || *c == '+' || *c == '-') {
1,586,702!
75
      return true;
141,899✔
76
    }
77
    c--;
1,444,803✔
78
  }
79

80
  return false;
1,106,272✔
81
}
82

83
char* forwardToTimeStringEnd(char* str) {
1,369,441✔
84
  int32_t i = 0;
1,369,441✔
85
  int32_t numOfSep = 0;
1,369,441✔
86

87
  while (str[i] != 0 && numOfSep < 2) {
28,613,591✔
88
    if (str[i++] == ':') {
27,244,150✔
89
      numOfSep++;
1,316,201✔
90
    }
91
  }
92

93
  while (str[i] >= '0' && str[i] <= '9') {
2,685,495✔
94
    i++;
1,316,054✔
95
  }
96

97
  return &str[i];
1,369,441✔
98
}
99

100
int32_t parseFraction(char* str, char** end, int32_t timePrec, int64_t* pFraction) {
349,784✔
101
  int32_t i = 0;
349,784✔
102
  int64_t fraction = 0;
349,784✔
103

104
  const int32_t MILLI_SEC_FRACTION_LEN = 3;
349,784✔
105
  const int32_t MICRO_SEC_FRACTION_LEN = 6;
349,784✔
106
  const int32_t NANO_SEC_FRACTION_LEN = 9;
349,784✔
107

108
  int32_t factor[9] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};
349,784✔
109
  int32_t times = 1;
349,784✔
110

111
  while (str[i] >= '0' && str[i] <= '9') {
1,311,221✔
112
    i++;
961,437✔
113
  }
114

115
  int32_t totalLen = i;
349,784✔
116
  if (totalLen <= 0) {
349,784!
117
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
118
  }
119

120
  /* parse the fraction */
121
  if (timePrec == TSDB_TIME_PRECISION_MILLI) {
349,784✔
122
    /* only use the initial 3 bits */
123
    if (i >= MILLI_SEC_FRACTION_LEN) {
239,178✔
124
      i = MILLI_SEC_FRACTION_LEN;
132,912✔
125
    }
126

127
    times = MILLI_SEC_FRACTION_LEN - i;
239,178✔
128
  } else if (timePrec == TSDB_TIME_PRECISION_MICRO) {
110,606✔
129
    if (i >= MICRO_SEC_FRACTION_LEN) {
179✔
130
      i = MICRO_SEC_FRACTION_LEN;
108✔
131
    }
132
    times = MICRO_SEC_FRACTION_LEN - i;
179✔
133
  } else if (timePrec == TSDB_TIME_PRECISION_NANO) {
110,427!
134
    if (i >= NANO_SEC_FRACTION_LEN) {
110,427✔
135
      i = NANO_SEC_FRACTION_LEN;
52✔
136
    }
137
    times = NANO_SEC_FRACTION_LEN - i;
110,427✔
138
  } else {
139
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
140
  }
141

142
  fraction = strnatoi(str, i) * factor[times];
349,784✔
143
  *end = str + totalLen;
349,784✔
144
  *pFraction = fraction;
349,784✔
145

146
  TAOS_RETURN(TSDB_CODE_SUCCESS);
349,784✔
147
}
148

149
int32_t parseTimezone(char* str, int64_t* tzOffset) {
3,014,046✔
150
  int64_t hour = 0;
3,014,046✔
151

152
  int32_t i = 0;
3,014,046✔
153
  if (str[i] != '+' && str[i] != '-') {
3,014,046!
154
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
155
  }
156

157
  i++;
3,014,046✔
158

159
  int32_t j = i;
3,014,046✔
160
  while (str[j]) {
15,080,149✔
161
    if ((str[j] >= '0' && str[j] <= '9') || str[j] == ':') {
12,066,106!
162
      ++j;
12,066,103✔
163
      continue;
12,066,103✔
164
    }
165

166
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
3✔
167
  }
168

169
  char* sep = strchr(&str[i], ':');
3,014,043✔
170
  if (sep != NULL) {
3,014,043✔
171
    int32_t len = (int32_t)(sep - &str[i]);
10,488✔
172

173
    hour = strnatoi(&str[i], len);
10,488✔
174
    i += len + 1;
10,488✔
175
  } else {
176
    hour = strnatoi(&str[i], 2);
3,003,555✔
177
    i += 2;
3,002,928✔
178
  }
179

180
  if (hour > 12 || hour < 0) {
3,013,416✔
181
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
48✔
182
  }
183

184
  // return error if there're illegal charaters after min(2 Digits)
185
  char* minStr = &str[i];
3,013,368✔
186
  if (minStr[1] != '\0' && minStr[2] != '\0') {
3,013,368!
187
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
188
  }
189

190
  int64_t minute = strnatoi(&str[i], 2);
3,013,368✔
191
  if (minute > 59 || (hour == 12 && minute > 0)) {
3,012,841!
192
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
193
  }
194

195
  if (str[0] == '+') {
3,012,880✔
196
    *tzOffset = -(hour * 3600 + minute * 60);
3,012,800✔
197
  } else {
198
    *tzOffset = hour * 3600 + minute * 60;
80✔
199
  }
200

201
  TAOS_RETURN(TSDB_CODE_SUCCESS);
3,012,880✔
202
}
203

204
int32_t offsetOfTimezone(char* tzStr, int64_t* offset) {
2,893,229✔
205
  if (tzStr && (tzStr[0] == 'z' || tzStr[0] == 'Z')) {
2,893,229!
206
    *offset = 0;
10✔
207
    return TSDB_CODE_SUCCESS;
10✔
208
  }
209
  return parseTimezone(tzStr, offset);
2,893,219✔
210
}
211

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

228
  struct tm tm = {0};
141,899✔
229

230
  char* str;
231
  if (delim == 'T') {
141,899✔
232
    str = taosStrpTime(timestr, "%Y-%m-%dT%H:%M:%S", &tm);
141,844✔
233
  } else if (delim == 0) {
55✔
234
    str = taosStrpTime(timestr, "%Y-%m-%d %H:%M:%S", &tm);
54✔
235
  } else {
236
    str = NULL;
1✔
237
  }
238

239
  if (str == NULL) {
141,897✔
240
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
20,352✔
241
  }
242

243
/* mktime will be affected by TZ, set by using taos_options */
244
#ifdef WINDOWS
245
  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);
246
  // int64_t seconds = gmtime(&tm);
247
#else
248
  int64_t seconds = timegm(&tm);
121,545✔
249
#endif
250

251
  int64_t fraction = 0;
121,547✔
252
  str = forwardToTimeStringEnd((char*)timestr);
121,547✔
253

254
  if ((str[0] == 'Z' || str[0] == 'z') && str[1] == '\0') {
121,547!
255
    /* utc time, no millisecond, return directly*/
256
    *time = seconds * factor;
710✔
257
  } else if (str[0] == '.') {
120,837✔
258
    str += 1;
110,447✔
259
    TAOS_CHECK_RETURN(parseFraction(str, &str, timePrec, &fraction));
110,447!
260

261
    *time = seconds * factor + fraction;
110,447✔
262

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

272
      *time += tzOffset * factor;
110,416✔
273
    }
274

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

278
    // parse the timezone
279
    TAOS_CHECK_RETURN(parseTimezone(str, &tzOffset));
10,390!
280

281
    *time += tzOffset * factor;
10,390✔
282
  } else {
283
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
284
  }
285

286
  TAOS_RETURN(TSDB_CODE_SUCCESS);
121,520✔
287
}
288

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

294
  int32_t dayOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
394,832✔
295

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

300
  if (isLeapYear && (pTm->tm_mon == 1)) {
394,832!
301
    if (pTm->tm_mday > leapYearMonthDay) {
6,384!
302
      return false;
16✔
303
    }
304
  } else {
305
    if (pTm->tm_mday > dayOfMonth[pTm->tm_mon]) {
388,448!
306
      return false;
×
307
    }
308
  }
309

310
  return true;
394,816✔
311
}
312

313
int32_t parseLocaltime(char* timestr, int32_t len, int64_t* utime, int32_t timePrec, char delim) {
×
314
  *utime = 0;
×
315
  struct tm tm = {0};
×
316

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

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

334
#ifdef _MSC_VER
335
#if _MSC_VER >= 1900
336
  int64_t timezone = _timezone;
337
#endif
338
#endif
339

340
  int64_t seconds =
341
      user_mktime64(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, timezone);
×
342

343
  int64_t fraction = 0;
×
344

345
  if (*str == '.') {
×
346
    /* parse the second fraction part */
347
    TAOS_CHECK_RETURN(parseFraction(str + 1, &str, timePrec, &fraction));
×
348
  }
349

350
  *utime = TSDB_TICK_PER_SECOND(timePrec) * seconds + fraction;
×
351
  TAOS_RETURN(TSDB_CODE_SUCCESS);
×
352
}
353

354
int32_t parseLocaltimeDst(char* timestr, int32_t len, int64_t* utime, int32_t timePrec, char delim) {
1,106,450✔
355
  *utime = 0;
1,106,450✔
356
  struct tm tm = {0};
1,106,450✔
357
  tm.tm_isdst = -1;
1,106,450✔
358

359
  char* str;
360
  if (delim == 'T') {
1,106,450✔
361
    str = taosStrpTime(timestr, "%Y-%m-%dT%H:%M:%S", &tm);
163,988✔
362
  } else if (delim == 0) {
942,462!
363
    str = taosStrpTime(timestr, "%Y-%m-%d %H:%M:%S", &tm);
943,513✔
364
  } else {
365
    str = NULL;
×
366
  }
367

368
  if (str == NULL || (((str - timestr) < len) && (*str != '.')) || !validateTm(&tm)) {
1,501,168!
369
    // if parse failed, try "%Y-%m-%d" format
370
    str = taosStrpTime(timestr, "%Y-%m-%d", &tm);
712,056✔
371
    if (str == NULL || (((str - timestr) < len) && (*str != '.')) || !validateTm(&tm)) {
712,342!
372
      TAOS_RETURN(TSDB_CODE_INVALID_PARA);
711,806✔
373
    }
374
  }
375

376
  /* mktime will be affected by TZ, set by using taos_options */
377
  int64_t seconds = taosMktime(&tm);
394,816✔
378

379
  int64_t fraction = 0;
394,820✔
380
  if (*str == '.') {
394,820✔
381
    /* parse the second fraction part */
382
    TAOS_CHECK_RETURN(parseFraction(str + 1, &str, timePrec, &fraction));
239,337!
383
  }
384

385
  *utime = TSDB_TICK_PER_SECOND(timePrec) * seconds + fraction;
394,820✔
386
  TAOS_RETURN(TSDB_CODE_SUCCESS);
394,820✔
387
}
388

389
char getPrecisionUnit(int32_t precision) {
23,353✔
390
  static char units[3] = {TIME_UNIT_MILLISECOND, TIME_UNIT_MICROSECOND, TIME_UNIT_NANOSECOND};
391
  switch (precision) {
23,353!
392
    case TSDB_TIME_PRECISION_MILLI:
23,353✔
393
    case TSDB_TIME_PRECISION_MICRO:
394
    case TSDB_TIME_PRECISION_NANO:
395
      return units[precision];
23,353✔
396
    default:
×
397
      return 0;
×
398
  }
399
}
400

401
int64_t convertTimePrecision(int64_t utime, int32_t fromPrecision, int32_t toPrecision) {
10,328,928✔
402
  switch (fromPrecision) {
10,328,928!
403
    case TSDB_TIME_PRECISION_MILLI: {
10,322,533✔
404
      switch (toPrecision) {
10,322,533!
405
        case TSDB_TIME_PRECISION_MILLI:
10,315,600✔
406
          return utime;
10,315,600✔
407
        case TSDB_TIME_PRECISION_MICRO:
2,644✔
408
          if (utime > INT64_MAX / 1000) {
2,644!
409
            return INT64_MAX;
×
410
          }
411
          return utime * 1000;
2,644✔
412
        case TSDB_TIME_PRECISION_NANO:
4,290✔
413
          if (utime > INT64_MAX / 1000000) {
4,290!
414
            return INT64_MAX;
×
415
          }
416
          return utime * 1000000;
4,290✔
417
        default:
×
418
          return utime;
×
419
      }
420
    }  // end from milli
421
    case TSDB_TIME_PRECISION_MICRO: {
1,228✔
422
      switch (toPrecision) {
1,228!
423
        case TSDB_TIME_PRECISION_MILLI:
382✔
424
          return utime / 1000;
382✔
425
        case TSDB_TIME_PRECISION_MICRO:
427✔
426
          return utime;
427✔
427
        case TSDB_TIME_PRECISION_NANO:
419✔
428
          if (utime > INT64_MAX / 1000) {
419!
429
            return INT64_MAX;
×
430
          }
431
          return utime * 1000;
419✔
432
        default:
×
433
          return utime;
×
434
      }
435
    }  // end from micro
436
    case TSDB_TIME_PRECISION_NANO: {
5,169✔
437
      switch (toPrecision) {
5,169!
438
        case TSDB_TIME_PRECISION_MILLI:
2,822✔
439
          return utime / 1000000;
2,822✔
440
        case TSDB_TIME_PRECISION_MICRO:
147✔
441
          return utime / 1000;
147✔
442
        case TSDB_TIME_PRECISION_NANO:
2,200✔
443
          return utime;
2,200✔
444
        default:
×
445
          return utime;
×
446
      }
447
    }  // end from nano
448
    default: {
×
449
      return utime;  // only to pass windows compilation
×
450
    }
451
  }  // end switch fromPrecision
452

453
  return utime;
454
}
455

456
// !!!!notice:there are precision problems, double lose precison if time is too large, for example:
457
// 1626006833631000000*1.0 = double = 1626006833631000064
458
// int64_t convertTimePrecision(int64_t time, int32_t fromPrecision, int32_t toPrecision) {
459
//  static double factors[3][3] = {{1., 1000., 1000000.}, {1.0 / 1000, 1., 1000.}, {1.0 / 1000000, 1.0 / 1000, 1.}};
460
//  ((double)time * factors[fromPrecision][toPrecision]);
461
//}
462

463
// !!!!notice: double lose precison if time is too large, for example: 1626006833631000000*1.0 = double =
464
// 1626006833631000064
465
int32_t convertTimeFromPrecisionToUnit(int64_t time, int32_t fromPrecision, char toUnit, int64_t* pRes) {
84,167✔
466
  if (fromPrecision != TSDB_TIME_PRECISION_MILLI && fromPrecision != TSDB_TIME_PRECISION_MICRO &&
84,167!
467
      fromPrecision != TSDB_TIME_PRECISION_NANO) {
468
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
469
  }
470

471
  int64_t factors[3] = {NANOSECOND_PER_MSEC, NANOSECOND_PER_USEC, 1};
84,167✔
472
  double  tmp = time;
84,167✔
473
  switch (toUnit) {
84,167!
474
    case 's': {
19,106✔
475
      time /= (NANOSECOND_PER_SEC / factors[fromPrecision]);
19,106✔
476
      tmp = (double)time;
19,106✔
477
      break;
19,106✔
478
    }
479
    case 'm':
38,567✔
480
      time /= (NANOSECOND_PER_MINUTE / factors[fromPrecision]);
38,567✔
481
      tmp = (double)time;
38,567✔
482
      break;
38,567✔
483
    case 'h':
12,146✔
484
      time /= (NANOSECOND_PER_HOUR / factors[fromPrecision]);
12,146✔
485
      tmp = (double)time;
12,146✔
486
      break;
12,146✔
487
    case 'd':
5,179✔
488
      time /= (NANOSECOND_PER_DAY / factors[fromPrecision]);
5,179✔
489
      tmp = (double)time;
5,179✔
490
      break;
5,179✔
491
    case 'w':
444✔
492
      time /= (NANOSECOND_PER_WEEK / factors[fromPrecision]);
444✔
493
      tmp = (double)time;
444✔
494
      break;
444✔
495
    case 'a':
8,629✔
496
      time /= (NANOSECOND_PER_MSEC / factors[fromPrecision]);
8,629✔
497
      tmp = (double)time;
8,629✔
498
      break;
8,629✔
499
    case 'u':
72!
500
      // the result of (NANOSECOND_PER_USEC/(double)factors[fromPrecision]) maybe a double
501
      switch (fromPrecision) {
502
        case TSDB_TIME_PRECISION_MILLI: {
×
503
          tmp *= 1000;
×
504
          time *= 1000;
×
505
          break;
×
506
        }
507
        case TSDB_TIME_PRECISION_MICRO: {
48✔
508
          time /= 1;
48✔
509
          tmp = (double)time;
48✔
510
          break;
48✔
511
        }
512
        case TSDB_TIME_PRECISION_NANO: {
24✔
513
          time /= 1000;
24✔
514
          tmp = (double)time;
24✔
515
          break;
24✔
516
        }
517
      }
518
      break;
72✔
519
    case 'b':
24✔
520
      tmp *= factors[fromPrecision];
24✔
521
      time *= factors[fromPrecision];
24✔
522
      break;
24✔
523
    default: {
×
524
      TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
525
    }
526
  }
527
  if (tmp >= (double)INT64_MAX) {
84,167!
528
    *pRes = INT64_MAX;
×
529
  } else if (tmp <= (double)INT64_MIN) {
84,167!
530
    *pRes = INT64_MIN;
×
531
  } else {
532
    *pRes = time;
84,167✔
533
  }
534

535
  TAOS_RETURN(TSDB_CODE_SUCCESS);
84,167✔
536
}
537

538
int32_t convertStringToTimestamp(int16_t type, char* inputData, int64_t timePrec, int64_t* timeVal) {
778,162✔
539
  int32_t charLen = varDataLen(inputData);
778,162✔
540
  char*   newColData;
541
  if (type == TSDB_DATA_TYPE_BINARY || type == TSDB_DATA_TYPE_VARBINARY) {
971,260✔
542
    newColData = taosMemoryCalloc(1, charLen + 1);
429,345✔
543
    if (NULL == newColData) {
430,297!
544
      TAOS_RETURN(terrno);
×
545
    }
546
    (void)memcpy(newColData, varDataVal(inputData), charLen);
430,297✔
547
    int32_t ret = taosParseTime(newColData, timeVal, charLen, (int32_t)timePrec, tsDaylight);
430,297✔
548
    if (ret != TSDB_CODE_SUCCESS) {
430,202✔
549
      taosMemoryFree(newColData);
237,104✔
550
      TAOS_RETURN(TSDB_CODE_INVALID_TIMESTAMP);
237,211✔
551
    }
552
    taosMemoryFree(newColData);
193,098✔
553
  } else if (type == TSDB_DATA_TYPE_NCHAR) {
348,817✔
554
    newColData = taosMemoryCalloc(1, charLen + TSDB_NCHAR_SIZE);
348,808✔
555
    if (NULL == newColData) {
349,629!
556
      TAOS_RETURN(terrno);
×
557
    }
558
    int len = taosUcs4ToMbs((TdUcs4*)varDataVal(inputData), charLen, newColData);
349,629✔
559
    if (len < 0) {
349,910!
560
      taosMemoryFree(newColData);
×
561
      TAOS_RETURN(TSDB_CODE_FAILED);
×
562
    }
563
    newColData[len] = 0;
349,910✔
564
    int32_t ret = taosParseTime(newColData, timeVal, len, (int32_t)timePrec, tsDaylight);
349,910✔
565
    if (ret != TSDB_CODE_SUCCESS) {
348,247✔
566
      taosMemoryFree(newColData);
348,245✔
567
      TAOS_RETURN(ret);
349,879✔
568
    }
569
    taosMemoryFree(newColData);
2✔
570
  } else {
571
    TAOS_RETURN(TSDB_CODE_FAILED);
9✔
572
  }
573
  TAOS_RETURN(TSDB_CODE_SUCCESS);
193,098✔
574
}
575

576
int32_t getDuration(int64_t val, char unit, int64_t* result, int32_t timePrecision) {
10,313,775✔
577
  switch (unit) {
10,313,775!
578
    case 's':
10,194,744✔
579
      if (val > INT64_MAX / MILLISECOND_PER_SECOND) {
10,194,744!
580
        TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
581
      }
582
      (*result) = convertTimePrecision(val * MILLISECOND_PER_SECOND, TSDB_TIME_PRECISION_MILLI, timePrecision);
10,194,744✔
583
      break;
10,194,744✔
584
    case 'm':
37,912✔
585
      if (val > INT64_MAX / MILLISECOND_PER_MINUTE) {
37,912!
586
        TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
587
      }
588
      (*result) = convertTimePrecision(val * MILLISECOND_PER_MINUTE, TSDB_TIME_PRECISION_MILLI, timePrecision);
37,912✔
589
      break;
37,912✔
590
    case 'h':
20,304✔
591
      if (val > INT64_MAX / MILLISECOND_PER_MINUTE) {
20,304!
592
        TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
593
      }
594
      (*result) = convertTimePrecision(val * MILLISECOND_PER_HOUR, TSDB_TIME_PRECISION_MILLI, timePrecision);
20,304✔
595
      break;
20,304✔
596
    case 'd':
24,445✔
597
      if (val > INT64_MAX / MILLISECOND_PER_DAY) {
24,445!
598
        TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
599
      }
600
      (*result) = convertTimePrecision(val * MILLISECOND_PER_DAY, TSDB_TIME_PRECISION_MILLI, timePrecision);
24,445✔
601
      break;
24,445✔
602
    case 'w':
3,678✔
603
      if (val > INT64_MAX / MILLISECOND_PER_WEEK) {
3,678!
604
        TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
605
      }
606
      (*result) = convertTimePrecision(val * MILLISECOND_PER_WEEK, TSDB_TIME_PRECISION_MILLI, timePrecision);
3,678✔
607
      break;
3,678✔
608
    case 'a':
30,273✔
609
      (*result) = convertTimePrecision(val, TSDB_TIME_PRECISION_MILLI, timePrecision);
30,273✔
610
      break;
30,273✔
611
    case 'u':
1,210✔
612
      (*result) = convertTimePrecision(val, TSDB_TIME_PRECISION_MICRO, timePrecision);
1,210✔
613
      break;
1,210✔
614
    case 'b':
1,209✔
615
      (*result) = convertTimePrecision(val, TSDB_TIME_PRECISION_NANO, timePrecision);
1,209✔
616
      break;
1,209✔
617
    default: {
×
618
      TAOS_RETURN(TSDB_CODE_OUT_OF_RANGE);
×
619
    }
620
  }
621
  TAOS_RETURN(TSDB_CODE_SUCCESS);
10,313,775✔
622
}
623

624
/*
625
 * n - months
626
 * y - Years
627
 * is not allowed, since the duration of month or year are both variable.
628
 *
629
 * b - nanoseconds;
630
 * u - microseconds;
631
 * a - Millionseconds
632
 * s - Seconds
633
 * m - Minutes
634
 * h - Hours
635
 * d - Days (24 hours)
636
 * w - Weeks (7 days)
637
 */
638
int32_t parseAbsoluteDuration(const char* token, int32_t tokenlen, int64_t* duration, char* unit,
10,173,173✔
639
                              int32_t timePrecision) {
640
  errno = 0;
10,173,173✔
641
  char* endPtr = NULL;
10,173,173✔
642

643
  /* get the basic numeric value */
644
  int64_t timestamp = taosStr2Int64(token, &endPtr, 10);
10,173,173✔
645
  if ((timestamp == 0 && token[0] != '0') || errno != 0) {
10,173,173!
646
    TAOS_RETURN(TAOS_SYSTEM_ERROR(errno));
×
647
  }
648

649
  /* natual month/year are not allowed in absolute duration */
650
  *unit = token[tokenlen - 1];
10,173,173✔
651
  if (*unit == 'n' || *unit == 'y') {
10,173,173✔
652
    TAOS_RETURN(TSDB_CODE_INVALID_PARA);
73✔
653
  }
654

655
  return getDuration(timestamp, *unit, duration, timePrecision);
10,173,100✔
656
}
657

658
int32_t parseNatualDuration(const char* token, int32_t tokenLen, int64_t* duration, char* unit, int32_t timePrecision,
142,280✔
659
                            bool negativeAllow) {
660
  errno = 0;
142,280✔
661

662
  /* get the basic numeric value */
663
  *duration = taosStr2Int64(token, NULL, 10);
142,280✔
664
  if ((*duration < 0 && !negativeAllow) || errno != 0) {
142,279!
665
    TAOS_RETURN(TAOS_SYSTEM_ERROR(errno));
1✔
666
  }
667

668
  *unit = token[tokenLen - 1];
142,278✔
669
  if (*unit == 'n' || *unit == 'y') {
142,278✔
670
    TAOS_RETURN(TSDB_CODE_SUCCESS);
1,603✔
671
  }
672
  if (isdigit(*unit)) {
140,675✔
673
    *unit = getPrecisionUnit(timePrecision);
26✔
674
  }
675

676
  return getDuration(*duration, *unit, duration, timePrecision);
140,675✔
677
}
678

679
static bool taosIsLeapYear(int32_t year) { return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); }
516,938!
680

681
int64_t taosTimeAdd(int64_t t, int64_t duration, char unit, int32_t precision) {
2,147,483,647✔
682
  if (duration == 0) {
2,147,483,647✔
683
    return t;
374,255,706✔
684
  }
685

686
  if (!IS_CALENDAR_TIME_DURATION(unit)) {
2,147,483,647!
687
    return t + duration;
2,147,483,647✔
688
  }
689

690
  // The following code handles the y/n time duration
691
  int64_t numOfMonth = (unit == 'y') ? duration * 12 : duration;
×
692
  int64_t fraction = t % TSDB_TICK_PER_SECOND(precision);
×
693

694
  struct tm  tm;
695
  time_t     tt = (time_t)(t / TSDB_TICK_PER_SECOND(precision));
×
696
  struct tm* ptm = taosLocalTime(&tt, &tm, NULL, 0);
×
697
  int32_t    mon = tm.tm_year * 12 + tm.tm_mon + (int32_t)numOfMonth;
516,945✔
698
  tm.tm_year = mon / 12;
516,945✔
699
  tm.tm_mon = mon % 12;
516,945✔
700
  int daysOfMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
516,945✔
701
  if (taosIsLeapYear(1900 + tm.tm_year)) {
516,945✔
702
    daysOfMonth[1] = 29;
159,562✔
703
  }
704
  if (tm.tm_mday > daysOfMonth[tm.tm_mon]) {
516,942✔
705
    tm.tm_mday = daysOfMonth[tm.tm_mon];
1✔
706
  }
707
  return (int64_t)(taosMktime(&tm) * TSDB_TICK_PER_SECOND(precision) + fraction);
516,942✔
708
}
709

710
/**
711
 * @brief calc how many windows after filling between skey and ekey
712
 * @notes for asc order
713
 *     skey      --->       ekey
714
 *      ^                    ^
715
 * _____!_____.........._____|_____..
716
 *      |__1__)
717
 *            |__2__)...-->|_ret+1_)
718
 *      skey + ret * interval <= ekey
719
 *      skey + ret * interval + interval > ekey
720
 * ======> (ekey - skey - interval) / interval < ret <= (ekey - skey) / interval
721
 * For keys from blocks which do not need filling, skey + ret * interval == ekey.
722
 * For keys need filling, skey + ret * interval <= ekey.
723
 * Total num of windows is ret + 1(the last window)
724
 *
725
 *        for desc order
726
 *     skey       <---      ekey
727
 *      ^                    ^
728
 * _____|____..........______!____...
729
 *                           |_first_)
730
 *                     |__1__)
731
 *  |_ret_)<--...|__2__)
732
 *      skey >= ekey - ret * interval
733
 *      skey < ekey - ret * interval + interval
734
 *=======> (ekey - skey) / interval <= ret < (ekey - skey + interval) / interval
735
 * For keys from blocks which do not need filling, skey == ekey - ret * interval.
736
 * For keys need filling, skey >= ekey - ret * interval.
737
 * Total num of windows is ret + 1(the first window)
738
 */
739
int32_t taosTimeCountIntervalForFill(int64_t skey, int64_t ekey, int64_t interval, char unit, int32_t precision,
1,201,422✔
740
                                     int32_t order) {
741
  if (ekey < skey) {
1,201,422✔
742
    int64_t tmp = ekey;
1,038,575✔
743
    ekey = skey;
1,038,575✔
744
    skey = tmp;
1,038,575✔
745
  }
746
  int32_t ret;
747

748
  if (unit != 'n' && unit != 'y') {
1,201,422!
749
    ret = (int32_t)((ekey - skey) / interval);
1,201,422✔
750
    if (order == TSDB_ORDER_DESC && ret * interval < (ekey - skey)) ret += 1;
1,201,422!
751
  } else {
752
    skey /= (int64_t)(TSDB_TICK_PER_SECOND(precision));
×
753
    ekey /= (int64_t)(TSDB_TICK_PER_SECOND(precision));
×
754

755
    struct tm  tm;
756
    time_t     t = (time_t)skey;
×
757
    struct tm* ptm = taosLocalTime(&t, &tm, NULL, 0);
×
758
    int32_t    smon = tm.tm_year * 12 + tm.tm_mon;
2✔
759

760
    t = (time_t)ekey;
2✔
761
    ptm = taosLocalTime(&t, &tm, NULL, 0);
2✔
762
    int32_t emon = tm.tm_year * 12 + tm.tm_mon;
2✔
763

764
    if (unit == 'y') {
2!
765
      interval *= 12;
×
766
    }
767
    ret = (emon - smon) / (int32_t)interval;
2✔
768
    if (order == TSDB_ORDER_DESC && ret * interval < (smon - emon)) ret += 1;
2!
769
  }
770
  return ret + 1;
1,201,424✔
771
}
772

773
int64_t taosTimeTruncate(int64_t ts, const SInterval* pInterval) {
23,994,634✔
774
  if (pInterval->sliding == 0) {
23,994,634✔
775
    return ts;
2,083✔
776
  }
777

778
  int64_t start = ts;
23,992,551✔
779
  int32_t precision = pInterval->precision;
23,992,551✔
780

781
  if (IS_CALENDAR_TIME_DURATION(pInterval->slidingUnit)) {
23,992,551✔
782
    start /= (int64_t)(TSDB_TICK_PER_SECOND(precision));
63,406✔
783
    struct tm  tm;
784
    time_t     tt = (time_t)start;
63,406✔
785
    struct tm* ptm = taosLocalTime(&tt, &tm, NULL, 0);
63,406✔
786
    tm.tm_sec = 0;
63,537✔
787
    tm.tm_min = 0;
63,537✔
788
    tm.tm_hour = 0;
63,537✔
789
    tm.tm_mday = 1;
63,537✔
790

791
    if (pInterval->slidingUnit == 'y') {
63,537✔
792
      tm.tm_mon = 0;
30,049✔
793
      tm.tm_year = (int32_t)(tm.tm_year / pInterval->sliding * pInterval->sliding);
30,049✔
794
    } else {
795
      int32_t mon = tm.tm_year * 12 + tm.tm_mon;
33,488✔
796
      mon = (int32_t)(mon / pInterval->sliding * pInterval->sliding);
33,488✔
797
      tm.tm_year = mon / 12;
33,488✔
798
      tm.tm_mon = mon % 12;
33,488✔
799
    }
800

801
    start = (int64_t)(taosMktime(&tm) * TSDB_TICK_PER_SECOND(precision));
63,537✔
802
  } else {
803
    if (IS_CALENDAR_TIME_DURATION(pInterval->intervalUnit)) {
23,929,223!
804
      int64_t news = (ts / pInterval->sliding) * pInterval->sliding;
643✔
805
      if (pInterval->slidingUnit == 'd' || pInterval->slidingUnit == 'w') {
643!
806
#if defined(WINDOWS) && _MSC_VER >= 1900
807
        int64_t timezone = _timezone;
808
#endif
809
        news += (int64_t)(timezone * TSDB_TICK_PER_SECOND(precision));
565!
810
      }
811

812
      if (news <= ts) {
643✔
813
        int64_t prev = news;
565✔
814
        int64_t newe = taosTimeAdd(news, pInterval->interval, pInterval->intervalUnit, precision) - 1;
565✔
815

816
        if (newe < ts) {  // move towards the greater endpoint
566!
817
          while (newe < ts && news < ts) {
×
818
            news += pInterval->sliding;
×
819
            newe = taosTimeAdd(news, pInterval->interval, pInterval->intervalUnit, precision) - 1;
×
820
          }
821

822
          prev = news;
×
823
        } else {
824
          while (newe >= ts) {
17,112✔
825
            prev = news;
16,547✔
826
            news -= pInterval->sliding;
16,547✔
827
            newe = taosTimeAdd(news, pInterval->interval, pInterval->intervalUnit, precision) - 1;
16,547✔
828
          }
829
        }
830

831
        return prev;
565✔
832
      }
833
    } else {
834
      int64_t delta = ts - pInterval->interval;
23,928,502✔
835
      int32_t factor = (delta >= 0) ? 1 : -1;
23,928,502✔
836

837
      start = (delta / pInterval->sliding + factor) * pInterval->sliding;
23,928,502✔
838

839
      if (pInterval->intervalUnit == 'd' || pInterval->intervalUnit == 'w') {
23,928,502✔
840
        /*
841
         * here we revised the start time of day according to the local time zone,
842
         * but in case of DST, the start time of one day need to be dynamically decided.
843
         */
844
        // todo refactor to extract function that is available for Linux/Windows/Mac platform
845
#if defined(WINDOWS) && _MSC_VER >= 1900
846
        // see
847
        // https://docs.microsoft.com/en-us/cpp/c-runtime-library/daylight-dstbias-timezone-and-tzname?view=vs-2019
848
        int64_t timezone = _timezone;
849
        int32_t daylight = _daylight;
850
        char**  tzname = _tzname;
851
#endif
852

853
        start += (int64_t)(timezone * TSDB_TICK_PER_SECOND(precision));
617,744!
854
      }
855

856
      int64_t end = 0;
23,928,502✔
857

858
      // not enough time range
859
      if (start < 0 || INT64_MAX - start > pInterval->interval - 1) {
23,928,502!
860
        end = taosTimeAdd(start, pInterval->interval, pInterval->intervalUnit, precision) - 1;
23,928,502✔
861
        while (end < ts) {  // move forward to the correct time window
23,988,195✔
862
          start += pInterval->sliding;
59,564✔
863

864
          if (start < 0 || INT64_MAX - start > pInterval->interval - 1) {
59,564!
865
            end = start + pInterval->interval - 1;
59,564✔
866
          } else {
867
            end = INT64_MAX;
×
868
            break;
×
869
          }
870
        }
871
      } else {
872
        end = INT64_MAX;
×
873
      }
874
    }
875
  }
876

877
  if (pInterval->offset > 0) {
23,992,246✔
878
    // try to move current window to the left-hande-side, due to the offset effect.
879
    int64_t newe = taosTimeAdd(start, pInterval->interval, pInterval->intervalUnit, precision) - 1;
1,175,735✔
880
    int64_t slidingStart = start;
1,175,729✔
881
    while (newe >= ts) {
3,627,415✔
882
      start = slidingStart;
2,451,676✔
883
      slidingStart = taosTimeAdd(slidingStart, -pInterval->sliding, pInterval->slidingUnit, precision);
2,451,676✔
884
      int64_t slidingEnd = taosTimeAdd(slidingStart, pInterval->interval, pInterval->intervalUnit, precision) - 1;
2,451,665✔
885
      newe = taosTimeAdd(slidingEnd, pInterval->offset, pInterval->offsetUnit, precision);
2,451,668✔
886
    }
887
    start = taosTimeAdd(start, pInterval->offset, pInterval->offsetUnit, precision);
1,175,739✔
888
  }
889

890
  return start;
23,992,057✔
891
}
892

893
// used together with taosTimeTruncate. when offset is great than zero, slide-start/slide-end is the anchor point
894
int64_t taosTimeGetIntervalEnd(int64_t intervalStart, const SInterval* pInterval) {
44,297,995✔
895
  if (pInterval->offset > 0) {
44,297,995✔
896
    int64_t slideStart =
897
        taosTimeAdd(intervalStart, -1 * pInterval->offset, pInterval->offsetUnit, pInterval->precision);
1,081,556✔
898
    int64_t slideEnd = taosTimeAdd(slideStart, pInterval->interval, pInterval->intervalUnit, pInterval->precision) - 1;
1,081,543✔
899
    int64_t result = taosTimeAdd(slideEnd, pInterval->offset, pInterval->offsetUnit, pInterval->precision);
1,081,544✔
900
    return result;
1,081,549✔
901
  } else {
902
    int64_t result = taosTimeAdd(intervalStart, pInterval->interval, pInterval->intervalUnit, pInterval->precision) - 1;
43,216,439✔
903
    return result;
43,216,365✔
904
  }
905
}
906
// internal function, when program is paused in debugger,
907
// one can call this function from debugger to print a
908
// timestamp as human readable string, for example (gdb):
909
//     p fmtts(1593769722)
910
// outputs:
911
//     2020-07-03 17:48:42
912
// and the parameter can also be a variable.
913
const char* fmtts(int64_t ts) {
×
914
  static char buf[TD_TIME_STR_LEN] = {0};
915
  size_t      pos = 0;
×
916
  struct tm   tm;
917

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

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

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

954
  return buf;
×
955
}
956

957
int32_t taosFormatUtcTime(char* buf, int32_t bufLen, int64_t t, int32_t precision) {
250,310✔
958
  char      ts[40] = {0};
250,310✔
959
  struct tm ptm;
960

961
  int32_t fractionLen;
962
  char*   format = NULL;
250,310✔
963
  time_t  quot = 0;
250,310✔
964
  long    mod = 0;
250,310✔
965

966
  switch (precision) {
250,310!
967
    case TSDB_TIME_PRECISION_MILLI: {
250,315✔
968
      quot = t / 1000;
250,315✔
969
      fractionLen = 5;
250,315✔
970
      format = ".%03" PRId64;
250,315✔
971
      mod = t % 1000;
250,315✔
972
      break;
250,315✔
973
    }
974

975
    case TSDB_TIME_PRECISION_MICRO: {
×
976
      quot = t / 1000000;
×
977
      fractionLen = 8;
×
978
      format = ".%06" PRId64;
×
979
      mod = t % 1000000;
×
980
      break;
×
981
    }
982

983
    case TSDB_TIME_PRECISION_NANO: {
×
984
      quot = t / 1000000000;
×
985
      fractionLen = 11;
×
986
      format = ".%09" PRId64;
×
987
      mod = t % 1000000000;
×
988
      break;
×
989
    }
990

991
    default:
×
992
      fractionLen = 0;
×
993
      TAOS_RETURN(TSDB_CODE_INVALID_PARA);
×
994
  }
995

996
  if (NULL == taosLocalTime(&quot, &ptm, buf, bufLen)) {
250,315!
997
    TAOS_RETURN(TAOS_SYSTEM_ERROR(errno));
×
998
  }
999
  int32_t length = (int32_t)strftime(ts, 40, "%Y-%m-%dT%H:%M:%S", &ptm);
250,313✔
1000
  length += tsnprintf(ts + length, fractionLen, format, mod);
250,313✔
1001
  length += (int32_t)strftime(ts + length, 40 - length, "%z", &ptm);
250,324✔
1002

1003
  tstrncpy(buf, ts, bufLen);
250,324✔
1004
  TAOS_RETURN(TSDB_CODE_SUCCESS);
250,324✔
1005
}
1006

1007
int32_t taosTs2Tm(int64_t ts, int32_t precision, struct STm* tm) {
800,600✔
1008
  tm->fsec = ts % TICK_PER_SECOND[precision] * (TICK_PER_SECOND[TSDB_TIME_PRECISION_NANO] / TICK_PER_SECOND[precision]);
800,600✔
1009
  time_t t = ts / TICK_PER_SECOND[precision];
800,600✔
1010
  if (NULL == taosLocalTime(&t, &tm->tm, NULL, 0)) {
800,600!
1011
    TAOS_RETURN(TAOS_SYSTEM_ERROR(errno));
×
1012
  }
1013
  return TSDB_CODE_SUCCESS;
803,633✔
1014
}
1015

1016
int32_t taosTm2Ts(struct STm* tm, int64_t* ts, int32_t precision) {
719,716✔
1017
  *ts = taosMktime(&tm->tm);
719,716✔
1018
  *ts *= TICK_PER_SECOND[precision];
721,074✔
1019
  *ts += tm->fsec / (TICK_PER_SECOND[TSDB_TIME_PRECISION_NANO] / TICK_PER_SECOND[precision]);
721,074✔
1020
  return TSDB_CODE_SUCCESS;
721,074✔
1021
}
1022

1023
typedef struct {
1024
  const char* name;
1025
  int         len;
1026
  int         id;
1027
  bool        isDigit;
1028
} TSFormatKeyWord;
1029

1030
typedef enum {
1031
  // TSFKW_AD,   // BC AD
1032
  // TSFKW_A_D,  // A.D. B.C.
1033
  TSFKW_AM,   // AM, PM
1034
  TSFKW_A_M,  // A.M., P.M.
1035
  // TSFKW_BC,   // BC AD
1036
  // TSFKW_B_C,  // B.C. A.D.
1037
  TSFKW_DAY,  // MONDAY, TUESDAY ...
1038
  TSFKW_DDD,  // Day of year 001-366
1039
  TSFKW_DD,   // Day of month 01-31
1040
  TSFKW_Day,  // Sunday, Monday
1041
  TSFKW_DY,   // MON, TUE
1042
  TSFKW_Dy,   // Mon, Tue
1043
  TSFKW_D,    // 1-7 -> Sunday(1) -> Saturday(7)
1044
  TSFKW_HH24,
1045
  TSFKW_HH12,
1046
  TSFKW_HH,
1047
  TSFKW_MI,  // minute
1048
  TSFKW_MM,
1049
  TSFKW_MONTH,  // JANUARY, FEBRUARY
1050
  TSFKW_MON,
1051
  TSFKW_Month,
1052
  TSFKW_Mon,
1053
  TSFKW_MS,
1054
  TSFKW_NS,
1055
  // TSFKW_OF,
1056
  TSFKW_PM,
1057
  TSFKW_P_M,
1058
  TSFKW_SS,
1059
  TSFKW_TZH,
1060
  // TSFKW_TZM,
1061
  // TSFKW_TZ,
1062
  TSFKW_US,
1063
  TSFKW_YYYY,
1064
  TSFKW_YYY,
1065
  TSFKW_YY,
1066
  TSFKW_Y,
1067
  // TSFKW_a_d,
1068
  // TSFKW_ad,
1069
  TSFKW_am,
1070
  TSFKW_a_m,
1071
  // TSFKW_b_c,
1072
  // TSFKW_bc,
1073
  TSFKW_day,
1074
  TSFKW_ddd,
1075
  TSFKW_dd,
1076
  TSFKW_dy,  // mon, tue
1077
  TSFKW_d,
1078
  TSFKW_hh24,
1079
  TSFKW_hh12,
1080
  TSFKW_hh,
1081
  TSFKW_mi,
1082
  TSFKW_mm,
1083
  TSFKW_month,
1084
  TSFKW_mon,
1085
  TSFKW_ms,
1086
  TSFKW_ns,
1087
  TSFKW_pm,
1088
  TSFKW_p_m,
1089
  TSFKW_ss,
1090
  TSFKW_tzh,
1091
  // TSFKW_tzm,
1092
  // TSFKW_tz,
1093
  TSFKW_us,
1094
  TSFKW_yyyy,
1095
  TSFKW_yyy,
1096
  TSFKW_yy,
1097
  TSFKW_y,
1098
  TSFKW_last_
1099
} TSFormatKeywordId;
1100

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

1175
#define TS_FROMAT_KEYWORD_INDEX_SIZE ('z' - 'A' + 1)
1176
static const int TSFormatKeywordIndex[TS_FROMAT_KEYWORD_INDEX_SIZE] = {
1177
    /*A*/ TSFKW_AM,     -1, -1,
1178
    /*D*/ TSFKW_DAY,    -1, -1, -1,
1179
    /*H*/ TSFKW_HH24,   -1, -1, -1, -1,
1180
    /*M*/ TSFKW_MI,
1181
    /*N*/ TSFKW_NS,     -1,
1182
    /*P*/ TSFKW_PM,     -1, -1,
1183
    /*S*/ TSFKW_SS,
1184
    /*T*/ TSFKW_TZH,
1185
    /*U*/ TSFKW_US,     -1, -1, -1,
1186
    /*Y*/ TSFKW_YYYY,   -1,
1187
    /*[ \ ] ^ _ `*/ -1, -1, -1, -1, -1, -1,
1188
    /*a*/ TSFKW_am,     -1, -1,
1189
    /*d*/ TSFKW_day,    -1, -1, -1,
1190
    /*h*/ TSFKW_hh24,   -1, -1, -1, -1,
1191
    /*m*/ TSFKW_mi,
1192
    /*n*/ TSFKW_ns,     -1,
1193
    /*p*/ TSFKW_pm,     -1, -1,
1194
    /*s*/ TSFKW_ss,
1195
    /*t*/ TSFKW_tzh,
1196
    /*u*/ TSFKW_us,     -1, -1, -1,
1197
    /*y*/ TSFKW_yyyy,   -1};
1198

1199
typedef struct {
1200
  uint8_t                type;
1201
  const char*            c;
1202
  int32_t                len;
1203
  const TSFormatKeyWord* key;
1204
} TSFormatNode;
1205

1206
static const char* const weekDays[] = {"Sunday",   "Monday", "Tuesday",  "Wednesday",
1207
                                       "Thursday", "Friday", "Saturday", "NULL"};
1208
static const char* const shortWeekDays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "NULL"};
1209
static const char* const fullMonths[] = {"January", "February",  "March",   "April",    "May",      "June", "July",
1210
                                         "August",  "September", "October", "November", "December", NULL};
1211
static const char* const months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
1212
                                     "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
1213
#define A_M_STR "A.M."
1214
#define a_m_str "a.m."
1215
#define AM_STR  "AM"
1216
#define am_str  "am"
1217
#define P_M_STR "P.M."
1218
#define p_m_str "p.m."
1219
#define PM_STR  "PM"
1220
#define pm_str  "pm"
1221
static const char* const apms[] = {AM_STR, PM_STR, am_str, pm_str, NULL};
1222
static const char* const long_apms[] = {A_M_STR, P_M_STR, a_m_str, p_m_str, NULL};
1223

1224
#define TS_FORMAT_NODE_TYPE_KEYWORD   1
1225
#define TS_FORMAT_NODE_TYPE_SEPARATOR 2
1226
#define TS_FORMAT_NODE_TYPE_CHAR      3
1227

1228
static const TSFormatKeyWord* keywordSearch(const char* str) {
1,822,077✔
1229
  if (*str < 'A' || *str > 'z' || (*str > 'Z' && *str < 'a')) return NULL;
1,822,077✔
1230
  int32_t idx = TSFormatKeywordIndex[str[0] - 'A'];
733,479✔
1231
  if (idx < 0) return NULL;
733,479!
1232
  const TSFormatKeyWord* key = &formatKeyWords[idx++];
733,479✔
1233
  while (key->name && str[0] == key->name[0]) {
1,114,845!
1234
    if (0 == strncmp(key->name, str, key->len)) {
1,115,740✔
1235
      return key;
734,374✔
1236
    }
1237
    key = &formatKeyWords[idx++];
381,366✔
1238
  }
1239
  return NULL;
×
1240
}
1241

1242
static bool isSeperatorChar(char c) {
3,883,205✔
1243
  return (c > 0x20 && c < 0x7F && !(c >= 'A' && c <= 'Z') && !(c >= 'a' && c <= 'z') && !(c >= '0' && c <= '9'));
3,883,205!
1244
}
1245

1246
static int32_t parseTsFormat(const char* formatStr, SArray* formats) {
122,466✔
1247
  TSFormatNode* lastOtherFormat = NULL;
122,466✔
1248
  while (*formatStr) {
1,946,038✔
1249
    const TSFormatKeyWord* key = keywordSearch(formatStr);
1,822,277✔
1250
    if (key) {
1,821,734✔
1251
      TSFormatNode format = {.key = key, .type = TS_FORMAT_NODE_TYPE_KEYWORD};
733,602✔
1252
      if (NULL == taosArrayPush(formats, &format)) TAOS_RETURN(terrno);
734,398!
1253
      formatStr += key->len;
734,398✔
1254
      lastOtherFormat = NULL;
734,398✔
1255
    } else {
1256
      if (*formatStr == '"') {
1,088,132✔
1257
        lastOtherFormat = NULL;
12✔
1258
        // for double quoted string
1259
        formatStr++;
12✔
1260
        TSFormatNode* last = NULL;
12✔
1261
        while (*formatStr) {
48!
1262
          if (*formatStr == '"') {
48✔
1263
            formatStr++;
12✔
1264
            break;
12✔
1265
          }
1266
          if (*formatStr == '\\' && *(formatStr + 1)) {
36!
1267
            formatStr++;
×
1268
            last = NULL;  // stop expanding last format, create new format
×
1269
          }
1270
          if (last) {
36✔
1271
            // expand
1272
            last->len++;
24✔
1273
            formatStr++;
24✔
1274
          } else {
1275
            // create new
1276
            TSFormatNode format = {.type = TS_FORMAT_NODE_TYPE_CHAR, .key = NULL};
12✔
1277
            format.c = formatStr;
12✔
1278
            format.len = 1;
12✔
1279
            if (NULL == taosArrayPush(formats, &format)) TAOS_RETURN(terrno);
12!
1280
            formatStr++;
12✔
1281
            last = taosArrayGetLast(formats);
12✔
1282
          }
1283
        }
1284
      } else {
1285
        // for other strings
1286
        if (*formatStr == '\\' && *(formatStr + 1)) {
1,088,120!
1287
          formatStr++;
×
1288
          lastOtherFormat = NULL;  // stop expanding
×
1289
        } else {
1290
          if (lastOtherFormat && !isSeperatorChar(*formatStr)) {
1,088,120✔
1291
            // expanding
1292
          } else {
1293
            // create new
1294
            lastOtherFormat = NULL;
610,996✔
1295
          }
1296
        }
1297
        if (lastOtherFormat) {
1,088,319✔
1298
          lastOtherFormat->len++;
477,365✔
1299
          formatStr++;
477,365✔
1300
        } else {
1301
          TSFormatNode format = {
611,548✔
1302
              .type = isSeperatorChar(*formatStr) ? TS_FORMAT_NODE_TYPE_SEPARATOR : TS_FORMAT_NODE_TYPE_CHAR,
610,954✔
1303
              .key = NULL};
1304
          format.c = formatStr;
611,548✔
1305
          format.len = 1;
611,548✔
1306
          if (NULL == taosArrayPush(formats, &format)) TAOS_RETURN(terrno);
611,876!
1307
          formatStr++;
611,876✔
1308
          if (format.type == TS_FORMAT_NODE_TYPE_CHAR) lastOtherFormat = taosArrayGetLast(formats);
611,876✔
1309
        }
1310
      }
1311
    }
1312
  }
1313
  TAOS_RETURN(TSDB_CODE_SUCCESS);
123,761✔
1314
}
1315

1316
static int32_t tm2char(const SArray* formats, const struct STm* tm, char* s, int32_t outLen) {
802,774✔
1317
  int32_t     size = taosArrayGetSize(formats);
802,774✔
1318
  const char* start = s;
802,064✔
1319
  for (int32_t i = 0; i < size; ++i) {
9,870,200✔
1320
    TSFormatNode* format = taosArrayGet(formats, i);
9,155,805✔
1321
    if (format->type != TS_FORMAT_NODE_TYPE_KEYWORD) {
9,068,136✔
1322
      if (s - start + format->len + 1 > outLen) break;
4,641,764!
1323
      (void)strncpy(s, format->c, format->len);
4,641,764✔
1324
      s += format->len;
4,641,764✔
1325
      continue;
4,641,764✔
1326
    }
1327
    if (s - start + 16 > outLen) break;
4,426,372!
1328

1329
    switch (format->key->id) {
4,426,372!
1330
      case TSFKW_AM:
8✔
1331
      case TSFKW_PM:
1332
        (void)sprintf(s, tm->tm.tm_hour % 24 >= 12 ? "PM" : "AM");
8!
1333
        s += 2;
8✔
1334
        break;
8✔
1335
      case TSFKW_A_M:
×
1336
      case TSFKW_P_M:
1337
        (void)sprintf(s, tm->tm.tm_hour % 24 >= 12 ? "P.M." : "A.M.");
×
1338
        s += 4;
×
1339
        break;
×
1340
      case TSFKW_am:
4✔
1341
      case TSFKW_pm:
1342
        (void)sprintf(s, tm->tm.tm_hour % 24 >= 12 ? "pm" : "am");
4!
1343
        s += 2;
4✔
1344
        break;
4✔
1345
      case TSFKW_a_m:
460✔
1346
      case TSFKW_p_m:
1347
        (void)sprintf(s, tm->tm.tm_hour % 24 >= 12 ? "p.m." : "a.m.");
460✔
1348
        s += 4;
460✔
1349
        break;
460✔
1350
      case TSFKW_DDD:
864✔
1351
#ifdef WINDOWS
1352
        return TSDB_CODE_FUNC_TO_CHAR_NOT_SUPPORTED;
1353
#endif
1354
        (void)sprintf(s, "%03d", tm->tm.tm_yday + 1);
864✔
1355
        s += strlen(s);
864✔
1356
        break;
864✔
1357
      case TSFKW_DD:
798,780✔
1358
        (void)sprintf(s, "%02d", tm->tm.tm_mday);
798,780✔
1359
        s += 2;
798,780✔
1360
        break;
798,780✔
1361
      case TSFKW_D:
752✔
1362
        (void)sprintf(s, "%d", tm->tm.tm_wday + 1);
752✔
1363
        s += 1;
752✔
1364
        break;
752✔
1365
      case TSFKW_DAY: {
32✔
1366
        // MONDAY, TUESDAY...
1367
        const char* wd = weekDays[tm->tm.tm_wday];
32✔
1368
        char        buf[10] = {0};
32✔
1369
        for (int32_t i = 0; i < strlen(wd); ++i) buf[i] = toupper(wd[i]);
224✔
1370
        (void)sprintf(s, "%-9s", buf);
32✔
1371
        s += strlen(s);
32✔
1372
        break;
32✔
1373
      }
1374
      case TSFKW_Day:
16✔
1375
        // Monday, TuesDay...
1376
        (void)sprintf(s, "%-9s", weekDays[tm->tm.tm_wday]);
16✔
1377
        s += strlen(s);
16✔
1378
        break;
16✔
1379
      case TSFKW_day: {
32✔
1380
        const char* wd = weekDays[tm->tm.tm_wday];
32✔
1381
        char        buf[10] = {0};
32✔
1382
        for (int32_t i = 0; i < strlen(wd); ++i) buf[i] = tolower(wd[i]);
224✔
1383
        (void)sprintf(s, "%-9s", buf);
32✔
1384
        s += strlen(s);
32✔
1385
        break;
32✔
1386
      }
1387
      case TSFKW_DY: {
656✔
1388
        // MON, TUE
1389
        const char* wd = shortWeekDays[tm->tm.tm_wday];
656✔
1390
        char        buf[8] = {0};
656✔
1391
        for (int32_t i = 0; i < strlen(wd); ++i) buf[i] = toupper(wd[i]);
2,624✔
1392
        (void)sprintf(s, "%3s", buf);
656✔
1393
        s += 3;
656✔
1394
        break;
656✔
1395
      }
1396
      case TSFKW_Dy:
32✔
1397
        // Mon, Tue
1398
        (void)sprintf(s, "%3s", shortWeekDays[tm->tm.tm_wday]);
32✔
1399
        s += 3;
32✔
1400
        break;
32✔
1401
      case TSFKW_dy: {
396,694✔
1402
        // mon, tue
1403
        const char* wd = shortWeekDays[tm->tm.tm_wday];
396,694✔
1404
        char        buf[8] = {0};
396,694✔
1405
        for (int32_t i = 0; i < strlen(wd); ++i) buf[i] = tolower(wd[i]);
1,586,740✔
1406
        (void)sprintf(s, "%3s", buf);
396,694✔
1407
        s += 3;
396,694✔
1408
        break;
396,694✔
1409
      }
1410
      case TSFKW_HH24:
399,744✔
1411
        (void)sprintf(s, "%02d", tm->tm.tm_hour);
399,744✔
1412
        s += 2;
399,744✔
1413
        break;
399,744✔
1414
      case TSFKW_HH:
399,101✔
1415
      case TSFKW_HH12:
1416
        // 0 or 12 o'clock in 24H coresponds to 12 o'clock (AM/PM) in 12H
1417
        (void)sprintf(s, "%02d", tm->tm.tm_hour % 12 == 0 ? 12 : tm->tm.tm_hour % 12);
399,101✔
1418
        s += 2;
399,101✔
1419
        break;
399,101✔
1420
      case TSFKW_MI:
798,757✔
1421
        (void)sprintf(s, "%02d", tm->tm.tm_min);
798,757✔
1422
        s += 2;
798,757✔
1423
        break;
798,757✔
1424
      case TSFKW_MM:
400,882✔
1425
        (void)sprintf(s, "%02d", tm->tm.tm_mon + 1);
400,882✔
1426
        s += 2;
400,882✔
1427
        break;
400,882✔
1428
      case TSFKW_MONTH: {
32✔
1429
        const char* mon = fullMonths[tm->tm.tm_mon];
32✔
1430
        char        buf[10] = {0};
32✔
1431
        for (int32_t i = 0; i < strlen(mon); ++i) buf[i] = toupper(mon[i]);
224✔
1432
        (void)sprintf(s, "%-9s", buf);
32✔
1433
        s += strlen(s);
32✔
1434
        break;
32✔
1435
      }
1436
      case TSFKW_MON: {
864✔
1437
        const char* mon = months[tm->tm.tm_mon];
864✔
1438
        char        buf[10] = {0};
864✔
1439
        for (int32_t i = 0; i < strlen(mon); ++i) buf[i] = toupper(mon[i]);
3,456✔
1440
        (void)sprintf(s, "%s", buf);
864✔
1441
        s += strlen(s);
864✔
1442
        break;
864✔
1443
      }
1444
      case TSFKW_Month:
16✔
1445
        (void)sprintf(s, "%-9s", fullMonths[tm->tm.tm_mon]);
16✔
1446
        s += strlen(s);
16✔
1447
        break;
16✔
1448
      case TSFKW_month: {
36✔
1449
        const char* mon = fullMonths[tm->tm.tm_mon];
36✔
1450
        char        buf[10] = {0};
36✔
1451
        for (int32_t i = 0; i < strlen(mon); ++i) buf[i] = tolower(mon[i]);
256✔
1452
        (void)sprintf(s, "%-9s", buf);
36✔
1453
        s += strlen(s);
36✔
1454
        break;
36✔
1455
      }
1456
      case TSFKW_Mon:
16✔
1457
        (void)sprintf(s, "%s", months[tm->tm.tm_mon]);
16✔
1458
        s += strlen(s);
16✔
1459
        break;
16✔
1460
      case TSFKW_mon: {
397,814✔
1461
        const char* mon = months[tm->tm.tm_mon];
397,814✔
1462
        char        buf[10] = {0};
397,814✔
1463
        for (int32_t i = 0; i < strlen(mon); ++i) buf[i] = tolower(mon[i]);
1,591,486✔
1464
        (void)sprintf(s, "%s", buf);
397,814✔
1465
        s += strlen(s);
397,814✔
1466
        break;
397,814✔
1467
      }
1468
      case TSFKW_SS:
798,408✔
1469
        (void)sprintf(s, "%02d", tm->tm.tm_sec);
798,408✔
1470
        s += 2;
798,408✔
1471
        break;
798,408✔
1472
      case TSFKW_MS:
748✔
1473
        (void)sprintf(s, "%03" PRId64, tm->fsec / 1000000L);
748✔
1474
        s += 3;
748✔
1475
        break;
748✔
1476
      case TSFKW_US:
60✔
1477
        (void)sprintf(s, "%06" PRId64, tm->fsec / 1000L);
60✔
1478
        s += 6;
60✔
1479
        break;
60✔
1480
      case TSFKW_NS:
76✔
1481
        (void)sprintf(s, "%09" PRId64, tm->fsec);
76✔
1482
        s += 9;
76✔
1483
        break;
76✔
1484
      case TSFKW_TZH:
456✔
1485
        (void)sprintf(s, "%s%02d", tsTimezone < 0 ? "-" : "+", tsTimezone);
456!
1486
        s += strlen(s);
456✔
1487
        break;
456✔
1488
      case TSFKW_YYYY:
402,019✔
1489
        (void)sprintf(s, "%04d", tm->tm.tm_year + 1900);
402,019✔
1490
        s += strlen(s);
402,019✔
1491
        break;
402,019✔
1492
      case TSFKW_YYY:
96✔
1493
        (void)sprintf(s, "%03d", (tm->tm.tm_year + 1900) % 1000);
96✔
1494
        s += strlen(s);
96✔
1495
        break;
96✔
1496
      case TSFKW_YY:
397,994✔
1497
        (void)sprintf(s, "%02d", (tm->tm.tm_year + 1900) % 100);
397,994✔
1498
        s += strlen(s);
397,994✔
1499
        break;
397,994✔
1500
      case TSFKW_Y:
112✔
1501
        (void)sprintf(s, "%01d", (tm->tm.tm_year + 1900) % 10);
112✔
1502
        s += strlen(s);
112✔
1503
        break;
112✔
1504
      default:
×
1505
        break;
×
1506
    }
1507
  }
1508
  return TSDB_CODE_SUCCESS;
714,395✔
1509
}
1510

1511
/// @brief find s in arr case insensitively
1512
/// @retval the index in arr if found, -1 if not found
1513
static int32_t strArrayCaseSearch(const char* const* arr, const char* s) {
769,970✔
1514
  if (!*s) return -1;
769,970!
1515
  const char* const* fmt = arr;
769,970✔
1516
  for (; *fmt; ++fmt) {
5,306,935!
1517
    const char *l, *r;
1518
    for (l = fmt[0], r = s;; l++, r++) {
7,669,778✔
1519
      if (*l == '\0') return fmt - arr;
7,669,778✔
1520
      if (*r == '\0' || tolower(*l) != tolower(*r)) break;
6,890,305!
1521
    }
1522
  }
1523
  return -1;
×
1524
}
1525

1526
static const char* tsFormatStr2Int32(int32_t* dest, const char* str, int32_t len, bool needMoreDigit) {
3,628,760✔
1527
  char*       last;
1528
  int64_t     res;
1529
  const char* s = str;
3,628,760✔
1530
  if ('\0' == str[0]) return NULL;
3,628,760!
1531
  if (len <= 0) {
3,628,760✔
1532
    res = taosStr2Int64(s, &last, 10);
208✔
1533
    s = last;
208✔
1534
  } else {
1535
    char buf[16] = {0};
3,628,552✔
1536
    (void)strncpy(buf, s, len);
3,628,552✔
1537
    int32_t copiedLen = strlen(buf);
3,628,552✔
1538
    if (copiedLen < len) {
3,628,552✔
1539
      if (!needMoreDigit) {
319,876!
1540
        // digits not enough, that's ok, cause we do not need more digits
1541
        // '2023-1' 'YYYY-MM'
1542
        // '202a' 'YYYY' -> 202
1543
        res = taosStr2Int64(s, &last, 10);
319,877✔
1544
        s += copiedLen;
319,730✔
1545
      } else {
1546
        // bytes not enough, and there are other digit formats to match
1547
        // '2023-1' 'YYYY-MMDD'
1548
        return NULL;
3✔
1549
      }
1550
    } else {
1551
      if (needMoreDigit) {
3,308,676✔
1552
        res = taosStr2Int64(buf, &last, 10);
16✔
1553
        // bytes enough, but digits not enough, like '202a12' 'YYYYMM', YYYY needs four digits
1554
        if (last - buf < len) return NULL;
16✔
1555
        s += last - buf;
12✔
1556
      } else {
1557
        res = taosStr2Int64(s, &last, 10);
3,308,660✔
1558
        s = last;
3,585,578✔
1559
      }
1560
    }
1561
  }
1562
  if (s == str) {
3,905,528!
1563
    // no integers found
1564
    return NULL;
×
1565
  }
1566
  if (errno == ERANGE || res > INT32_MAX || res < INT32_MIN) {
3,905,528!
1567
    // out of range
1568
    return NULL;
7,777✔
1569
  }
1570
  *dest = res;
3,897,751✔
1571
  return s;
3,897,751✔
1572
}
1573

1574
static int32_t adjustYearTo2020(int32_t year) {
399,964✔
1575
  if (year < 70) return year + 2000;    // 2000 - 2069
399,964!
1576
  if (year < 100) return year + 1900;   // 1970 - 1999
×
1577
  if (year < 520) return year + 2000;   // 2100 - 2519
×
1578
  if (year < 1000) return year + 1000;  // 1520 - 1999
×
1579
  return year;
×
1580
}
1581

1582
static bool checkTm(const struct tm* tm) {
718,927✔
1583
  if (tm->tm_mon < 0 || tm->tm_mon > 11) return false;
718,927!
1584
  if (tm->tm_wday < 0 || tm->tm_wday > 6) return false;
719,079!
1585
  if (tm->tm_yday < 0 || tm->tm_yday > 365) return false;
719,096!
1586
  if (tm->tm_mday < 0 || tm->tm_mday > 31) return false;
719,179!
1587
  if (tm->tm_hour < 0 || tm->tm_hour > 23) return false;
719,188!
1588
  if (tm->tm_min < 0 || tm->tm_min > 59) return false;
719,214!
1589
  if (tm->tm_sec < 0 || tm->tm_sec > 60) return false;
719,235!
1590
  return true;
719,258✔
1591
}
1592

1593
static bool needMoreDigits(SArray* formats, int32_t curIdx) {
3,681,934✔
1594
  if (curIdx == taosArrayGetSize(formats) - 1) return false;
3,681,934✔
1595
  TSFormatNode* pNextNode = taosArrayGet(formats, curIdx + 1);
3,350,982✔
1596
  if (pNextNode->type == TS_FORMAT_NODE_TYPE_SEPARATOR) {
3,335,831✔
1597
    return false;
2,382,793✔
1598
  } else if (pNextNode->type == TS_FORMAT_NODE_TYPE_KEYWORD) {
953,038✔
1599
    return pNextNode->key->isDigit;
252✔
1600
  } else {
1601
    return isdigit(pNextNode->c[0]);
952,786✔
1602
  }
1603
}
1604

1605
/// @brief convert a formatted time str to timestamp
1606
/// @param[in] s the formatted timestamp str
1607
/// @param[in] formats array of TSFormatNode, output of parseTsFormat
1608
/// @param[out] ts output timestamp
1609
/// @param precision the timestamp precision to convert to, sec/milli/micro/nano
1610
/// @param[out] sErrPos if not NULL, when err occured, points to the failed position of s, only set when ret is -1
1611
/// @param[out] fErrIdx if not NULL, when err occured, the idx of the failed format idx, only set when ret is -1
1612
/// @retval 0 for success
1613
/// @retval -1 for format and s mismatch error
1614
/// @retval -2 if datetime err, like 2023-13-32 25:61:69
1615
/// @retval -3 if not supported
1616
static int32_t char2ts(const char* s, SArray* formats, int64_t* ts, int32_t precision, const char** sErrPos,
721,017✔
1617
                       int32_t* fErrIdx) {
1618
  int32_t size = taosArrayGetSize(formats);
721,017✔
1619
  int32_t pm = 0;      // default am
720,869✔
1620
  int32_t hour12 = 0;  // default HH24
720,869✔
1621
  int32_t year = 0, mon = 0, yd = 0, md = 1, wd = 0;
720,869✔
1622
  int32_t hour = 0, min = 0, sec = 0, us = 0, ms = 0, ns = 0;
720,869✔
1623
  int32_t tzSign = 1, tz = tsTimezone;
720,869✔
1624
  int32_t err = 0;
720,869✔
1625
  bool    withYD = false, withMD = false;
720,869✔
1626

1627
  for (int32_t i = 0; i < size && *s != '\0'; ++i) {
8,977,123✔
1628
    while (isspace(*s) && *s != '\0') {
9,095,640!
1629
      s++;
1,114,502✔
1630
    }
1631
    if (!s) break;
7,981,138!
1632
    TSFormatNode* node = taosArrayGet(formats, i);
7,981,138✔
1633
    if (node->type == TS_FORMAT_NODE_TYPE_SEPARATOR) {
7,934,499✔
1634
      // separator matches any character
1635
      if (isSeperatorChar(s[0])) s += node->len;
2,494,203✔
1636
      continue;
2,793,327✔
1637
    }
1638
    if (node->type == TS_FORMAT_NODE_TYPE_CHAR) {
5,440,296✔
1639
      int32_t pos = 0;
1,108,584✔
1640
      // skip leading spaces
1641
      while (isspace(node->c[pos]) && node->len > 0) pos++;
2,217,211!
1642
      while (pos < node->len && *s != '\0') {
1,125,096✔
1643
        if (!isspace(node->c[pos++])) {
16,512✔
1644
          while (isspace(*s) && *s != '\0') s++;
16,504!
1645
          if (*s != '\0') s++;  // forward together
16,504!
1646
        }
1647
      }
1648
      continue;
1,108,584✔
1649
    }
1650
    switch (node->key->id) {
4,331,712!
1651
      case TSFKW_A_M:
216✔
1652
      case TSFKW_P_M:
1653
      case TSFKW_a_m:
1654
      case TSFKW_p_m: {
1655
        int32_t idx = strArrayCaseSearch(long_apms, s);
216✔
1656
        if (idx >= 0) {
216!
1657
          s += 4;
216✔
1658
          pm = idx % 2;
216✔
1659
          hour12 = 1;
216✔
1660
        } else {
1661
          err = -1;
×
1662
        }
1663
      } break;
216✔
1664
      case TSFKW_AM:
24✔
1665
      case TSFKW_PM:
1666
      case TSFKW_am:
1667
      case TSFKW_pm: {
1668
        int32_t idx = strArrayCaseSearch(apms, s);
24✔
1669
        if (idx >= 0) {
24!
1670
          s += 2;
24✔
1671
          pm = idx % 2;
24✔
1672
          hour12 = 1;
24✔
1673
        } else {
1674
          err = -1;
×
1675
        }
1676
      } break;
24✔
1677
      case TSFKW_HH:
100✔
1678
      case TSFKW_HH12: {
1679
        const char* newPos = tsFormatStr2Int32(&hour, s, 2, needMoreDigits(formats, i));
100✔
1680
        if (NULL == newPos || hour > 12 || hour <= 0) {
100!
1681
          err = -1;
×
1682
        } else {
1683
          hour12 = 1;
100✔
1684
          s = newPos;
100✔
1685
        }
1686
      } break;
100✔
1687
      case TSFKW_HH24: {
715,892✔
1688
        const char* newPos = tsFormatStr2Int32(&hour, s, 2, needMoreDigits(formats, i));
715,892✔
1689
        if (NULL == newPos) {
719,881!
1690
          err = -1;
×
1691
        } else {
1692
          hour12 = 0;
719,881✔
1693
          s = newPos;
719,881✔
1694
        }
1695
      } break;
719,881✔
1696
      case TSFKW_MI: {
714,436✔
1697
        const char* newPos = tsFormatStr2Int32(&min, s, 2, needMoreDigits(formats, i));
714,436✔
1698
        if (NULL == newPos) {
719,975!
1699
          err = -1;
×
1700
        } else {
1701
          s = newPos;
719,975✔
1702
        }
1703
      } break;
719,975✔
1704
      case TSFKW_SS: {
714,355✔
1705
        const char* newPos = tsFormatStr2Int32(&sec, s, 2, needMoreDigits(formats, i));
714,355✔
1706
        if (NULL == newPos)
719,902!
1707
          err = -1;
×
1708
        else
1709
          s = newPos;
719,902✔
1710
      } break;
719,902✔
1711
      case TSFKW_MS: {
640✔
1712
        const char* newPos = tsFormatStr2Int32(&ms, s, 3, needMoreDigits(formats, i));
640✔
1713
        if (NULL == newPos)
640!
1714
          err = -1;
×
1715
        else {
1716
          int32_t len = newPos - s;
640✔
1717
          ms *= len == 1 ? 100 : len == 2 ? 10 : 1;
640!
1718
          s = newPos;
640✔
1719
        }
1720
      } break;
640✔
1721
      case TSFKW_US: {
40✔
1722
        const char* newPos = tsFormatStr2Int32(&us, s, 6, needMoreDigits(formats, i));
40✔
1723
        if (NULL == newPos)
40!
1724
          err = -1;
×
1725
        else {
1726
          int32_t len = newPos - s;
40✔
1727
          us *= len == 1 ? 100000 : len == 2 ? 10000 : len == 3 ? 1000 : len == 4 ? 100 : len == 5 ? 10 : 1;
40!
1728
          s = newPos;
40✔
1729
        }
1730
      } break;
40✔
1731
      case TSFKW_NS: {
56✔
1732
        const char* newPos = tsFormatStr2Int32(&ns, s, 9, needMoreDigits(formats, i));
56✔
1733
        if (NULL == newPos)
56!
1734
          err = -1;
×
1735
        else {
1736
          int32_t len = newPos - s;
56✔
1737
          ns *= len == 1   ? 100000000
56✔
1738
                : len == 2 ? 10000000
112!
1739
                : len == 3 ? 1000000
112!
1740
                : len == 4 ? 100000
112!
1741
                : len == 5 ? 10000
112!
1742
                : len == 6 ? 1000
112!
1743
                : len == 7 ? 100
112!
1744
                : len == 8 ? 10
112!
1745
                           : 1;
56✔
1746
          s = newPos;
56✔
1747
        }
1748
      } break;
56✔
1749
      case TSFKW_TZH: {
208✔
1750
        tzSign = *s == '-' ? -1 : 1;
208!
1751
        const char* newPos = tsFormatStr2Int32(&tz, s, -1, needMoreDigits(formats, i));
208✔
1752
        if (NULL == newPos)
208!
1753
          err = -1;
×
1754
        else {
1755
          s = newPos;
208✔
1756
        }
1757
      } break;
208✔
1758
      case TSFKW_MONTH:
40✔
1759
      case TSFKW_Month:
1760
      case TSFKW_month: {
1761
        int32_t idx = strArrayCaseSearch(fullMonths, s);
40✔
1762
        if (idx >= 0) {
40!
1763
          s += strlen(fullMonths[idx]);
40✔
1764
          mon = idx;
40✔
1765
        } else {
1766
          err = -1;
×
1767
        }
1768
      } break;
40✔
1769
      case TSFKW_MON:
396,777✔
1770
      case TSFKW_Mon:
1771
      case TSFKW_mon: {
1772
        int32_t idx = strArrayCaseSearch(months, s);
396,777✔
1773
        if (idx >= 0) {
397,752!
1774
          s += strlen(months[idx]);
397,964✔
1775
          mon = idx;
397,964✔
1776
        } else {
1777
          err = -1;
×
1778
        }
1779
      } break;
397,752✔
1780
      case TSFKW_MM: {
320,438✔
1781
        const char* newPos = tsFormatStr2Int32(&mon, s, 2, needMoreDigits(formats, i));
320,438✔
1782
        if (NULL == newPos) {
320,465!
1783
          err = -1;
×
1784
        } else {
1785
          s = newPos;
320,465✔
1786
          mon -= 1;
320,465✔
1787
        }
1788
      } break;
320,465✔
1789
      case TSFKW_DAY:
32✔
1790
      case TSFKW_Day:
1791
      case TSFKW_day: {
1792
        int32_t idx = strArrayCaseSearch(weekDays, s);
32✔
1793
        if (idx >= 0) {
32!
1794
          s += strlen(weekDays[idx]);
32✔
1795
          wd = idx;
32✔
1796
        } else {
1797
          err = -1;
×
1798
        }
1799
      } break;
32✔
1800
      case TSFKW_DY:
395,189✔
1801
      case TSFKW_Dy:
1802
      case TSFKW_dy: {
1803
        int32_t idx = strArrayCaseSearch(shortWeekDays, s);
395,189✔
1804
        if (idx >= 0) {
395,612!
1805
          s += strlen(shortWeekDays[idx]);
395,901✔
1806
          wd = idx;
395,901✔
1807
        } else {
1808
          err = -1;
×
1809
        }
1810
      } break;
395,612✔
1811
      case TSFKW_DDD: {
352✔
1812
        const char* newPos = tsFormatStr2Int32(&yd, s, 3, needMoreDigits(formats, i));
352✔
1813
        if (NULL == newPos) {
352!
1814
          err = -1;
×
1815
        } else {
1816
          s = newPos;
352✔
1817
        }
1818
        withYD = true;
352✔
1819
      } break;
352✔
1820
      case TSFKW_DD: {
714,916✔
1821
        const char* newPos = tsFormatStr2Int32(&md, s, 2, needMoreDigits(formats, i));
714,916✔
1822
        if (NULL == newPos) {
720,285!
1823
          err = -1;
×
1824
        } else {
1825
          s = newPos;
720,285✔
1826
        }
1827
        withMD = true;
720,285✔
1828
      } break;
720,285✔
1829
      case TSFKW_D: {
320✔
1830
        const char* newPos = tsFormatStr2Int32(&wd, s, 1, needMoreDigits(formats, i));
320✔
1831
        if (NULL == newPos) {
320!
1832
          err = -1;
×
1833
        } else {
1834
          s = newPos;
320✔
1835
        }
1836
      } break;
320✔
1837
      case TSFKW_YYYY: {
320,986✔
1838
        const char* newPos = tsFormatStr2Int32(&year, s, 4, needMoreDigits(formats, i));
320,986✔
1839
        if (NULL == newPos) {
320,986✔
1840
          err = -1;
4✔
1841
        } else {
1842
          s = newPos;
320,982✔
1843
        }
1844
      } break;
320,986✔
1845
      case TSFKW_YYY: {
64✔
1846
        const char* newPos = tsFormatStr2Int32(&year, s, 3, needMoreDigits(formats, i));
64✔
1847
        if (NULL == newPos) {
64!
1848
          err = -1;
×
1849
        } else {
1850
          year = adjustYearTo2020(year);
64✔
1851
          s = newPos;
64✔
1852
        }
1853
      } break;
64✔
1854
      case TSFKW_YY: {
399,073✔
1855
        const char* newPos = tsFormatStr2Int32(&year, s, 2, needMoreDigits(formats, i));
399,073✔
1856
        if (NULL == newPos) {
399,906!
1857
          err = -1;
×
1858
        } else {
1859
          year = adjustYearTo2020(year);
399,906✔
1860
          s = newPos;
399,839✔
1861
        }
1862
      } break;
399,839✔
1863
      case TSFKW_Y: {
64✔
1864
        const char* newPos = tsFormatStr2Int32(&year, s, 1, needMoreDigits(formats, i));
64✔
1865
        if (NULL == newPos) {
64!
1866
          err = -1;
×
1867
        } else {
1868
          year = adjustYearTo2020(year);
64✔
1869
          s = newPos;
64✔
1870
        }
1871
      } break;
64✔
1872
      default:
×
1873
        break;
×
1874
    }
1875
    if (err) {
4,354,347✔
1876
      if (sErrPos) *sErrPos = s;
4!
1877
      if (fErrIdx) *fErrIdx = i;
4!
1878
      return err;
4✔
1879
    }
1880
  }
1881
  if (!withMD) {
995,985✔
1882
    // yyyy-mm-DDD, currently, the c api can't convert to correct timestamp, return not supported
1883
    if (withYD) return -3;
480✔
1884
  }
1885
  struct STm tm = {0};
995,953✔
1886
  tm.tm.tm_year = year - 1900;
995,953✔
1887
  tm.tm.tm_mon = mon;
995,953✔
1888
  tm.tm.tm_yday = yd;
995,953✔
1889
  tm.tm.tm_mday = md;
995,953✔
1890
  tm.tm.tm_wday = wd;
995,953✔
1891
  if (hour12) {
995,953✔
1892
    if (pm && hour < 12)
268!
1893
      tm.tm.tm_hour = hour + 12;
24✔
1894
    else if (!pm && hour == 12)
244!
1895
      tm.tm.tm_hour = 0;
32✔
1896
    else
1897
      tm.tm.tm_hour = hour;
212✔
1898
  } else {
1899
    tm.tm.tm_hour = hour;
995,685✔
1900
  }
1901
  tm.tm.tm_min = min;
995,953✔
1902
  tm.tm.tm_sec = sec;
995,953✔
1903
  if (!checkTm(&tm.tm)) return -2;
995,953✔
1904
  if (tz < -12 || tz > 12) return -2;
719,292!
1905
  tm.fsec = ms * 1000000 + us * 1000 + ns;
719,522✔
1906
  int32_t ret = taosTm2Ts(&tm, ts, precision);
719,522✔
1907
  *ts += 60 * 60 * (tsTimezone - tz) * TICK_PER_SECOND[precision];
721,001✔
1908
  return ret;
721,001✔
1909
}
1910

1911
int32_t taosTs2Char(const char* format, SArray** formats, int64_t ts, int32_t precision, char* out, int32_t outLen) {
800,687✔
1912
  if (!*formats) {
800,687✔
1913
    *formats = taosArrayInit(8, sizeof(TSFormatNode));
1,351✔
1914
    if (!*formats) {
1,351!
1915
      TAOS_RETURN(terrno);
×
1916
    }
1917
    TAOS_CHECK_RETURN(parseTsFormat(format, *formats));
1,351!
1918
  }
1919
  struct STm tm;
1920
  TAOS_CHECK_RETURN(taosTs2Tm(ts, precision, &tm));
800,687!
1921
  return tm2char(*formats, &tm, out, outLen);
803,289✔
1922
}
1923

1924
int32_t taosChar2Ts(const char* format, SArray** formats, const char* tsStr, int64_t* ts, int32_t precision,
721,187✔
1925
                    char* errMsg, int32_t errMsgLen) {
1926
  const char* sErrPos;
1927
  int32_t     fErrIdx;
1928
  if (!*formats) {
721,187✔
1929
    *formats = taosArrayInit(4, sizeof(TSFormatNode));
121,130✔
1930
    if (!*formats) {
121,115!
1931
      TAOS_RETURN(terrno);
×
1932
    }
1933
    TAOS_CHECK_RETURN(parseTsFormat(format, *formats));
121,115!
1934
  }
1935
  int32_t code = char2ts(tsStr, *formats, ts, precision, &sErrPos, &fErrIdx);
721,186✔
1936
  if (code == -1) {
721,034!
UNCOV
1937
    TSFormatNode* fNode = (taosArrayGet(*formats, fErrIdx));
×
1938
    snprintf(errMsg, errMsgLen, "mismatch format for: %s and %s", sErrPos,
4!
1939
             fErrIdx < taosArrayGetSize(*formats) ? ((TSFormatNode*)taosArrayGet(*formats, fErrIdx))->key->name : "");
8✔
1940
    code = TSDB_CODE_FUNC_TO_TIMESTAMP_FAILED_FORMAT_ERR;
4✔
1941
  } else if (code == -2) {
721,035✔
1942
    snprintf(errMsg, errMsgLen, "timestamp format error: %s -> %s", tsStr, format);
8✔
1943
    code = TSDB_CODE_FUNC_TO_TIMESTAMP_FAILED_TS_ERR;
8✔
1944
  } else if (code == -3) {
721,027✔
1945
    snprintf(errMsg, errMsgLen, "timestamp format not supported");
32✔
1946
    code = TSDB_CODE_FUNC_TO_TIMESTAMP_FAILED_NOT_SUPPORTED;
32✔
1947
  }
1948
  TAOS_RETURN(code);
721,039✔
1949
}
1950

1951
int32_t TEST_ts2char(const char* format, int64_t ts, int32_t precision, char* out, int32_t outLen) {
×
1952
  int32_t code = TSDB_CODE_SUCCESS;
×
1953

1954
  SArray* formats = taosArrayInit(4, sizeof(TSFormatNode));
×
1955
  if (!formats) {
×
1956
    TAOS_RETURN(terrno);
×
1957
  }
1958
  TAOS_CHECK_RETURN(parseTsFormat(format, formats));
×
1959
  struct STm tm;
1960
  TAOS_CHECK_GOTO(taosTs2Tm(ts, precision, &tm), NULL, _exit);
×
1961
  TAOS_CHECK_GOTO(tm2char(formats, &tm, out, outLen), NULL, _exit);
×
1962

1963
_exit:
×
1964
  taosArrayDestroy(formats);
×
1965
  TAOS_RETURN(TSDB_CODE_SUCCESS);
×
1966
}
1967

1968
int32_t TEST_char2ts(const char* format, int64_t* ts, int32_t precision, const char* tsStr) {
×
1969
  const char* sErrPos;
1970
  int32_t     fErrIdx;
1971
  SArray*     formats = taosArrayInit(4, sizeof(TSFormatNode));
×
1972
  TAOS_CHECK_RETURN(parseTsFormat(format, formats));
×
1973
  int32_t code = char2ts(tsStr, formats, ts, precision, &sErrPos, &fErrIdx);
×
1974
  if (code == -1) {
×
1975
    (void)printf("failed position: %s\n", sErrPos);
×
1976
    (void)printf("failed format: %s\n", ((TSFormatNode*)taosArrayGet(formats, fErrIdx))->key->name);
×
1977
  }
1978
  taosArrayDestroy(formats);
×
1979
  return code;
×
1980
}
1981

1982
static int8_t UNIT_INDEX[26] = {/*a*/ 2,  0,  -1, 6,  -1, -1, -1,
1983
                                /*h*/ 5,  -1, -1, -1, -1, 4,  8,
1984
                                /*o*/ -1, -1, -1, -1, 3,  -1,
1985
                                /*u*/ 1,  -1, 7,  -1, 9,  -1};
1986

1987
#define GET_UNIT_INDEX(idx) UNIT_INDEX[(idx)-97]
1988

1989
// clang-format off
1990
static int64_t UNIT_MATRIX[10][11] = {     /*  ns,   us,   ms,    s,   min,   h,   d,   w, month, y*/
1991
                                /*ns*/ {   1, 1000,    0},
1992
                                /*us*/ {1000,    1, 1000,    0},
1993
                                /*ms*/ {   0, 1000,    1, 1000,    0},
1994
                                 /*s*/ {   0,    0, 1000,    1,   60,    0},
1995
                               /*min*/ {   0,    0,    0,   60,    1,   60,   0},
1996
                                 /*h*/ {   0,    0,    0,    0,   60,    1,   1,   0},
1997
                                 /*d*/ {   0,    0,    0,    0,    0,   24,   1,   7,   1,   0},
1998
                                 /*w*/ {   0,    0,    0,    0,    0,    0,   7,   1,  -1,   0},
1999
                               /*mon*/ {   0,    0,    0,    0,    0,    0,   0,   0,   1,  12,  0},
2000
                                 /*y*/ {   0,    0,    0,    0,    0,    0,   0,   0,   12,   1,  0}};
2001
// clang-format on
2002

2003
static bool recursiveTsmaCheckRecursive(int64_t baseInterval, int8_t baseIdx, int64_t interval, int8_t idx,
23,923✔
2004
                                        bool checkEq) {
2005
  if (UNIT_MATRIX[baseIdx][idx] == -1) return false;
23,923✔
2006
  if (baseIdx == idx) {
23,651✔
2007
    if (interval < baseInterval) return false;
14,851✔
2008
    if (checkEq && interval == baseInterval) return false;
12,563✔
2009
    return interval % baseInterval == 0;
12,559✔
2010
  }
2011
  int8_t  next = baseIdx + 1;
8,800✔
2012
  int64_t val = UNIT_MATRIX[baseIdx][next];
8,800✔
2013
  while (val != 0 && next <= idx) {
9,484!
2014
    if (val == -1) {
9,274✔
2015
      next++;
202✔
2016
      val = UNIT_MATRIX[baseIdx][next];
202✔
2017
      continue;
202✔
2018
    }
2019
    if (val % baseInterval == 0 || baseInterval % val == 0) {
9,072✔
2020
      int8_t extra = baseInterval >= val ? 0 : 1;
9,056✔
2021
      bool   needCheckEq = baseInterval >= val && !(baseIdx < next && val == 1);
9,056!
2022
      if (!recursiveTsmaCheckRecursive(baseInterval / val + extra, next, interval, idx, needCheckEq && checkEq)) {
9,056!
2023
        next++;
482✔
2024
        val = UNIT_MATRIX[baseIdx][next];
482✔
2025
        continue;
482✔
2026
      } else {
2027
        return true;
8,574✔
2028
      }
2029
    } else {
2030
      return false;
16✔
2031
    }
2032
  }
2033
  return false;
210✔
2034
}
2035

2036
static bool recursiveTsmaCheckRecursiveReverse(int64_t baseInterval, int8_t baseIdx, int64_t interval, int8_t idx,
10,502✔
2037
                                               bool checkEq) {
2038
  if (UNIT_MATRIX[baseIdx][idx] == -1) return false;
10,502!
2039

2040
  if (baseIdx == idx) {
10,502✔
2041
    if (interval < baseInterval) return false;
4,786✔
2042
    if (checkEq && interval == baseInterval) return false;
70!
2043
    return interval % baseInterval == 0;
70✔
2044
  }
2045

2046
  int8_t  next = baseIdx - 1;
5,716✔
2047
  int64_t val = UNIT_MATRIX[baseIdx][next];
5,716✔
2048
  while (val != 0 && next >= 0) {
5,716!
2049
    return recursiveTsmaCheckRecursiveReverse(baseInterval * val, next, interval, idx, checkEq);
5,096✔
2050
  }
2051
  return false;
620✔
2052
}
2053

2054
/*
2055
 * @breif check if tsma with param [interval], [unit] can create based on base tsma with baseInterval and baseUnit
2056
 * @param baseInterval, baseUnit, interval/unit of base tsma
2057
 * @param interval the tsma interval going to create. Not that if unit is not calander unit, then interval has already
2058
 * been translated to TICKS of [precision]
2059
 * @param unit the tsma unit going to create
2060
 * @param precision the precision of this db
2061
 * @param checkEq pass true if same interval is not acceptable, false if acceptable.
2062
 * @ret true the tsma can be created, else cannot
2063
 * */
2064
bool checkRecursiveTsmaInterval(int64_t baseInterval, int8_t baseUnit, int64_t interval, int8_t unit, int8_t precision,
20,273✔
2065
                                bool checkEq) {
2066
  bool baseIsCalendarDuration = IS_CALENDAR_TIME_DURATION(baseUnit);
20,273!
2067
  if (!baseIsCalendarDuration) {
20,273✔
2068
    if (TSDB_CODE_SUCCESS != convertTimeFromPrecisionToUnit(baseInterval, precision, baseUnit, &baseInterval)) {
18,897!
2069
      return false;
×
2070
    }
2071
  }
2072
  bool isCalendarDuration = IS_CALENDAR_TIME_DURATION(unit);
20,273!
2073
  if (!isCalendarDuration) {
20,273✔
2074
    if (TSDB_CODE_SUCCESS != convertTimeFromPrecisionToUnit(interval, precision, unit, &interval)) {
19,035!
2075
      return false;
×
2076
    }
2077
  }
2078

2079
  bool needCheckEq = baseIsCalendarDuration == isCalendarDuration && checkEq;
20,273✔
2080

2081
  int8_t baseIdx = GET_UNIT_INDEX(baseUnit), idx = GET_UNIT_INDEX(unit);
20,273✔
2082
  if (baseIdx <= idx) {
20,273✔
2083
    return recursiveTsmaCheckRecursive(baseInterval, baseIdx, interval, idx, needCheckEq);
14,867✔
2084
  } else {
2085
    return recursiveTsmaCheckRecursiveReverse(baseInterval, baseIdx, interval, idx, checkEq);
5,406✔
2086
  }
2087
  return true;
2088
}
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