Coverage Report

Created: 2023-11-19 07:29

/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
293k
{
129
293k
  assert(tm != NULL);
130
0
  assert(date != NULL);
131
0
  int year = tm->tm_year;
132
293k
  int mon = tm->tm_mon;
133
293k
  int mday = tm->tm_mday;
134
293k
  int yday = tm->tm_yday;
135
293k
  int wday = tm->tm_wday;
136
293k
  dt_t dt = 0;
137
138
293k
  if ((year | mon | mday) == 0) {
139
658
    if (yday != 0) {
140
15
      dt = yday - 1 + DT_EPOCH_1970_OFFSET;
141
643
    } else if (wday != 0) {
142
      /* 1970-01-01 was Thursday */
143
17
      dt = ((wday - 4) % 7) + DT_EPOCH_1970_OFFSET;
144
17
    }
145
293k
  } else {
146
293k
    if (mday == 0)
147
64
      mday = 1;
148
293k
    assert(mday >= 1 && mday <= 31);
149
0
    assert(mon >= 0 && mon <= 11);
150
293k
    if (dt_from_ymd_checked(year + 1900, mon + 1, mday, &dt) == false)
151
7
      return false;
152
293k
  }
153
293k
  int64_t local_secs =
154
293k
    (int64_t)dt * SECS_PER_DAY - SECS_EPOCH_1970_OFFSET;
155
293k
  local_secs += tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;
156
293k
  date->epoch = local_secs - tm->tm_gmtoff;
157
293k
  date->nsec = tm->tm_nsec;
158
293k
  date->tzindex = tm->tm_tzindex;
159
293k
  date->tzoffset = tm->tm_gmtoff / 60;
160
293k
  return true;
161
293k
}
162
163
size_t
164
datetime_strptime(struct datetime *date, const char *buf, const char *fmt)
165
1.26k
{
166
1.26k
  assert(date != NULL);
167
0
  assert(fmt != NULL);
168
0
  assert(buf != NULL);
169
0
  struct tnt_tm t = { .tm_epoch = 0 };
170
1.26k
  char *ret = tnt_strptime(buf, fmt, &t);
171
1.26k
  if (ret == NULL)
172
405
    return 0;
173
862
  if (tm_to_datetime(&t, date) == false)
174
6
    return 0;
175
856
  return ret - buf;
176
862
}
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
1.98k
{
270
1.98k
  return ((int64_t)dt_rdn(dt) - DT_EPOCH_1970_OFFSET) * SECS_PER_DAY;
271
1.98k
}
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
908
{
278
  /* 1st attempt: decode as MSK */
279
908
  const struct date_time_zone *zone;
280
908
  long gmtoff = 0;
281
908
  ssize_t l = timezone_epoch_lookup(str, len, base, &zone, &gmtoff);
282
908
  if (l < 0)
283
175
    return l;
284
733
  if (l > 0) {
285
483
    assert(zone != NULL);
286
0
    *offset = gmtoff / 60;
287
483
    *tzindex = timezone_index(zone);
288
483
    assert(l <= (ssize_t)len);
289
0
    return l;
290
483
  }
291
292
  /* 2nd attempt: decode as +03:00 */
293
250
  *tzindex = 0;
294
250
  l = dt_parse_iso_zone_lenient(str, len, offset);
295
250
  assert(l <= (ssize_t)len);
296
297
0
  return l;
298
733
}
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
1.95k
{
304
1.95k
  size_t n;
305
1.95k
  dt_t dt;
306
1.95k
  const char *svp = str;
307
1.95k
  char c;
308
1.95k
  int sec_of_day = 0, nanosecond = 0;
309
1.95k
  int16_t tzindex = 0;
310
311
1.95k
  n = dt_parse_iso_date(str, len, &dt);
312
1.95k
  if (n == 0)
313
464
    return 0;
314
315
1.49k
  str += n;
316
1.49k
  len -= n;
317
1.49k
  if (len <= 0)
318
242
    goto exit;
319
320
1.24k
  c = *str++;
321
1.24k
  if (c != 'T' && c != 't' && c != ' ')
322
32
    return 0;
323
1.21k
  len--;
324
1.21k
  if (len <= 0)
325
3
    goto exit;
326
327
1.21k
  n = dt_parse_iso_time(str, len, &sec_of_day, &nanosecond);
328
1.21k
  if (n == 0)
329
211
    return 0;
330
331
1.00k
  str += n;
332
1.00k
  len -= n;
333
1.00k
  if (len <= 0)
334
93
    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
910
  time_t base = dt_epoch(dt) + sec_of_day - offset * 60;
340
910
  ssize_t l;
341
910
  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
910
  if (*str == ' ') {
350
7
    str++;
351
7
    len--;
352
7
  }
353
910
  if (len <= 0)
354
2
    goto exit;
355
356
908
  l = parse_tz_suffix(str, len, base, &tzindex, &offset);
357
908
  if (l < 0)
358
175
    return l;
359
733
  str += l;
360
361
1.07k
exit:
362
1.07k
  date->epoch = dt_epoch(dt) + sec_of_day - offset * 60;
363
1.07k
  date->nsec = nanosecond;
364
1.07k
  date->tzoffset = offset;
365
1.07k
  date->tzindex = tzindex;
366
367
1.07k
  return str - svp;
368
733
}
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
    if (nsec < 0) {
651
0
      secs -= 1;
652
0
      nsec += NANOS_PER_SEC;
653
0
    }
654
0
  }
655
0
  *psecs = secs;
656
0
  *pnsec = nsec;
657
0
}
658
659
static inline int64_t
660
utc_secs(int64_t epoch, int tzoffset)
661
0
{
662
0
  return epoch - tzoffset * 60;
663
0
}
664
665
/** minimum supported date - -5879610-06-22 */
666
0
#define MIN_DATE_YEAR -5879610LL
667
#define MIN_DATE_MONTH 6
668
#define MIN_DATE_DAY 22
669
670
/** maximum supported date - 5879611-07-11 */
671
0
#define MAX_DATE_YEAR 5879611LL
672
#define MAX_DATE_MONTH 7
673
#define MAX_DATE_DAY 11
674
/**
675
 * In the Julian calendar, the average year length is
676
 * 365 1/4 days = 365.25 days. This gives an error of
677
 * about 1 day in 128 years.
678
 */
679
0
#define AVERAGE_DAYS_YEAR 365.25
680
0
#define AVERAGE_DAYS_MONTH (AVERAGE_DAYS_YEAR / 12)
681
0
#define AVERAGE_WEEK_YEAR  (AVERAGE_DAYS_YEAR / 7)
682
683
0
#define MAX_YEAR_RANGE (MAX_DATE_YEAR - MIN_DATE_YEAR)
684
0
#define MAX_MONTH_RANGE (MAX_YEAR_RANGE * 12)
685
0
#define MAX_WEEK_RANGE (MAX_YEAR_RANGE * AVERAGE_WEEK_YEAR)
686
0
#define MAX_DAY_RANGE (MAX_YEAR_RANGE * AVERAGE_DAYS_YEAR)
687
0
#define MAX_HOUR_RANGE (MAX_DAY_RANGE * 24)
688
0
#define MAX_MIN_RANGE (MAX_HOUR_RANGE * 60)
689
0
#define MAX_SEC_RANGE (MAX_DAY_RANGE * SECS_PER_DAY)
690
0
#define MAX_NSEC_RANGE ((int64_t)INT_MAX)
691
692
static inline int
693
verify_range(int64_t v, int64_t from, int64_t to)
694
0
{
695
0
  return (v < from) ? -1 : (v > to ? +1 : 0);
696
0
}
697
698
static inline int
699
verify_dt(int64_t dt)
700
0
{
701
0
  return verify_range(dt, INT_MIN, INT_MAX);
702
0
}
703
704
int
705
datetime_increment_by(struct datetime *self, int direction,
706
          const struct interval *ival)
707
0
{
708
0
  int64_t secs = local_secs(self);
709
0
  int64_t dt = local_dt(secs);
710
0
  int nsec = self->nsec;
711
0
  int offset = self->tzoffset;
712
0
  int tzindex = self->tzindex;
713
714
0
  bool is_ymd_updated = false;
715
0
  int64_t years = ival->year;
716
0
  int64_t months = ival->month;
717
0
  int64_t weeks = ival->week;
718
0
  int64_t days = ival->day;
719
0
  int64_t hours = ival->hour;
720
0
  int64_t minutes = ival->min;
721
0
  int64_t seconds = ival->sec;
722
0
  int nanoseconds = ival->nsec;
723
0
  dt_adjust_t adjust = ival->adjust;
724
0
  int rc = 0;
725
726
0
  if (years != 0) {
727
0
    rc = verify_dt(dt + direction * years * AVERAGE_DAYS_YEAR);
728
0
    if (rc != 0)
729
0
      return rc;
730
    /* tnt_dt_add_years() not handle properly DT_SNAP or DT_LIMIT
731
     * mode so use tnt_dt_add_months() as a work-around
732
     */
733
0
    dt = dt_add_months(dt, direction * years * 12, adjust);
734
0
    is_ymd_updated = true;
735
0
  }
736
0
  if (months != 0) {
737
0
    rc = verify_dt(dt + direction * months * AVERAGE_DAYS_MONTH);
738
0
    if (rc != 0)
739
0
      return rc;
740
741
0
    dt = dt_add_months(dt, direction * months, adjust);
742
0
    is_ymd_updated = true;
743
0
  }
744
0
  if (weeks != 0) {
745
0
    rc = verify_dt(dt + direction * weeks * 7);
746
0
    if (rc != 0)
747
0
      return rc;
748
749
0
    dt += direction * weeks * 7;
750
0
    is_ymd_updated = true;
751
0
  }
752
0
  if (days != 0) {
753
0
    rc = verify_dt(dt + direction * days);
754
0
    if (rc != 0)
755
0
      return rc;
756
757
0
    dt += direction * days;
758
0
    is_ymd_updated = true;
759
0
  }
760
761
0
  if (is_ymd_updated) {
762
0
    secs = dt * SECS_PER_DAY - SECS_EPOCH_1970_OFFSET +
763
0
           secs % SECS_PER_DAY;
764
0
  }
765
766
0
  if (hours != 0) {
767
0
    rc = verify_range(secs + direction * hours * 3600,
768
0
          MIN_EPOCH_SECS_VALUE, MAX_EPOCH_SECS_VALUE);
769
0
    if (rc != 0)
770
0
      return rc;
771
772
0
    secs += direction * hours * 3600;
773
0
  }
774
0
  if (minutes != 0) {
775
0
    rc = verify_range(secs + direction * minutes * 60,
776
0
          MIN_EPOCH_SECS_VALUE, MAX_EPOCH_SECS_VALUE);
777
0
    if (rc != 0)
778
0
      return rc;
779
780
0
    secs += direction * minutes * 60;
781
0
  }
782
0
  if (seconds != 0) {
783
0
    rc = verify_range(secs + direction * seconds,
784
0
          MIN_EPOCH_SECS_VALUE, MAX_EPOCH_SECS_VALUE);
785
0
    if (rc != 0)
786
0
      return rc;
787
788
0
    secs += direction * seconds;
789
0
  }
790
0
  if (nanoseconds != 0)
791
0
    nsec += direction * nanoseconds;
792
793
0
  normalize_nsec(&secs, &nsec);
794
0
  rc = verify_dt((secs + SECS_EPOCH_1970_OFFSET) / SECS_PER_DAY);
795
0
  if (rc != 0)
796
0
    return rc;
797
798
0
  if (tzindex != 0) {
799
0
    int isdst = 0;
800
0
    long gmtoff = offset * 60;
801
0
    epoch_timezone_lookup(secs, tzindex, &gmtoff, &isdst);
802
0
    offset = gmtoff / 60;
803
0
  }
804
0
  self->epoch = utc_secs(secs, offset);
805
0
  self->nsec = nsec;
806
0
  self->tzoffset = offset;
807
0
  return 0;
808
0
}
809
810
/**
811
 * Check attributes of interval record after prior operation
812
 */
813
static int
814
interval_check_args(const struct interval *ival)
815
0
{
816
0
  int rc = verify_range(ival->year, -MAX_YEAR_RANGE, MAX_YEAR_RANGE);
817
0
  if (rc != 0)
818
0
    return rc * CHECK_YEARS;
819
0
  rc = verify_range(ival->month, -MAX_MONTH_RANGE, MAX_MONTH_RANGE);
820
0
  if (rc != 0)
821
0
    return rc * CHECK_MONTHS;
822
0
  rc = verify_range(ival->week, -MAX_WEEK_RANGE, MAX_WEEK_RANGE);
823
0
  if (rc != 0)
824
0
    return rc * CHECK_WEEKS;
825
0
  rc = verify_range(ival->day, -MAX_DAY_RANGE, MAX_DAY_RANGE);
826
0
  if (rc != 0)
827
0
    return rc * CHECK_DAYS;
828
0
  rc = verify_range(ival->hour, -MAX_HOUR_RANGE, MAX_HOUR_RANGE);
829
0
  if (rc != 0)
830
0
    return rc * CHECK_HOURS;
831
0
  rc = verify_range(ival->min, -MAX_MIN_RANGE, MAX_MIN_RANGE);
832
0
  if (rc != 0)
833
0
    return rc * CHECK_MINUTES;
834
0
  rc = verify_range(ival->sec, -MAX_SEC_RANGE, MAX_SEC_RANGE);
835
0
  if (rc != 0)
836
0
    return rc * CHECK_SECONDS;
837
0
  return verify_range(ival->nsec, -MAX_NSEC_RANGE, MAX_NSEC_RANGE) *
838
0
    CHECK_NANOSECS;
839
0
}
840
841
int
842
datetime_datetime_sub(struct interval *res, const struct datetime *lhs,
843
          const struct datetime *rhs)
844
0
{
845
0
  assert(res != NULL);
846
0
  assert(lhs != NULL);
847
0
  assert(rhs != NULL);
848
0
  struct interval inv_rhs;
849
0
  datetime_totable(lhs, res);
850
0
  datetime_totable(rhs, &inv_rhs);
851
0
  res->min -= lhs->tzoffset - rhs->tzoffset;
852
0
  return interval_interval_sub(res, &inv_rhs);
853
0
}
854
855
int
856
interval_interval_sub(struct interval *lhs, const struct interval *rhs)
857
0
{
858
0
  assert(lhs != NULL);
859
0
  assert(rhs != NULL);
860
0
  lhs->year -= rhs->year;
861
0
  lhs->month -= rhs->month;
862
0
  lhs->week -= rhs->week;
863
0
  lhs->day -= rhs->day;
864
0
  lhs->hour -= rhs->hour;
865
0
  lhs->min -= rhs->min;
866
0
  lhs->sec -= rhs->sec;
867
0
  lhs->nsec -= rhs->nsec;
868
0
  return interval_check_args(lhs);
869
0
}
870
871
int
872
interval_interval_add(struct interval *lhs, const struct interval *rhs)
873
0
{
874
0
  assert(lhs != NULL);
875
0
  assert(rhs != NULL);
876
0
  lhs->year += rhs->year;
877
0
  lhs->month += rhs->month;
878
0
  lhs->day += rhs->day;
879
0
  lhs->week += rhs->week;
880
0
  lhs->hour += rhs->hour;
881
0
  lhs->min += rhs->min;
882
0
  lhs->sec += rhs->sec;
883
0
  lhs->nsec += rhs->nsec;
884
0
  return interval_check_args(lhs);
885
0
}
886
887
/** This structure contains information about the given date and time fields. */
888
struct dt_fields {
889
  /* Specified year. */
890
  double year;
891
  /* Specified month. */
892
  double month;
893
  /* Specified day. */
894
  double day;
895
  /* Specified hour. */
896
  double hour;
897
  /* Specified minute. */
898
  double min;
899
  /* Specified second. */
900
  double sec;
901
  /* Specified millisecond. */
902
  double msec;
903
  /* Specified microsecond. */
904
  double usec;
905
  /* Specified nanosecond. */
906
  double nsec;
907
  /* Specified timestamp. */
908
  double timestamp;
909
  /* Specified timezone offset. */
910
  double tzoffset;
911
  /* Number of given fields among msec, usec and nsec. */
912
  int count_usec;
913
  /* True, if any of year, month, day, hour, min or sec is specified. */
914
  bool is_ymdhms;
915
  /* True, if timestamp is specified. */
916
  bool is_ts;
917
};
918
919
/** Parse msgpack value and convert it to double, if possible. */
920
static int
921
get_double_from_mp(const char **data, double *value)
922
0
{
923
0
  switch (mp_typeof(**data)) {
924
0
  case MP_INT:
925
0
    *value = mp_decode_int(data);
926
0
    break;
927
0
  case MP_UINT:
928
0
    *value = mp_decode_uint(data);
929
0
    break;
930
0
  case MP_DOUBLE:
931
0
    *value = mp_decode_double(data);
932
0
    break;
933
0
  case MP_EXT: {
934
0
    int8_t type;
935
0
    uint32_t len = mp_decode_extl(data, &type);
936
0
    if (type != MP_DECIMAL)
937
0
      return -1;
938
0
    decimal_t dec;
939
0
    if (decimal_unpack(data, len, &dec) == NULL)
940
0
      return -1;
941
0
    *value = atof(decimal_str(&dec));
942
0
    break;
943
0
  }
944
0
  default:
945
0
    return -1;
946
0
  }
947
0
  return 0;
948
0
}
949
950
/** Parse msgpack value and convert it to int32, if possible. */
951
static int
952
get_int32_from_mp(const char **data, int32_t *value)
953
0
{
954
0
  switch (mp_typeof(**data)) {
955
0
  case MP_INT: {
956
0
    int64_t val = mp_decode_int(data);
957
0
    if (val < INT32_MIN)
958
0
      return -1;
959
0
    *value = val;
960
0
    break;
961
0
  }
962
0
  case MP_UINT: {
963
0
    uint64_t val = mp_decode_uint(data);
964
0
    if (val > INT32_MAX)
965
0
      return -1;
966
0
    *value = val;
967
0
    break;
968
0
  }
969
0
  case MP_DOUBLE: {
970
0
    double val = mp_decode_double(data);
971
0
    if (val > (double)INT32_MAX || val < (double)INT32_MIN)
972
0
      return -1;
973
0
    if (val != floor(val))
974
0
      return -1;
975
0
    *value = val;
976
0
    break;
977
0
  }
978
0
  case MP_EXT: {
979
0
    int8_t type;
980
0
    uint32_t len = mp_decode_extl(data, &type);
981
0
    if (type != MP_DECIMAL)
982
0
      return -1;
983
0
    decimal_t dec;
984
0
    if (decimal_unpack(data, len, &dec) == NULL)
985
0
      return -1;
986
0
    if (!decimal_is_int(&dec))
987
0
      return -1;
988
0
    int64_t val;
989
0
    if (decimal_to_int64(&dec, &val) == NULL)
990
0
      return -1;
991
0
    if (val < INT32_MIN || val > INT32_MAX)
992
0
      return -1;
993
0
    *value = val;
994
0
    break;
995
0
  }
996
0
  default:
997
0
    return -1;
998
0
  }
999
0
  return 0;
1000
0
}
1001
1002
/** Define field of DATETIME value from field of given MAP value.*/
1003
static int
1004
map_field_to_dt_field(struct dt_fields *fields, const char **data)
1005
0
{
1006
0
  if (mp_typeof(**data) != MP_STR) {
1007
0
    mp_next(data);
1008
0
    mp_next(data);
1009
0
    return 0;
1010
0
  }
1011
0
  uint32_t size;
1012
0
  const char *str = mp_decode_str(data, &size);
1013
0
  double *value;
1014
0
  if (strncmp(str, "year", size) == 0) {
1015
0
    value = &fields->year;
1016
0
    fields->is_ymdhms = true;
1017
0
  } else if (strncmp(str, "month", size) == 0) {
1018
0
    value = &fields->month;
1019
0
    fields->is_ymdhms = true;
1020
0
  } else if (strncmp(str, "day", size) == 0) {
1021
0
    value = &fields->day;
1022
0
    fields->is_ymdhms = true;
1023
0
  } else if (strncmp(str, "hour", size) == 0) {
1024
0
    value = &fields->hour;
1025
0
    fields->is_ymdhms = true;
1026
0
  } else if (strncmp(str, "min", size) == 0) {
1027
0
    value = &fields->min;
1028
0
    fields->is_ymdhms = true;
1029
0
  } else if (strncmp(str, "sec", size) == 0) {
1030
0
    value = &fields->sec;
1031
0
    fields->is_ymdhms = true;
1032
0
  } else if (strncmp(str, "msec", size) == 0) {
1033
0
    value = &fields->msec;
1034
0
    ++fields->count_usec;
1035
0
  } else if (strncmp(str, "usec", size) == 0) {
1036
0
    value = &fields->usec;
1037
0
    ++fields->count_usec;
1038
0
  } else if (strncmp(str, "nsec", size) == 0) {
1039
0
    value = &fields->nsec;
1040
0
    ++fields->count_usec;
1041
0
  } else if (strncmp(str, "timestamp", size) == 0) {
1042
0
    value = &fields->timestamp;
1043
0
    fields->is_ts = true;
1044
0
  } else if (strncmp(str, "tzoffset", size) == 0) {
1045
0
    value = &fields->tzoffset;
1046
0
  } else {
1047
0
    mp_next(data);
1048
0
    return 0;
1049
0
  }
1050
0
  return get_double_from_mp(data, value);
1051
0
}
1052
1053
/** Create a DATETIME value using fields of the DATETIME. */
1054
static int
1055
datetime_from_fields(struct datetime *dt, const struct dt_fields *fields)
1056
0
{
1057
0
  if (fields->count_usec > 1)
1058
0
    return -1;
1059
0
  double nsec = fields->msec * 1000000 + fields->usec * 1000 +
1060
0
          fields->nsec;
1061
0
  if (nsec < 0 || nsec >= MAX_NANOS_PER_SEC)
1062
0
    return -1;
1063
0
  if (fields->tzoffset < -720 || fields->tzoffset > 840)
1064
0
    return -1;
1065
0
  if (fields->timestamp < (double)INT32_MIN * SECS_PER_DAY ||
1066
0
      fields->timestamp > (double)INT32_MAX * SECS_PER_DAY)
1067
0
    return -1;
1068
0
  if (fields->is_ts) {
1069
0
    if (fields->is_ymdhms)
1070
0
      return -1;
1071
0
    double timestamp = floor(fields->timestamp);
1072
0
    double frac = fields->timestamp - timestamp;
1073
0
    if (frac != 0) {
1074
0
      if (fields->count_usec > 0)
1075
0
        return -1;
1076
0
      nsec = frac * NANOS_PER_SEC;
1077
0
    }
1078
0
    dt->epoch = timestamp;
1079
0
    dt->nsec = nsec;
1080
0
    dt->tzoffset = fields->tzoffset;
1081
0
    dt->tzindex = 0;
1082
0
    return 0;
1083
0
  }
1084
0
  if (fields->year < MIN_DATE_YEAR || fields->year > MAX_DATE_YEAR)
1085
0
    return -1;
1086
0
  if (fields->month < 1 || fields->month > 12)
1087
0
    return -1;
1088
0
  if (fields->day < 1 ||
1089
0
      fields->day > dt_days_in_month(fields->year, fields->month))
1090
0
    return -1;
1091
0
  if (fields->hour < 0 || fields->hour > 23)
1092
0
    return -1;
1093
0
  if (fields->min < 0 || fields->min > 59)
1094
0
    return -1;
1095
0
  if (fields->sec < 0 || fields->sec > 60)
1096
0
    return -1;
1097
0
  double days = dt_from_ymd(fields->year, fields->month, fields->day) -
1098
0
          DT_EPOCH_1970_OFFSET;
1099
0
  dt->epoch = days * SECS_PER_DAY + fields->hour * 3600 +
1100
0
        fields->min * 60 + fields->sec;
1101
0
  dt->nsec = nsec;
1102
0
  dt->tzoffset = fields->tzoffset;
1103
0
  dt->tzindex = 0;
1104
0
  return 0;
1105
0
}
1106
1107
int
1108
datetime_from_map(struct datetime *dt, const char *data)
1109
0
{
1110
0
  assert(mp_typeof(*data) == MP_MAP);
1111
0
  uint32_t len = mp_decode_map(&data);
1112
0
  struct dt_fields fields;
1113
0
  memset(&fields, 0, sizeof(fields));
1114
0
  fields.year = 1970;
1115
0
  fields.month = 1;
1116
0
  fields.day = 1;
1117
0
  for (uint32_t i = 0; i < len; ++i) {
1118
0
    if (map_field_to_dt_field(&fields, &data) != 0)
1119
0
      return -1;
1120
0
  }
1121
0
  return datetime_from_fields(dt, &fields);
1122
0
}
1123
1124
/** Define field of INTERVAL value from field of given MAP value.*/
1125
static int
1126
map_field_to_itv_field(struct interval *itv, const char **data)
1127
0
{
1128
0
  if (mp_typeof(**data) != MP_STR) {
1129
0
    mp_next(data);
1130
0
    mp_next(data);
1131
0
    return 0;
1132
0
  }
1133
0
  uint32_t size;
1134
0
  const char *str = mp_decode_str(data, &size);
1135
0
  double *dvalue = NULL;
1136
0
  int32_t *ivalue = NULL;
1137
0
  if (strncmp(str, "year", size) == 0) {
1138
0
    ivalue = &itv->year;
1139
0
  } else if (strncmp(str, "month", size) == 0) {
1140
0
    ivalue = &itv->month;
1141
0
  } else if (strncmp(str, "week", size) == 0) {
1142
0
    ivalue = &itv->week;
1143
0
  } else if (strncmp(str, "day", size) == 0) {
1144
0
    dvalue = &itv->day;
1145
0
  } else if (strncmp(str, "hour", size) == 0) {
1146
0
    dvalue = &itv->hour;
1147
0
  } else if (strncmp(str, "min", size) == 0) {
1148
0
    dvalue = &itv->min;
1149
0
  } else if (strncmp(str, "sec", size) == 0) {
1150
0
    dvalue = &itv->sec;
1151
0
  } else if (strncmp(str, "nsec", size) == 0) {
1152
0
    ivalue = &itv->nsec;
1153
0
  } else if (strncmp(str, "adjust", size) == 0) {
1154
0
    if (mp_typeof(**data) != MP_STR)
1155
0
      return -1;
1156
0
    uint32_t vsize;
1157
0
    const char *val = mp_decode_str(data, &vsize);
1158
0
    if (strncasecmp(val, "none", vsize) == 0)
1159
0
      itv->adjust = DT_LIMIT;
1160
0
    else if (strncasecmp(val, "last", vsize) == 0)
1161
0
      itv->adjust = DT_SNAP;
1162
0
    else if (strncasecmp(val, "excess", vsize) == 0)
1163
0
      itv->adjust = DT_EXCESS;
1164
0
    else
1165
0
      return -1;
1166
0
    return 0;
1167
0
  } else {
1168
0
    mp_next(data);
1169
0
    return 0;
1170
0
  }
1171
0
  if (dvalue != NULL) {
1172
0
    double val;
1173
0
    if (get_double_from_mp(data, &val) != 0)
1174
0
      return -1;
1175
0
    if (val != floor(val))
1176
0
      return -1;
1177
0
    *dvalue = val;
1178
0
    return 0;
1179
0
  }
1180
0
  assert(ivalue != NULL);
1181
0
  return get_int32_from_mp(data, ivalue);
1182
0
}
1183
1184
int
1185
interval_from_map(struct interval *itv, const char *data)
1186
0
{
1187
0
  assert(mp_typeof(*data) == MP_MAP);
1188
0
  uint32_t len = mp_decode_map(&data);
1189
0
  memset(itv, 0, sizeof(*itv));
1190
0
  itv->adjust = DT_LIMIT;
1191
0
  for (uint32_t i = 0; i < len; ++i) {
1192
0
    if (map_field_to_itv_field(itv, &data) != 0)
1193
0
      return -1;
1194
0
  }
1195
0
  return interval_check_args(itv) == 0 ? 0 : -1;
1196
0
}