Coverage Report

Created: 2023-03-26 06:05

/src/tarantool/src/lib/core/datetime.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * SPDX-License-Identifier: BSD-2-Clause
3
 *
4
 * Copyright 2021, Tarantool AUTHORS, please see AUTHORS file.
5
 */
6
7
#include <math.h>
8
#include <assert.h>
9
#include <limits.h>
10
#include <string.h>
11
#include <stdbool.h>
12
#include <time.h>
13
#include <inttypes.h>
14
15
#define DT_PARSE_ISO_TNT
16
#include "decimal.h"
17
#include "msgpuck.h"
18
#include "c-dt/dt.h"
19
#include "datetime.h"
20
#include "trivia/util.h"
21
#include "tzcode/tzcode.h"
22
#include "tzcode/timezone.h"
23
#include "mp_extension_types.h"
24
25
#include "fiber.h"
26
27
/** floored modulo and divide */
28
0
#define MOD(a, b) (unlikely((a) < 0) ? (((b) + ((a) % (b))) % (b)) : \
29
0
      ((a) % (b)))
30
0
#define DIV(a, b) (unlikely((a) < 0) ? (((a) - (b) + 1) / (b)) : ((a) / (b)))
31
32
/**
33
 * Given the seconds from Epoch (1970-01-01) we calculate date
34
 * since Rata Die (0001-01-01).
35
 * DT_EPOCH_1970_OFFSET is the distance in days from Rata Die to Epoch.
36
 */
37
static int
38
local_dt(int64_t secs)
39
0
{
40
0
  return dt_from_rdn((int)(DIV(secs, SECS_PER_DAY)) +
41
0
         DT_EPOCH_1970_OFFSET);
42
0
}
43
44
static int64_t
45
local_secs(const struct datetime *date)
46
0
{
47
0
  return (int64_t)date->epoch + date->tzoffset * 60;
48
0
}
49
50
/**
51
 * Resolve tzindex encoded timezone from @sa date using Olson facilities.
52
 * @param[in] epoch decode input epoch time (in seconds).
53
 * @param[in] tzindex use timezone index for decode.
54
 * @param[out] gmtoff return resolved timezone offset (in seconds).
55
 * @param[out] isdst return resolved daylight saving time status for the zone.
56
 */
57
static inline bool
58
epoch_timezone_lookup(int64_t epoch, int16_t tzindex, long *gmtoff, int *isdst)
59
0
{
60
0
  if (tzindex == 0)
61
0
    return false;
62
63
0
  struct tnt_tm tm = {.tm_epoch = epoch};
64
0
  if (!timezone_tzindex_lookup(tzindex, &tm))
65
0
    return false;
66
67
0
  *gmtoff = tm.tm_gmtoff;
68
0
  *isdst = tm.tm_isdst;
69
70
0
  return true;
71
0
}
72
73
bool
74
datetime_isdst(const struct datetime *date)
75
0
{
76
0
  int isdst = 0;
77
0
  long gmtoff = 0;
78
79
0
  epoch_timezone_lookup(date->epoch, date->tzindex, &gmtoff, &isdst);
80
0
  return isdst != 0;
81
0
}
82
83
long
84
datetime_gmtoff(const struct datetime *date)
85
0
{
86
0
  int isdst = 0;
87
0
  long gmtoff = date->tzoffset * 60;
88
89
0
  epoch_timezone_lookup(date->epoch, date->tzindex, &gmtoff, &isdst);
90
0
  return gmtoff;
91
0
}
92
93
void
94
datetime_to_tm(const struct datetime *date, struct tnt_tm *tm)
95
0
{
96
0
  struct tm t;
97
0
  memset(&t, 0, sizeof(t));
98
0
  tm->tm_epoch = local_secs(date);
99
0
  dt_to_struct_tm(local_dt(tm->tm_epoch), &t);
100
0
  tm->tm_year = t.tm_year;
101
0
  tm->tm_mon = t.tm_mon;
102
0
  tm->tm_mday = t.tm_mday;
103
0
  tm->tm_wday = t.tm_wday;
104
0
  tm->tm_yday = t.tm_yday;
105
106
0
  tm->tm_gmtoff = date->tzoffset * 60;
107
0
  tm->tm_tzindex = date->tzindex;
108
0
  tm->tm_nsec = date->nsec;
109
110
0
  int seconds_of_day = MOD(tm->tm_epoch, SECS_PER_DAY);
111
0
  tm->tm_hour = (seconds_of_day / 3600) % 24;
112
0
  tm->tm_min = (seconds_of_day / 60) % 60;
113
0
  tm->tm_sec = seconds_of_day % 60;
114
0
}
115
116
size_t
117
datetime_strftime(const struct datetime *date, char *buf, size_t len,
118
      const char *fmt)
119
0
{
120
0
  assert(date != NULL);
121
0
  struct tnt_tm tm;
122
0
  datetime_to_tm(date, &tm);
123
0
  return tnt_strftime(buf, len, fmt, &tm);
124
0
}
125
126
bool
127
tm_to_datetime(struct tnt_tm *tm, struct datetime *date)
128
0
{
129
0
  assert(tm != NULL);
130
0
  assert(date != NULL);
131
0
  int year = tm->tm_year;
132
0
  int mon = tm->tm_mon;
133
0
  int mday = tm->tm_mday;
134
0
  int yday = tm->tm_yday;
135
0
  int wday = tm->tm_wday;
136
0
  dt_t dt = 0;
137
138
0
  if ((year | mon | mday) == 0) {
139
0
    if (yday != 0) {
140
0
      dt = yday - 1 + DT_EPOCH_1970_OFFSET;
141
0
    } else if (wday != 0) {
142
      /* 1970-01-01 was Thursday */
143
0
      dt = ((wday - 4) % 7) + DT_EPOCH_1970_OFFSET;
144
0
    }
145
0
  } else {
146
0
    if (mday == 0)
147
0
      mday = 1;
148
0
    assert(mday >= 1 && mday <= 31);
149
0
    assert(mon >= 0 && mon <= 11);
150
0
    if (dt_from_ymd_checked(year + 1900, mon + 1, mday, &dt) == false)
151
0
      return false;
152
0
  }
153
0
  int64_t local_secs =
154
0
    (int64_t)dt * SECS_PER_DAY - SECS_EPOCH_1970_OFFSET;
155
0
  local_secs += tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;
156
0
  date->epoch = local_secs - tm->tm_gmtoff;
157
0
  date->nsec = tm->tm_nsec;
158
0
  date->tzindex = tm->tm_tzindex;
159
0
  date->tzoffset = tm->tm_gmtoff / 60;
160
0
  return true;
161
0
}
162
163
size_t
164
datetime_strptime(struct datetime *date, const char *buf, const char *fmt)
165
0
{
166
0
  assert(date != NULL);
167
0
  assert(fmt != NULL);
168
0
  assert(buf != NULL);
169
0
  struct tnt_tm t = { .tm_epoch = 0 };
170
0
  char *ret = tnt_strptime(buf, fmt, &t);
171
0
  if (ret == NULL)
172
0
    return 0;
173
0
  if (tm_to_datetime(&t, date) == false)
174
0
    return 0;
175
0
  return ret - buf;
176
0
}
177
178
void
179
datetime_now(struct datetime *now)
180
0
{
181
0
  struct timeval tv;
182
0
  gettimeofday(&tv, NULL);
183
0
  now->epoch = tv.tv_sec;
184
0
  now->nsec = tv.tv_usec * 1000;
185
186
0
  struct tm tm;
187
0
  localtime_r(&tv.tv_sec, &tm);
188
0
  now->tzoffset = tm.tm_gmtoff / 60;
189
0
}
190
191
void
192
datetime_ev_now(struct datetime *now)
193
0
{
194
0
  double timestamp = fiber_time();
195
0
  assert(timestamp > INT32_MIN && timestamp < INT32_MAX);
196
0
  long sec = timestamp;
197
0
  now->epoch = sec;
198
0
  now->nsec = (timestamp - sec) * NANOS_PER_SEC;
199
200
0
  struct tm tm;
201
0
  localtime_r(&sec, &tm);
202
0
  now->tzoffset = tm.tm_gmtoff / 60;
203
0
  now->tzindex = 0;
204
0
}
205
206
/**
207
 * NB! buf may be NULL, and we should handle it gracefully, returning
208
 * calculated length of output string
209
 */
210
size_t
211
datetime_to_string(const struct datetime *date, char *buf, ssize_t len)
212
0
{
213
0
  int offset = date->tzoffset;
214
0
  int tzindex = date->tzindex;
215
0
  int64_t rd_seconds = (int64_t)date->epoch + offset * 60 +
216
0
           SECS_EPOCH_1970_OFFSET;
217
0
  int64_t rd_number = DIV(rd_seconds, SECS_PER_DAY);
218
0
  assert(rd_number <= INT_MAX);
219
0
  assert(rd_number >= INT_MIN);
220
0
  dt_t dt = dt_from_rdn((int)rd_number);
221
222
0
  int year, month, day, second, nanosec, sign;
223
0
  dt_to_ymd(dt, &year, &month, &day);
224
225
0
  rd_seconds = MOD(rd_seconds, SECS_PER_DAY);
226
0
  int hour = (rd_seconds / 3600) % 24;
227
0
  int minute = (rd_seconds / 60) % 60;
228
0
  second = rd_seconds % 60;
229
0
  nanosec = date->nsec;
230
231
0
  size_t sz = 0;
232
0
  SNPRINT(sz, snprintf, buf, len, "%04d-%02d-%02dT%02d:%02d:%02d",
233
0
    year, month, day, hour, minute, second);
234
0
  if (nanosec != 0) {
235
0
    if (nanosec % 1000000 == 0) {
236
0
      SNPRINT(sz, snprintf, buf, len, ".%03d",
237
0
        nanosec / 1000000);
238
239
0
    } else if (nanosec % 1000 == 0) {
240
0
      SNPRINT(sz, snprintf, buf, len, ".%06d",
241
0
        nanosec / 1000);
242
0
    } else {
243
0
      SNPRINT(sz, snprintf, buf, len, ".%09d", nanosec);
244
0
    }
245
0
  }
246
0
  if (tzindex != 0) {
247
0
    const char *tz_name = timezone_name(tzindex);
248
0
    assert(tz_name != NULL);
249
0
    SNPRINT(sz, snprintf, buf, len,
250
0
      tz_name[1] == '\0' ? "%s" : " %s",
251
0
      tz_name);
252
0
  } else if (offset == 0) {
253
0
    SNPRINT(sz, snprintf, buf, len, "Z");
254
0
  } else {
255
0
    if (offset < 0) {
256
0
      sign = '-';
257
0
      offset = -offset;
258
0
    } else {
259
0
      sign = '+';
260
0
    }
261
0
    SNPRINT(sz, snprintf, buf, len, "%c%02d%02d", sign,
262
0
      offset / 60, offset % 60);
263
0
  }
264
0
  return sz;
265
0
}
266
267
static inline int64_t
268
dt_epoch(dt_t dt)
269
2.35k
{
270
2.35k
  return ((int64_t)dt_rdn(dt) - DT_EPOCH_1970_OFFSET) * SECS_PER_DAY;
271
2.35k
}
272
273
/** Common timezone suffix parser */
274
static inline ssize_t
275
parse_tz_suffix(const char *str, size_t len, time_t base,
276
    int16_t *tzindex, int32_t *offset)
277
1.09k
{
278
  /* 1st attempt: decode as MSK */
279
1.09k
  const struct date_time_zone *zone;
280
1.09k
  long gmtoff = 0;
281
1.09k
  ssize_t l = timezone_epoch_lookup(str, len, base, &zone, &gmtoff);
282
1.09k
  if (l < 0)
283
207
    return l;
284
887
  if (l > 0) {
285
612
    assert(zone != NULL);
286
0
    *offset = gmtoff / 60;
287
612
    *tzindex = timezone_index(zone);
288
612
    assert(l <= (ssize_t)len);
289
0
    return l;
290
612
  }
291
292
  /* 2nd attempt: decode as +03:00 */
293
275
  *tzindex = 0;
294
275
  l = dt_parse_iso_zone_lenient(str, len, offset);
295
275
  assert(l <= (ssize_t)len);
296
297
0
  return l;
298
887
}
299
300
ssize_t
301
datetime_parse_full(struct datetime *date, const char *str, size_t len,
302
        const char *tzsuffix, int32_t offset)
303
2.24k
{
304
2.24k
  size_t n;
305
2.24k
  dt_t dt;
306
2.24k
  const char *svp = str;
307
2.24k
  char c;
308
2.24k
  int sec_of_day = 0, nanosecond = 0;
309
2.24k
  int16_t tzindex = 0;
310
311
2.24k
  n = dt_parse_iso_date(str, len, &dt);
312
2.24k
  if (n == 0)
313
529
    return 0;
314
315
1.71k
  str += n;
316
1.71k
  len -= n;
317
1.71k
  if (len <= 0)
318
272
    goto exit;
319
320
1.43k
  c = *str++;
321
1.43k
  if (c != 'T' && c != 't' && c != ' ')
322
33
    return 0;
323
1.40k
  len--;
324
1.40k
  if (len <= 0)
325
3
    goto exit;
326
327
1.40k
  n = dt_parse_iso_time(str, len, &sec_of_day, &nanosecond);
328
1.40k
  if (n == 0)
329
214
    return 0;
330
331
1.18k
  str += n;
332
1.18k
  len -= n;
333
1.18k
  if (len <= 0)
334
94
    goto exit;
335
336
  /* now we have parsed enough of date literal, and we are
337
   * ready to consume timezone suffix, if overridden
338
   */
339
1.09k
  time_t base = dt_epoch(dt) + sec_of_day - offset * 60;
340
1.09k
  ssize_t l;
341
1.09k
  if (tzsuffix != NULL) {
342
0
    l = parse_tz_suffix(tzsuffix, strlen(tzsuffix), base,
343
0
            &tzindex, &offset);
344
0
    if (l < 0)
345
0
      return l;
346
0
    goto exit;
347
0
  }
348
349
1.09k
  if (*str == ' ') {
350
3
    str++;
351
3
    len--;
352
3
  }
353
1.09k
  if (len <= 0)
354
1
    goto exit;
355
356
1.09k
  l = parse_tz_suffix(str, len, base, &tzindex, &offset);
357
1.09k
  if (l < 0)
358
207
    return l;
359
887
  str += l;
360
361
1.25k
exit:
362
1.25k
  date->epoch = dt_epoch(dt) + sec_of_day - offset * 60;
363
1.25k
  date->nsec = nanosecond;
364
1.25k
  date->tzoffset = offset;
365
1.25k
  date->tzindex = tzindex;
366
367
1.25k
  return str - svp;
368
887
}
369
370
ssize_t
371
datetime_parse_tz(const char *str, size_t len, time_t base, int16_t *tzoffset,
372
      int16_t *tzindex)
373
0
{
374
0
  int32_t offset = 0;
375
0
  ssize_t l = parse_tz_suffix(str, len, base, tzindex, &offset);
376
0
  if (l <= 0)
377
0
    return l;
378
0
  assert(offset <= INT16_MAX);
379
0
  *tzoffset = offset;
380
0
  return l;
381
0
}
382
383
int
384
datetime_compare(const struct datetime *lhs, const struct datetime *rhs)
385
0
{
386
0
  int result = COMPARE_RESULT(lhs->epoch, rhs->epoch);
387
0
  if (result != 0)
388
0
    return result;
389
390
0
  return COMPARE_RESULT(lhs->nsec, rhs->nsec);
391
0
}
392
393
static inline int64_t
394
dt_seconds(const struct datetime *date)
395
0
{
396
0
  return (int64_t)date->epoch + date->tzoffset * 60 +
397
0
         SECS_EPOCH_1970_OFFSET;
398
0
}
399
400
int64_t
401
datetime_year(const struct datetime *date)
402
0
{
403
0
  int64_t rd_seconds = dt_seconds(date);
404
0
  int64_t rd_number = DIV(rd_seconds, SECS_PER_DAY);
405
0
  assert(rd_number <= INT_MAX && rd_number >= INT_MIN);
406
0
  dt_t dt = dt_from_rdn((int)rd_number);
407
0
  int year;
408
0
  int day;
409
0
  dt_to_yd(dt, &year, &day);
410
0
  return year;
411
0
}
412
413
int64_t
414
datetime_quarter(const struct datetime *date)
415
0
{
416
0
  int64_t rd_seconds = dt_seconds(date);
417
0
  int64_t rd_number = DIV(rd_seconds, SECS_PER_DAY);
418
0
  assert(rd_number <= INT_MAX && rd_number >= INT_MIN);
419
0
  dt_t dt = dt_from_rdn((int)rd_number);
420
0
  int year;
421
0
  int quarter;
422
0
  int day;
423
0
  dt_to_yqd(dt, &year, &quarter, &day);
424
0
  return quarter;
425
0
}
426
427
int64_t
428
datetime_month(const struct datetime *date)
429
0
{
430
0
  int64_t rd_seconds = dt_seconds(date);
431
0
  int64_t rd_number = DIV(rd_seconds, SECS_PER_DAY);
432
0
  assert(rd_number <= INT_MAX && rd_number >= INT_MIN);
433
0
  dt_t dt = dt_from_rdn((int)rd_number);
434
0
  int year;
435
0
  int month;
436
0
  int day;
437
0
  dt_to_ymd(dt, &year, &month, &day);
438
0
  return month;
439
0
}
440
441
int64_t
442
datetime_week(const struct datetime *date)
443
0
{
444
0
  int64_t rd_seconds = dt_seconds(date);
445
0
  int64_t rd_number = DIV(rd_seconds, SECS_PER_DAY);
446
0
  assert(rd_number <= INT_MAX && rd_number >= INT_MIN);
447
0
  dt_t dt = dt_from_rdn((int)rd_number);
448
0
  int year;
449
0
  int week;
450
0
  int day;
451
0
  dt_to_ywd(dt, &year, &week, &day);
452
0
  return week;
453
0
}
454
455
int64_t
456
datetime_day(const struct datetime *date)
457
0
{
458
0
  int64_t rd_seconds = dt_seconds(date);
459
0
  int64_t rd_number = DIV(rd_seconds, SECS_PER_DAY);
460
0
  assert(rd_number <= INT_MAX && rd_number >= INT_MIN);
461
0
  dt_t dt = dt_from_rdn((int)rd_number);
462
0
  int year;
463
0
  int month;
464
0
  int day;
465
0
  dt_to_ymd(dt, &year, &month, &day);
466
0
  return day;
467
0
}
468
469
int64_t
470
datetime_dow(const struct datetime *date)
471
0
{
472
0
  int64_t rd_seconds = dt_seconds(date);
473
0
  int64_t rd_number = DIV(rd_seconds, SECS_PER_DAY);
474
0
  assert(rd_number <= INT_MAX && rd_number >= INT_MIN);
475
0
  dt_t dt = dt_from_rdn((int)rd_number);
476
0
  return (int64_t)dt_dow(dt);
477
0
}
478
479
int64_t
480
datetime_doy(const struct datetime *date)
481
0
{
482
0
  int64_t rd_seconds = dt_seconds(date);
483
0
  int64_t rd_number = DIV(rd_seconds, SECS_PER_DAY);
484
0
  assert(rd_number <= INT_MAX && rd_number >= INT_MIN);
485
0
  dt_t dt = dt_from_rdn((int)rd_number);
486
0
  int year;
487
0
  int day;
488
0
  dt_to_yd(dt, &year, &day);
489
0
  return day;
490
0
}
491
492
int64_t
493
datetime_hour(const struct datetime *date)
494
0
{
495
0
  int64_t rd_seconds = dt_seconds(date);
496
0
  int64_t hour = (MOD(rd_seconds, SECS_PER_DAY) / 3600) % 24;
497
0
  return hour;
498
0
}
499
500
int64_t
501
datetime_min(const struct datetime *date)
502
0
{
503
0
  int64_t rd_seconds = dt_seconds(date);
504
0
  int64_t minute = (MOD(rd_seconds, SECS_PER_DAY) / 60) % 60;
505
0
  return minute;
506
0
}
507
508
int64_t
509
datetime_sec(const struct datetime *date)
510
0
{
511
0
  int64_t rd_seconds = dt_seconds(date);
512
0
  int64_t second = MOD(rd_seconds, 60);
513
0
  return second;
514
0
}
515
516
int64_t
517
datetime_tzoffset(const struct datetime *date)
518
0
{
519
0
  return date->tzoffset;
520
0
}
521
522
int64_t
523
datetime_epoch(const struct datetime *date)
524
0
{
525
0
  return date->epoch;
526
0
}
527
528
int64_t
529
datetime_nsec(const struct datetime *date)
530
0
{
531
0
  return date->nsec;
532
0
}
533
534
/**
535
 * Interval support functions: stringization and operations
536
 */
537
bool
538
datetime_totable(const struct datetime *date, struct interval *out)
539
0
{
540
0
  int64_t secs = local_secs(date);
541
0
  int64_t dt = local_dt(secs);
542
543
0
  out->year = dt_year(dt);
544
0
  out->month = dt_month(dt);
545
0
  out->week = 0;
546
0
  out->day = dt_dom(dt);
547
0
  out->hour = (secs / 3600) % 24;
548
0
  out->min = (secs / 60) % 60;
549
0
  out->sec = secs % 60;
550
0
  out->nsec = date->nsec;
551
0
  out->adjust = DT_LIMIT;
552
553
0
  return true;
554
0
}
555
556
/**
557
 * Interval support functions: stringization and operations
558
 */
559
560
#define SPACE() \
561
0
  do { \
562
0
    if (sz > 0) { \
563
0
      SNPRINT(sz, snprintf, buf, len, ", "); \
564
0
    } \
565
0
  } while (0)
566
567
size_t
568
interval_to_string(const struct interval *ival, char *buf, ssize_t len)
569
0
{
570
0
  static const char *const long_signed_fmt[] = {
571
0
    "%" PRId64, /* false */
572
0
    "%+" PRId64,  /* true */
573
0
  };
574
0
  static const char *const signed_fmt[] = {
575
0
    "%d", /* false */
576
0
    "%+d",  /* true */
577
0
  };
578
579
0
  size_t sz = 0;
580
0
  if (ival->year != 0) {
581
0
    SNPRINT(sz, snprintf, buf, len, "%+d years", ival->year);
582
0
  }
583
0
  if (ival->month != 0) {
584
0
    SPACE();
585
0
    SNPRINT(sz, snprintf, buf, len, signed_fmt[sz == 0],
586
0
      ival->month);
587
0
    SNPRINT(sz, snprintf, buf, len, " months");
588
0
  }
589
0
  if (ival->week != 0) {
590
0
    SPACE();
591
0
    SNPRINT(sz, snprintf, buf, len, signed_fmt[sz == 0],
592
0
      ival->week);
593
0
    SNPRINT(sz, snprintf, buf, len, " weeks");
594
0
  }
595
0
  int64_t days = (int64_t)ival->day;
596
0
  if (days != 0) {
597
0
    SPACE();
598
0
    SNPRINT(sz, snprintf, buf, len, long_signed_fmt[sz == 0],
599
0
      days);
600
0
    SNPRINT(sz, snprintf, buf, len, " days");
601
0
  }
602
0
  int64_t hours = (int64_t)ival->hour;
603
0
  if (ival->hour != 0) {
604
0
    SPACE();
605
0
    SNPRINT(sz, snprintf, buf, len, long_signed_fmt[sz == 0],
606
0
      hours);
607
0
    SNPRINT(sz, snprintf, buf, len, " hours");
608
0
  }
609
0
  int64_t minutes = (int64_t)ival->min;
610
0
  if (minutes != 0) {
611
0
    SPACE();
612
0
    SNPRINT(sz, snprintf, buf, len, long_signed_fmt[sz == 0],
613
0
      minutes);
614
0
    SNPRINT(sz, snprintf, buf, len, " minutes");
615
0
  }
616
617
0
  int64_t secs = (int64_t)ival->sec;
618
0
  if (secs != 0 || sz == 0) {
619
0
    SPACE();
620
0
    SNPRINT(sz, snprintf, buf, len, long_signed_fmt[sz == 0],
621
0
      secs);
622
0
    SNPRINT(sz, snprintf, buf, len, " seconds");
623
0
  }
624
0
  int32_t nsec = ival->nsec;
625
0
  if (nsec != 0) {
626
0
    SPACE();
627
0
    SNPRINT(sz, snprintf, buf, len, signed_fmt[sz == 0],
628
0
      nsec);
629
0
    SNPRINT(sz, snprintf, buf, len, " nanoseconds");
630
0
  }
631
0
  return sz;
632
0
}
633
634
/**
635
 * Normalize seconds and nanoseconds:
636
 * - make sure that nanoseconds part is positive
637
 * - make sure it's not exceeding maximum allowed value
638
 */
639
static void
640
normalize_nsec(int64_t *psecs, int *pnsec)
641
0
{
642
0
  assert(psecs != NULL);
643
0
  assert(pnsec != NULL);
644
0
  int64_t secs = *psecs;
645
0
  int nsec = *pnsec;
646
647
0
  if (nsec < 0 || nsec >= NANOS_PER_SEC) {
648
0
    secs += nsec / NANOS_PER_SEC;
649
0
    nsec %= NANOS_PER_SEC;
650
0
  }
651
0
  *psecs = secs;
652
0
  *pnsec = nsec;
653
0
}
654
655
static inline int64_t
656
utc_secs(int64_t epoch, int tzoffset)
657
0
{
658
0
  return epoch - tzoffset * 60;
659
0
}
660
661
/** minimum supported date - -5879610-06-22 */
662
0
#define MIN_DATE_YEAR -5879610LL
663
#define MIN_DATE_MONTH 6
664
#define MIN_DATE_DAY 22
665
666
/** maximum supported date - 5879611-07-11 */
667
0
#define MAX_DATE_YEAR 5879611LL
668
#define MAX_DATE_MONTH 7
669
#define MAX_DATE_DAY 11
670
/**
671
 * In the Julian calendar, the average year length is
672
 * 365 1/4 days = 365.25 days. This gives an error of
673
 * about 1 day in 128 years.
674
 */
675
0
#define AVERAGE_DAYS_YEAR 365.25
676
0
#define AVERAGE_DAYS_MONTH (AVERAGE_DAYS_YEAR / 12)
677
0
#define AVERAGE_WEEK_YEAR  (AVERAGE_DAYS_YEAR / 7)
678
679
0
#define MAX_YEAR_RANGE (MAX_DATE_YEAR - MIN_DATE_YEAR)
680
0
#define MAX_MONTH_RANGE (MAX_YEAR_RANGE * 12)
681
0
#define MAX_WEEK_RANGE (MAX_YEAR_RANGE * AVERAGE_WEEK_YEAR)
682
0
#define MAX_DAY_RANGE (MAX_YEAR_RANGE * AVERAGE_DAYS_YEAR)
683
0
#define MAX_HOUR_RANGE (MAX_DAY_RANGE * 24)
684
0
#define MAX_MIN_RANGE (MAX_HOUR_RANGE * 60)
685
0
#define MAX_SEC_RANGE (MAX_DAY_RANGE * SECS_PER_DAY)
686
0
#define MAX_NSEC_RANGE ((int64_t)INT_MAX)
687
688
static inline int
689
verify_range(int64_t v, int64_t from, int64_t to)
690
0
{
691
0
  return (v < from) ? -1 : (v > to ? +1 : 0);
692
0
}
693
694
static inline int
695
verify_dt(int64_t dt)
696
0
{
697
0
  return verify_range(dt, INT_MIN, INT_MAX);
698
0
}
699
700
int
701
datetime_increment_by(struct datetime *self, int direction,
702
          const struct interval *ival)
703
0
{
704
0
  int64_t secs = local_secs(self);
705
0
  int64_t dt = local_dt(secs);
706
0
  int nsec = self->nsec;
707
0
  int offset = self->tzoffset;
708
0
  int tzindex = self->tzindex;
709
710
0
  bool is_ymd_updated = false;
711
0
  int64_t years = ival->year;
712
0
  int64_t months = ival->month;
713
0
  int64_t weeks = ival->week;
714
0
  int64_t days = ival->day;
715
0
  int64_t hours = ival->hour;
716
0
  int64_t minutes = ival->min;
717
0
  int64_t seconds = ival->sec;
718
0
  int nanoseconds = ival->nsec;
719
0
  dt_adjust_t adjust = ival->adjust;
720
0
  int rc = 0;
721
722
0
  if (years != 0) {
723
0
    rc = verify_dt(dt + direction * years * AVERAGE_DAYS_YEAR);
724
0
    if (rc != 0)
725
0
      return rc;
726
    /* tnt_dt_add_years() not handle properly DT_SNAP or DT_LIMIT
727
     * mode so use tnt_dt_add_months() as a work-around
728
     */
729
0
    dt = dt_add_months(dt, direction * years * 12, adjust);
730
0
    is_ymd_updated = true;
731
0
  }
732
0
  if (months != 0) {
733
0
    rc = verify_dt(dt + direction * months * AVERAGE_DAYS_MONTH);
734
0
    if (rc != 0)
735
0
      return rc;
736
737
0
    dt = dt_add_months(dt, direction * months, adjust);
738
0
    is_ymd_updated = true;
739
0
  }
740
0
  if (weeks != 0) {
741
0
    rc = verify_dt(dt + direction * weeks * 7);
742
0
    if (rc != 0)
743
0
      return rc;
744
745
0
    dt += direction * weeks * 7;
746
0
    is_ymd_updated = true;
747
0
  }
748
0
  if (days != 0) {
749
0
    rc = verify_dt(dt + direction * days);
750
0
    if (rc != 0)
751
0
      return rc;
752
753
0
    dt += direction * days;
754
0
    is_ymd_updated = true;
755
0
  }
756
757
0
  if (is_ymd_updated) {
758
0
    secs = dt * SECS_PER_DAY - SECS_EPOCH_1970_OFFSET +
759
0
           secs % SECS_PER_DAY;
760
0
  }
761
762
0
  if (hours != 0) {
763
0
    rc = verify_range(secs + direction * hours * 3600,
764
0
          MIN_EPOCH_SECS_VALUE, MAX_EPOCH_SECS_VALUE);
765
0
    if (rc != 0)
766
0
      return rc;
767
768
0
    secs += direction * hours * 3600;
769
0
  }
770
0
  if (minutes != 0) {
771
0
    rc = verify_range(secs + direction * minutes * 60,
772
0
          MIN_EPOCH_SECS_VALUE, MAX_EPOCH_SECS_VALUE);
773
0
    if (rc != 0)
774
0
      return rc;
775
776
0
    secs += direction * minutes * 60;
777
0
  }
778
0
  if (seconds != 0) {
779
0
    rc = verify_range(secs + direction * seconds,
780
0
          MIN_EPOCH_SECS_VALUE, MAX_EPOCH_SECS_VALUE);
781
0
    if (rc != 0)
782
0
      return rc;
783
784
0
    secs += direction * seconds;
785
0
  }
786
0
  if (nanoseconds != 0)
787
0
    nsec += direction * nanoseconds;
788
789
0
  normalize_nsec(&secs, &nsec);
790
0
  rc = verify_dt((secs + SECS_EPOCH_1970_OFFSET) / SECS_PER_DAY);
791
0
  if (rc != 0)
792
0
    return rc;
793
794
0
  if (tzindex != 0) {
795
0
    int isdst = 0;
796
0
    long gmtoff = offset * 60;
797
0
    epoch_timezone_lookup(secs, tzindex, &gmtoff, &isdst);
798
0
    offset = gmtoff / 60;
799
0
  }
800
0
  self->epoch = utc_secs(secs, offset);
801
0
  self->nsec = nsec;
802
0
  self->tzoffset = offset;
803
0
  return 0;
804
0
}
805
806
/**
807
 * Check attributes of interval record after prior operation
808
 */
809
static int
810
interval_check_args(const struct interval *ival)
811
0
{
812
0
  int rc = verify_range(ival->year, -MAX_YEAR_RANGE, MAX_YEAR_RANGE);
813
0
  if (rc != 0)
814
0
    return rc * CHECK_YEARS;
815
0
  rc = verify_range(ival->month, -MAX_MONTH_RANGE, MAX_MONTH_RANGE);
816
0
  if (rc != 0)
817
0
    return rc * CHECK_MONTHS;
818
0
  rc = verify_range(ival->week, -MAX_WEEK_RANGE, MAX_WEEK_RANGE);
819
0
  if (rc != 0)
820
0
    return rc * CHECK_WEEKS;
821
0
  rc = verify_range(ival->day, -MAX_DAY_RANGE, MAX_DAY_RANGE);
822
0
  if (rc != 0)
823
0
    return rc * CHECK_DAYS;
824
0
  rc = verify_range(ival->hour, -MAX_HOUR_RANGE, MAX_HOUR_RANGE);
825
0
  if (rc != 0)
826
0
    return rc * CHECK_HOURS;
827
0
  rc = verify_range(ival->min, -MAX_MIN_RANGE, MAX_MIN_RANGE);
828
0
  if (rc != 0)
829
0
    return rc * CHECK_MINUTES;
830
0
  rc = verify_range(ival->sec, -MAX_SEC_RANGE, MAX_SEC_RANGE);
831
0
  if (rc != 0)
832
0
    return rc * CHECK_SECONDS;
833
0
  return verify_range(ival->nsec, -MAX_NSEC_RANGE, MAX_NSEC_RANGE) *
834
0
    CHECK_NANOSECS;
835
0
}
836
837
int
838
datetime_datetime_sub(struct interval *res, const struct datetime *lhs,
839
          const struct datetime *rhs)
840
0
{
841
0
  assert(res != NULL);
842
0
  assert(lhs != NULL);
843
0
  assert(rhs != NULL);
844
0
  struct interval inv_rhs;
845
0
  datetime_totable(lhs, res);
846
0
  datetime_totable(rhs, &inv_rhs);
847
0
  res->min -= lhs->tzoffset - rhs->tzoffset;
848
0
  return interval_interval_sub(res, &inv_rhs);
849
0
}
850
851
int
852
interval_interval_sub(struct interval *lhs, const struct interval *rhs)
853
0
{
854
0
  assert(lhs != NULL);
855
0
  assert(rhs != NULL);
856
0
  lhs->year -= rhs->year;
857
0
  lhs->month -= rhs->month;
858
0
  lhs->week -= rhs->week;
859
0
  lhs->day -= rhs->day;
860
0
  lhs->hour -= rhs->hour;
861
0
  lhs->min -= rhs->min;
862
0
  lhs->sec -= rhs->sec;
863
0
  lhs->nsec -= rhs->nsec;
864
0
  return interval_check_args(lhs);
865
0
}
866
867
int
868
interval_interval_add(struct interval *lhs, const struct interval *rhs)
869
0
{
870
0
  assert(lhs != NULL);
871
0
  assert(rhs != NULL);
872
0
  lhs->year += rhs->year;
873
0
  lhs->month += rhs->month;
874
0
  lhs->day += rhs->day;
875
0
  lhs->week += rhs->week;
876
0
  lhs->hour += rhs->hour;
877
0
  lhs->min += rhs->min;
878
0
  lhs->sec += rhs->sec;
879
0
  lhs->nsec += rhs->nsec;
880
0
  return interval_check_args(lhs);
881
0
}
882
883
/** This structure contains information about the given date and time fields. */
884
struct dt_fields {
885
  /* Specified year. */
886
  double year;
887
  /* Specified month. */
888
  double month;
889
  /* Specified day. */
890
  double day;
891
  /* Specified hour. */
892
  double hour;
893
  /* Specified minute. */
894
  double min;
895
  /* Specified second. */
896
  double sec;
897
  /* Specified millisecond. */
898
  double msec;
899
  /* Specified microsecond. */
900
  double usec;
901
  /* Specified nanosecond. */
902
  double nsec;
903
  /* Specified timestamp. */
904
  double timestamp;
905
  /* Specified timezone offset. */
906
  double tzoffset;
907
  /* Number of given fields among msec, usec and nsec. */
908
  int count_usec;
909
  /* True, if any of year, month, day, hour, min or sec is specified. */
910
  bool is_ymdhms;
911
  /* True, if timestamp is specified. */
912
  bool is_ts;
913
};
914
915
/** Parse msgpack value and convert it to double, if possible. */
916
static int
917
get_double_from_mp(const char **data, double *value)
918
0
{
919
0
  switch (mp_typeof(**data)) {
920
0
  case MP_INT:
921
0
    *value = mp_decode_int(data);
922
0
    break;
923
0
  case MP_UINT:
924
0
    *value = mp_decode_uint(data);
925
0
    break;
926
0
  case MP_DOUBLE:
927
0
    *value = mp_decode_double(data);
928
0
    break;
929
0
  case MP_EXT: {
930
0
    int8_t type;
931
0
    uint32_t len = mp_decode_extl(data, &type);
932
0
    if (type != MP_DECIMAL)
933
0
      return -1;
934
0
    decimal_t dec;
935
0
    if (decimal_unpack(data, len, &dec) == NULL)
936
0
      return -1;
937
0
    *value = atof(decimal_str(&dec));
938
0
    break;
939
0
  }
940
0
  default:
941
0
    return -1;
942
0
  }
943
0
  return 0;
944
0
}
945
946
/** Parse msgpack value and convert it to int32, if possible. */
947
static int
948
get_int32_from_mp(const char **data, int32_t *value)
949
0
{
950
0
  switch (mp_typeof(**data)) {
951
0
  case MP_INT: {
952
0
    int64_t val = mp_decode_int(data);
953
0
    if (val < INT32_MIN)
954
0
      return -1;
955
0
    *value = val;
956
0
    break;
957
0
  }
958
0
  case MP_UINT: {
959
0
    uint64_t val = mp_decode_uint(data);
960
0
    if (val > INT32_MAX)
961
0
      return -1;
962
0
    *value = val;
963
0
    break;
964
0
  }
965
0
  case MP_DOUBLE: {
966
0
    double val = mp_decode_double(data);
967
0
    if (val > (double)INT32_MAX || val < (double)INT32_MIN)
968
0
      return -1;
969
0
    if (val != floor(val))
970
0
      return -1;
971
0
    *value = val;
972
0
    break;
973
0
  }
974
0
  case MP_EXT: {
975
0
    int8_t type;
976
0
    uint32_t len = mp_decode_extl(data, &type);
977
0
    if (type != MP_DECIMAL)
978
0
      return -1;
979
0
    decimal_t dec;
980
0
    if (decimal_unpack(data, len, &dec) == NULL)
981
0
      return -1;
982
0
    if (!decimal_is_int(&dec))
983
0
      return -1;
984
0
    int64_t val;
985
0
    if (decimal_to_int64(&dec, &val) == NULL)
986
0
      return -1;
987
0
    if (val < INT32_MIN || val > INT32_MAX)
988
0
      return -1;
989
0
    *value = val;
990
0
    break;
991
0
  }
992
0
  default:
993
0
    return -1;
994
0
  }
995
0
  return 0;
996
0
}
997
998
/** Define field of DATETIME value from field of given MAP value.*/
999
static int
1000
map_field_to_dt_field(struct dt_fields *fields, const char **data)
1001
0
{
1002
0
  if (mp_typeof(**data) != MP_STR) {
1003
0
    mp_next(data);
1004
0
    mp_next(data);
1005
0
    return 0;
1006
0
  }
1007
0
  uint32_t size;
1008
0
  const char *str = mp_decode_str(data, &size);
1009
0
  double *value;
1010
0
  if (strncmp(str, "year", size) == 0) {
1011
0
    value = &fields->year;
1012
0
    fields->is_ymdhms = true;
1013
0
  } else if (strncmp(str, "month", size) == 0) {
1014
0
    value = &fields->month;
1015
0
    fields->is_ymdhms = true;
1016
0
  } else if (strncmp(str, "day", size) == 0) {
1017
0
    value = &fields->day;
1018
0
    fields->is_ymdhms = true;
1019
0
  } else if (strncmp(str, "hour", size) == 0) {
1020
0
    value = &fields->hour;
1021
0
    fields->is_ymdhms = true;
1022
0
  } else if (strncmp(str, "min", size) == 0) {
1023
0
    value = &fields->min;
1024
0
    fields->is_ymdhms = true;
1025
0
  } else if (strncmp(str, "sec", size) == 0) {
1026
0
    value = &fields->sec;
1027
0
    fields->is_ymdhms = true;
1028
0
  } else if (strncmp(str, "msec", size) == 0) {
1029
0
    value = &fields->msec;
1030
0
    ++fields->count_usec;
1031
0
  } else if (strncmp(str, "usec", size) == 0) {
1032
0
    value = &fields->usec;
1033
0
    ++fields->count_usec;
1034
0
  } else if (strncmp(str, "nsec", size) == 0) {
1035
0
    value = &fields->nsec;
1036
0
    ++fields->count_usec;
1037
0
  } else if (strncmp(str, "timestamp", size) == 0) {
1038
0
    value = &fields->timestamp;
1039
0
    fields->is_ts = true;
1040
0
  } else if (strncmp(str, "tzoffset", size) == 0) {
1041
0
    value = &fields->tzoffset;
1042
0
  } else {
1043
0
    mp_next(data);
1044
0
    return 0;
1045
0
  }
1046
0
  return get_double_from_mp(data, value);
1047
0
}
1048
1049
/** Create a DATETIME value using fields of the DATETIME. */
1050
static int
1051
datetime_from_fields(struct datetime *dt, const struct dt_fields *fields)
1052
0
{
1053
0
  if (fields->count_usec > 1)
1054
0
    return -1;
1055
0
  double nsec = fields->msec * 1000000 + fields->usec * 1000 +
1056
0
          fields->nsec;
1057
0
  if (nsec < 0 || nsec >= MAX_NANOS_PER_SEC)
1058
0
    return -1;
1059
0
  if (fields->tzoffset < -720 || fields->tzoffset > 840)
1060
0
    return -1;
1061
0
  if (fields->timestamp < (double)INT32_MIN * SECS_PER_DAY ||
1062
0
      fields->timestamp > (double)INT32_MAX * SECS_PER_DAY)
1063
0
    return -1;
1064
0
  if (fields->is_ts) {
1065
0
    if (fields->is_ymdhms)
1066
0
      return -1;
1067
0
    double timestamp = floor(fields->timestamp);
1068
0
    double frac = fields->timestamp - timestamp;
1069
0
    if (frac != 0) {
1070
0
      if (fields->count_usec > 0)
1071
0
        return -1;
1072
0
      nsec = frac * NANOS_PER_SEC;
1073
0
    }
1074
0
    dt->epoch = timestamp;
1075
0
    dt->nsec = nsec;
1076
0
    dt->tzoffset = fields->tzoffset;
1077
0
    dt->tzindex = 0;
1078
0
    return 0;
1079
0
  }
1080
0
  if (fields->year < MIN_DATE_YEAR || fields->year > MAX_DATE_YEAR)
1081
0
    return -1;
1082
0
  if (fields->month < 1 || fields->month > 12)
1083
0
    return -1;
1084
0
  if (fields->day < 1 ||
1085
0
      fields->day > dt_days_in_month(fields->year, fields->month))
1086
0
    return -1;
1087
0
  if (fields->hour < 0 || fields->hour > 23)
1088
0
    return -1;
1089
0
  if (fields->min < 0 || fields->min > 59)
1090
0
    return -1;
1091
0
  if (fields->sec < 0 || fields->sec > 60)
1092
0
    return -1;
1093
0
  double days = dt_from_ymd(fields->year, fields->month, fields->day) -
1094
0
          DT_EPOCH_1970_OFFSET;
1095
0
  dt->epoch = days * SECS_PER_DAY + fields->hour * 3600 +
1096
0
        fields->min * 60 + fields->sec;
1097
0
  dt->nsec = nsec;
1098
0
  dt->tzoffset = fields->tzoffset;
1099
0
  dt->tzindex = 0;
1100
0
  return 0;
1101
0
}
1102
1103
int
1104
datetime_from_map(struct datetime *dt, const char *data)
1105
0
{
1106
0
  assert(mp_typeof(*data) == MP_MAP);
1107
0
  uint32_t len = mp_decode_map(&data);
1108
0
  struct dt_fields fields;
1109
0
  memset(&fields, 0, sizeof(fields));
1110
0
  fields.year = 1970;
1111
0
  fields.month = 1;
1112
0
  fields.day = 1;
1113
0
  for (uint32_t i = 0; i < len; ++i) {
1114
0
    if (map_field_to_dt_field(&fields, &data) != 0)
1115
0
      return -1;
1116
0
  }
1117
0
  return datetime_from_fields(dt, &fields);
1118
0
}
1119
1120
/** Define field of INTERVAL value from field of given MAP value.*/
1121
static int
1122
map_field_to_itv_field(struct interval *itv, const char **data)
1123
0
{
1124
0
  if (mp_typeof(**data) != MP_STR) {
1125
0
    mp_next(data);
1126
0
    mp_next(data);
1127
0
    return 0;
1128
0
  }
1129
0
  uint32_t size;
1130
0
  const char *str = mp_decode_str(data, &size);
1131
0
  double *dvalue = NULL;
1132
0
  int32_t *ivalue = NULL;
1133
0
  if (strncmp(str, "year", size) == 0) {
1134
0
    ivalue = &itv->year;
1135
0
  } else if (strncmp(str, "month", size) == 0) {
1136
0
    ivalue = &itv->month;
1137
0
  } else if (strncmp(str, "week", size) == 0) {
1138
0
    ivalue = &itv->week;
1139
0
  } else if (strncmp(str, "day", size) == 0) {
1140
0
    dvalue = &itv->day;
1141
0
  } else if (strncmp(str, "hour", size) == 0) {
1142
0
    dvalue = &itv->hour;
1143
0
  } else if (strncmp(str, "min", size) == 0) {
1144
0
    dvalue = &itv->min;
1145
0
  } else if (strncmp(str, "sec", size) == 0) {
1146
0
    dvalue = &itv->sec;
1147
0
  } else if (strncmp(str, "nsec", size) == 0) {
1148
0
    ivalue = &itv->nsec;
1149
0
  } else if (strncmp(str, "adjust", size) == 0) {
1150
0
    if (mp_typeof(**data) != MP_STR)
1151
0
      return -1;
1152
0
    uint32_t vsize;
1153
0
    const char *val = mp_decode_str(data, &vsize);
1154
0
    if (strncasecmp(val, "none", vsize) == 0)
1155
0
      itv->adjust = DT_LIMIT;
1156
0
    else if (strncasecmp(val, "last", vsize) == 0)
1157
0
      itv->adjust = DT_SNAP;
1158
0
    else if (strncasecmp(val, "excess", vsize) == 0)
1159
0
      itv->adjust = DT_EXCESS;
1160
0
    else
1161
0
      return -1;
1162
0
    return 0;
1163
0
  } else {
1164
0
    mp_next(data);
1165
0
    return 0;
1166
0
  }
1167
0
  if (dvalue != NULL) {
1168
0
    double val;
1169
0
    if (get_double_from_mp(data, &val) != 0)
1170
0
      return -1;
1171
0
    if (val != floor(val))
1172
0
      return -1;
1173
0
    *dvalue = val;
1174
0
    return 0;
1175
0
  }
1176
0
  assert(ivalue != NULL);
1177
0
  return get_int32_from_mp(data, ivalue);
1178
0
}
1179
1180
int
1181
interval_from_map(struct interval *itv, const char *data)
1182
0
{
1183
0
  assert(mp_typeof(*data) == MP_MAP);
1184
0
  uint32_t len = mp_decode_map(&data);
1185
0
  memset(itv, 0, sizeof(*itv));
1186
0
  itv->adjust = DT_LIMIT;
1187
0
  for (uint32_t i = 0; i < len; ++i) {
1188
0
    if (map_field_to_itv_field(itv, &data) != 0)
1189
0
      return -1;
1190
0
  }
1191
0
  return interval_check_args(itv) == 0 ? 0 : -1;
1192
0
}