/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 | } |