1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V.
7 */
8
9/* In this file we implement three new types with supporting code.
10 * The types are:
11 *
12 * - daytime - representing a time-of-day between 00:00:00 (included)
13 * and 24:00:00 (not included);
14 * - date - representing a date between the year -4712 and 170050;
15 * - timestamp - a combination of date and daytime, representing an
16 * exact point in time.
17 *
18 * Dates, both in the date and the timestamp types, are represented in
19 * the so-called proleptic Gregorian calendar, that is to say, the
20 * Gregorian calendar (which is in common use today) is extended
21 * backwards. See e.g.
22 * <https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar>.
23 *
24 * Times, both in the daytime and the timestamp types, are recorded
25 * with microsecond precision.
26 */
27
28#include "monetdb_config.h"
29#include "gdk.h"
30#include "mtime.h"
31#include "mal_exception.h"
32
33#ifndef HAVE_STRPTIME
34extern char *strptime(const char *, const char *, struct tm *);
35#endif
36
37#define YEAR_MIN (-4712) /* 4713 BC */
38
39#define YEAR_OFFSET (-YEAR_MIN)
40#define DTDAY_WIDTH 5 /* 1..28/29/30/31, depending on month */
41#define DTDAY_SHIFT 0
42#define DTMONTH_WIDTH 21 /* enough for 174761 years (and 8 months) */
43#define DTMONTH_SHIFT (DTDAY_WIDTH+DTDAY_SHIFT)
44
45#define YEAR_MAX (YEAR_MIN+(1<<DTMONTH_WIDTH)/12-1)
46
47#define isdate(y, m, d) ((m) > 0 && (m) <= 12 && (d) > 0 && (y) >= YEAR_MIN && (y) <= YEAR_MAX && (d) <= monthdays(y, m))
48#define mkdate(y, m, d) ((date) (((uint32_t) (((y) + YEAR_OFFSET) * 12 + (m) - 1) << DTMONTH_SHIFT) \
49 | ((uint32_t) (d) << DTDAY_SHIFT)))
50#define date_extract_day(dt) ((int) (((uint32_t) (dt) >> DTDAY_SHIFT) & ((1 << DTDAY_WIDTH) - 1)))
51#define date_extract_month(dt) ((int) ((((uint32_t) (dt) >> DTMONTH_SHIFT) & ((1 << DTMONTH_WIDTH) - 1)) % 12 + 1))
52#define date_extract_year(dt) ((int) ((((uint32_t) (dt) >> DTMONTH_SHIFT) & ((1 << DTMONTH_WIDTH) - 1)) / 12 - YEAR_OFFSET))
53#define date_extract_century(dt) (date_extract_year(dt) > 0 ? (date_extract_year(dt) - 1) / 100 + 1 : -((-date_extract_year(dt) - 1) / 100 + 1))
54#define date_extract_decade(dt) (date_extract_year(dt) / 10)
55#define date_extract_quarter(dt) ((date_extract_month(dt) - 1) / 3 + 1)
56
57#define istime(h,m,s,u) ((h) >= 0 && (h) < 24 && (m) >= 0 && (m) < 60 && (s) >= 0 && (s) <= 60 && (u) >= 0 && (u) < 1000000)
58#define mkdaytime(h,m,s,u) (((((daytime) (h) * 60 + (m)) * 60) + (s)) * LL_CONSTANT(1000000) + (u))
59
60#define daytime_extract_hour(tm) ((int) (tm / HOUR_USEC))
61#define daytime_extract_minute(tm) ((int) ((tm / 60000000) % 60))
62#define daytime_extract_usecond(tm) ((int) (tm % 60000000)) /* includes seconds */
63
64#define TSTIME_WIDTH 37 /* [0..24*60*60*1000000) */
65#define TSTIME_SHIFT 0
66#define TSDATE_WIDTH (DTDAY_WIDTH+DTMONTH_WIDTH)
67#define TSDATE_SHIFT (TSTIME_SHIFT+TSTIME_WIDTH)
68#define ts_time(ts) ((daytime) (((uint64_t) (ts) >> TSTIME_SHIFT) & ((LL_CONSTANT(1) << TSTIME_WIDTH) - 1)))
69#define ts_date(ts) ((date) (((uint64_t) (ts) >> TSDATE_SHIFT) & ((1 << TSDATE_WIDTH) - 1)))
70#define mktimestamp(d, t) ((timestamp) (((uint64_t) (d) << TSDATE_SHIFT) | \
71 ((uint64_t) (t) << TSTIME_SHIFT)))
72
73#define unixepoch mktimestamp(mkdate(1970, 1, 1), mkdaytime(0, 0, 0, 0))
74
75static const int leapdays[13] = { /* days per month in leap year */
76 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
77};
78static const int cumdays[13] = { /* cumulative days in non leap year */
79 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
80};
81#define isleapyear(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
82#define monthdays(y, m) ((m) != 2 ? leapdays[m] : 28 + isleapyear(y))
83
84int TYPE_date;
85int TYPE_daytime;
86int TYPE_timestamp;
87
88date
89date_create(int year, int month, int day)
90{
91 return isdate(year, month, day) ? mkdate(year, month, day) : date_nil;
92}
93
94int
95date_year(date dt)
96{
97 if (is_date_nil(dt))
98 return int_nil;
99 return date_extract_year(dt);
100}
101
102int
103date_month(date dt)
104{
105 if (is_date_nil(dt))
106 return int_nil;
107 return date_extract_month(dt);
108}
109
110int
111date_day(date dt)
112{
113 if (is_date_nil(dt))
114 return int_nil;
115 return date_extract_day(dt);
116}
117
118date
119date_add_day(date dt, int days)
120{
121 if (is_date_nil(dt) || is_int_nil(days))
122 return date_nil;
123
124 if (abs(days) >= 1 << (DTDAY_WIDTH + DTMONTH_WIDTH))
125 return date_nil; /* overflow for sure */
126
127 int y = date_extract_year(dt);
128 int m = date_extract_month(dt);
129 int d = date_extract_day(dt);
130
131 d += days;
132 while (d <= 0) {
133 if (--m == 0) {
134 m = 12;
135 if (--y < YEAR_MIN)
136 return date_nil;
137 }
138 d += monthdays(y, m);
139 }
140 while (d > monthdays(y, m)) {
141 d -= monthdays(y, m);
142 if (++m > 12) {
143 m = 1;
144 if (++y > YEAR_MAX)
145 return date_nil;
146 }
147 }
148 return mkdate(y, m, d);
149}
150
151date
152date_add_month(date dt, int months)
153{
154 if (is_date_nil(dt) || is_int_nil(months))
155 return date_nil;
156
157 if (abs(months) >= 1 << DTMONTH_WIDTH)
158 return date_nil; /* overflow for sure */
159
160 int y = date_extract_year(dt);
161 int m = date_extract_month(dt);
162 int d = date_extract_day(dt);
163 m += months;
164 if (m <= 0) {
165 y -= (12 - m) / 12;
166 if (y < YEAR_MIN)
167 return date_nil;
168 m = 12 - (-m % 12);
169 } else if (m > 12) {
170 y += (m - 1) / 12;
171 if (y > YEAR_MAX)
172 return date_nil;
173 m = (m - 1) % 12 + 1;
174 }
175 if (d > monthdays(y, m)) {
176 d -= monthdays(y, m);
177 if (++m > 12) {
178 m = 1;
179 if (++y > YEAR_MAX)
180 return date_nil;
181 }
182 }
183 return mkdate(y, m, d);
184}
185
186/* count days (including leap days) since some time before YEAR_MIN */
187#define CNT_OFF (((YEAR_OFFSET+399)/400)*400)
188static inline int
189date_countdays(date dt)
190{
191 static_assert(CNT_OFF % 400 == 0, /* for leapyear function to work */
192 "CNT_OFF must be multiple of 400");
193 assert(!is_date_nil(dt));
194 int y = date_extract_year(dt);
195 int m = date_extract_month(dt);
196 int y1 = y + CNT_OFF - 1;
197 return date_extract_day(dt)
198 + (y+CNT_OFF)*365 + y1/4 - y1/100 + y1/400
199 + cumdays[m-1] + (m > 2 && isleapyear(y));
200}
201
202/* return the difference in days between the two dates */
203int
204date_diff(date d1, date d2)
205{
206 if (is_date_nil(d1) || is_date_nil(d2))
207 return int_nil;
208 return date_countdays(d1) - date_countdays(d2);
209}
210
211/* 21 April 2019 is a Sunday, we can calculate the offset for the
212 * day-of-week calculation below from this fact */
213#define DOW_OFF (7 - (((21 + (2019+CNT_OFF)*365 + (2019+CNT_OFF-1)/4 - (2019+CNT_OFF-1)/100 + (2019+CNT_OFF-1)/400 + 90) % 7) + 1))
214/* return day of week of given date; Monday = 1, Sunday = 7 */
215int
216date_dayofweek(date dt)
217{
218 if (is_date_nil(dt))
219 return int_nil;
220 /* calculate number of days since the start of the year -CNT_OFF */
221 int d = date_countdays(dt);
222 /* then simply take the remainder from 7 and convert to correct
223 * weekday */
224 return (d + DOW_OFF) % 7 + 1;
225}
226
227/* week 1 is the week (Monday to Sunday) in which January 4 falls; if
228 * January 1 to 3 fall in the week before the 4th, they are in the
229 * last week of the previous year; the last days of the year may fall
230 * in week 1 of the following year */
231int
232date_weekofyear(date dt)
233{
234 if (is_date_nil(dt))
235 return int_nil;
236 int y = date_extract_year(dt);
237 int m = date_extract_month(dt);
238 int d = date_extract_day(dt);
239 int cnt1 = date_countdays(mkdate(y, 1, 4));
240 int wd1 = (cnt1 + DOW_OFF) % 7 + 1; /* date_dayofweek(mkdate(y, 1, 4)) */
241 int cnt2 = date_countdays(dt);
242 int wd2 = (cnt2 + DOW_OFF) % 7 + 1; /* date_dayofweek(dt) */
243 if (wd2 > wd1 && m == 1 && d < 4) {
244 /* last week of previous year */
245 cnt1 = date_countdays(mkdate(y - 1, 1, 4));
246 wd1 = (cnt1 + DOW_OFF) % 7 + 1; /* date_dayofweek(mkdate(y-1, 1, 4)) */
247 } else if (m == 12 && wd2 + 31 - d < 4)
248 return 1;
249 if (wd2 < wd1)
250 cnt2 += 6;
251 return (cnt2 - cnt1) / 7 + 1;
252}
253
254int
255date_dayofyear(date dt)
256{
257 if (is_date_nil(dt))
258 return int_nil;
259 int m = date_extract_month(dt);
260 return date_extract_day(dt) + cumdays[m-1]
261 + (m > 2 && isleapyear(date_extract_year(dt)));
262}
263
264daytime
265daytime_create(int hour, int min, int sec, int usec)
266{
267 return istime(hour, min, sec, usec) ? mkdaytime(hour, min, sec, usec) : daytime_nil;
268}
269
270int
271daytime_hour(daytime tm)
272{
273 if (is_daytime_nil(tm))
274 return int_nil;
275 return daytime_extract_hour(tm);
276}
277
278int
279daytime_min(daytime tm)
280{
281 if (is_daytime_nil(tm))
282 return int_nil;
283 return daytime_extract_minute(tm);
284}
285
286int
287daytime_sec(daytime tm)
288{
289 if (is_daytime_nil(tm))
290 return int_nil;
291 return (int) ((tm / 1000000) % 60);
292}
293
294int
295daytime_usec(daytime tm)
296{
297 if (is_daytime_nil(tm))
298 return int_nil;
299 return (int) (tm % 1000000);
300}
301
302int
303daytime_sec_usec(daytime tm)
304{
305 if (is_daytime_nil(tm))
306 return int_nil;
307 return daytime_extract_usecond(tm);
308}
309
310daytime
311daytime_add_usec(daytime t, lng usec)
312{
313 if (is_daytime_nil(t) || is_lng_nil(usec))
314 return daytime_nil;
315 if (llabs(usec) >= DAY_USEC)
316 return daytime_nil; /* overflow for sure */
317 t += usec;
318 if (t < 0 || t >= DAY_USEC)
319 return daytime_nil;
320 return t;
321}
322
323daytime
324daytime_add_usec_modulo(daytime t, lng usec)
325{
326 if (is_daytime_nil(t) || is_lng_nil(usec))
327 return daytime_nil;
328 /* if usec < 0, usec%DAY_USEC < 0 */
329 t += usec % DAY_USEC;
330 if (t < 0)
331 t += DAY_USEC;
332 else if (t >= DAY_USEC)
333 t -= DAY_USEC;
334 return t;
335}
336
337#if !defined(HAVE_GMTIME_R) || !defined(HAVE_LOCALTIME_R)
338static MT_Lock timelock = MT_LOCK_INITIALIZER("timelock");
339#endif
340
341/* convert a value returned by the system time() function to a timestamp */
342timestamp
343timestamp_fromtime(time_t timeval)
344{
345 struct tm tm, *tmp;
346 date d;
347 daytime t;
348
349#ifdef HAVE_GMTIME_R
350 if ((tmp = gmtime_r(&timeval, &tm)) == NULL)
351 return timestamp_nil;
352#else
353 MT_lock_set(&timelock);
354 if ((tmp = gmtime(&timeval)) == NULL) {
355 MT_lock_unset(&timelock);
356 return timestamp_nil;
357 }
358 tm = *tmp; /* copy as quickly as possible */
359 tmp = &tm;
360 MT_lock_unset(&timelock);
361#endif
362 if (tmp->tm_sec >= 60)
363 tmp->tm_sec = 59; /* ignore leap seconds */
364 d = date_create(tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday);
365 t = daytime_create(tmp->tm_hour, tmp->tm_min, tmp->tm_sec, 0);
366 if (is_date_nil(d) || is_daytime_nil(t))
367 return timestamp_nil;
368 return mktimestamp(d, t);
369}
370
371/* convert a value returned by GDKusec() to a timestamp */
372timestamp
373timestamp_fromusec(lng usec)
374{
375 if (is_lng_nil(usec))
376 return timestamp_nil;
377 return timestamp_add_usec(unixepoch, usec);
378}
379
380timestamp
381timestamp_fromdate(date dt)
382{
383 if (is_date_nil(dt))
384 return timestamp_nil;
385 return mktimestamp(dt, mkdaytime(0, 0, 0, 0));
386}
387
388timestamp
389timestamp_create(date dt, daytime tm)
390{
391 if (is_date_nil(dt) || is_daytime_nil(tm))
392 return timestamp_nil;
393 return mktimestamp(dt, tm);
394}
395
396timestamp
397timestamp_current(void)
398{
399#if defined(_MSC_VER)
400 FILETIME ft;
401 ULARGE_INTEGER l;
402 GetSystemTimeAsFileTime(&ft);
403 l.LowPart = ft.dwLowDateTime;
404 l.HighPart = ft.dwHighDateTime;
405 return timestamp_add_usec(mktimestamp(mkdate(1601, 1, 1),
406 mkdaytime(0, 0, 0, 0)),
407 (lng) (l.QuadPart / 10));
408#elif defined(HAVE_CLOCK_GETTIME)
409 struct timespec ts;
410 clock_gettime(CLOCK_REALTIME, &ts);
411 return timestamp_add_usec(unixepoch,
412 ts.tv_sec * LL_CONSTANT(1000000)
413 + ts.tv_nsec / 1000);
414#elif defined(HAVE_GETTIMEOFDAY)
415 struct timeval tv;
416 gettimeofday(&tv, NULL);
417 return timestamp_add_usec(unixepoch,
418 tv.tv_sec * LL_CONSTANT(1000000) + tv.tv_usec);
419#else
420 return timestamp_add_usec(unixepoch,
421 (lng) time(NULL) * LL_CONSTANT(1000000));
422#endif
423}
424
425timestamp
426timestamp_add_usec(timestamp t, lng usec)
427{
428 if (is_timestamp_nil(t) || is_lng_nil(usec))
429 return timestamp_nil;
430
431 daytime tm = ts_time(t);
432 date dt = ts_date(t);
433
434 tm += usec;
435 if (tm < 0) {
436 int add = (int) ((DAY_USEC - 1 - tm) / DAY_USEC);
437 tm += add * DAY_USEC;
438 dt = date_add_day(dt, -add);
439 } else if (tm >= DAY_USEC) {
440 dt = date_add_day(dt, (int) (tm / DAY_USEC));
441 tm %= DAY_USEC;
442 }
443 if (is_date_nil(dt))
444 return timestamp_nil;
445 return mktimestamp(dt, tm);
446}
447
448timestamp
449timestamp_add_month(timestamp t, int m)
450{
451 if (is_timestamp_nil(t) || is_int_nil(m))
452 return timestamp_nil;
453
454 daytime tm = ts_time(t);
455 date dt = ts_date(t);
456
457 dt = date_add_month(dt, m);
458 if (is_date_nil(dt))
459 return timestamp_nil;
460 return mktimestamp(dt, tm);
461}
462
463date
464timestamp_date(timestamp t)
465{
466 if (is_timestamp_nil(t))
467 return date_nil;
468 return ts_date(t);
469}
470
471daytime
472timestamp_daytime(timestamp t)
473{
474 if (is_timestamp_nil(t))
475 return daytime_nil;
476 return ts_time(t);
477}
478
479lng
480timestamp_diff(timestamp t1, timestamp t2)
481{
482 if (is_timestamp_nil(t1) || is_timestamp_nil(t2))
483 return lng_nil;
484 return ts_time(t1) - ts_time(t2) + DAY_USEC * date_diff(ts_date(t1), ts_date(t2));
485}
486
487/* GDK level atom functions with some helpers */
488static ssize_t
489fleximatch(const char *s, const char *pat, size_t min)
490{
491 size_t hit;
492 bool spacy = false;
493
494 if (min == 0) {
495 min = (int) strlen(pat); /* default minimum required hits */
496 }
497 for (hit = 0; *pat; hit++) {
498 if (tolower((unsigned char) s[hit]) != (unsigned char) *pat) {
499 if (GDKisspace(s[hit]) && spacy) {
500 min++;
501 continue; /* extra spaces */
502 }
503 break;
504 }
505 spacy = GDKisspace(*pat);
506 pat++;
507 }
508 return (hit >= min) ? hit : 0;
509}
510
511static ssize_t
512parse_substr(int *ret, const char *s, size_t min, const char *list[], int size)
513{
514 ssize_t j = 0;
515 int i = 0;
516
517 *ret = int_nil;
518 while (++i <= size) {
519 if ((j = fleximatch(s, list[i], min)) > 0) {
520 *ret = i;
521 break;
522 }
523 }
524 return j;
525}
526
527static const char *MONTHS[13] = {
528 NULL, "january", "february", "march", "april", "may", "june",
529 "july", "august", "september", "october", "november", "december"
530};
531
532static ssize_t
533parse_date(const char *buf, date *d, bool external)
534{
535 int day = 0, month = int_nil;
536 int year = 0;
537 bool yearneg, yearlast = false;
538 ssize_t pos = 0;
539 int sep;
540
541 *d = date_nil;
542 if (strcmp(buf, str_nil) == 0)
543 return 1;
544 if (external && strncmp(buf, "nil", 3) == 0)
545 return 3;
546 if ((yearneg = (buf[0] == '-')))
547 buf++;
548 if (!yearneg && !GDKisdigit(buf[0])) {
549#ifdef HAVE_SYNONYMS
550 if (!synonyms) {
551 GDKerror("Syntax error in date.\n");
552 return -1;
553 }
554#endif
555 yearlast = true;
556 sep = ' ';
557 } else {
558 for (pos = 0; GDKisdigit(buf[pos]); pos++) {
559 year = (buf[pos] - '0') + year * 10;
560 if (year > YEAR_MAX)
561 break;
562 }
563 sep = (unsigned char) buf[pos++];
564#ifdef HAVE_SYNONYMS
565 if (!synonyms && sep != '-') {
566 GDKerror("Syntax error in date.\n");
567 return -1;
568 }
569#endif
570 sep = tolower(sep);
571 if (sep >= 'a' && sep <= 'z') {
572 sep = 0;
573 } else if (sep == ' ') {
574 while (buf[pos] == ' ')
575 pos++;
576 } else if (sep != '-' && sep != '/' && sep != '\\') {
577 GDKerror("Syntax error in date.\n");
578 return -1;
579 }
580 }
581 if (GDKisdigit(buf[pos])) {
582 month = buf[pos++] - '0';
583 if (GDKisdigit(buf[pos])) {
584 month = (buf[pos++] - '0') + month * 10;
585 }
586#ifdef HAVE_SYNONYMS
587 } else if (!synonyms) {
588 GDKerror("Syntax error in date.\n");
589 return -1;
590#endif
591 } else {
592 pos += parse_substr(&month, buf + pos, 3, MONTHS, 12);
593 }
594 if (is_int_nil(month) || (sep && buf[pos++] != sep)) {
595 GDKerror("Syntax error in date.\n");
596 return -1;
597 }
598 if (sep == ' ') {
599 while (buf[pos] == ' ')
600 pos++;
601 }
602 if (!GDKisdigit(buf[pos])) {
603 GDKerror("Syntax error in date.\n");
604 return -1;
605 }
606 while (GDKisdigit(buf[pos])) {
607 day = (buf[pos++] - '0') + day * 10;
608 if (day > 31)
609 break;
610 }
611 if (yearlast && (buf[pos] == ',' || buf[pos] == ' ')) {
612 while (buf[++pos] == ' ')
613 ;
614 if (buf[pos] == '-') {
615 yearneg = true;
616 pos++;
617 }
618 while (GDKisdigit(buf[pos])) {
619 year = (buf[pos++] - '0') + year * 10;
620 if (year > YEAR_MAX)
621 break;
622 }
623 }
624 /* handle semantic error here (returns nil in that case) */
625 *d = date_create(yearneg ? -year : year, month, day);
626 if (is_date_nil(*d)) {
627 GDKerror("Semantic error in date.\n");
628 return -1;
629 }
630 return pos + yearneg;
631}
632
633ssize_t
634date_fromstr(const char *buf, size_t *len, date **d, bool external)
635{
636 if (*len < sizeof(date) || *d == NULL) {
637 GDKfree(*d);
638 *d = (date *) GDKmalloc(*len = sizeof(date));
639 if( *d == NULL)
640 return -1;
641 }
642 return parse_date(buf, *d, external);
643}
644
645ssize_t
646date_tostr(str *buf, size_t *len, const date *val, bool external)
647{
648 /* 15 bytes is more than enough */
649 if (*len < 15 || *buf == NULL) {
650 GDKfree(*buf);
651 *buf = GDKmalloc(15);
652 if( *buf == NULL)
653 return -1;
654 *len = 15;
655 }
656 if (is_date_nil(*val)) {
657 if (external) {
658 strcpy(*buf, "nil");
659 return 3;
660 }
661 strcpy(*buf, str_nil);
662 return 1;
663 }
664 return (ssize_t) snprintf(*buf, *len, "%d-%02d-%02d",
665 date_extract_year(*val), date_extract_month(*val),
666 date_extract_day(*val));
667}
668
669static ssize_t
670parse_daytime(const char *buf, daytime *dt, bool external)
671{
672 int hour, min, sec = 0, usec = 0;
673 ssize_t pos = 0;
674
675 *dt = daytime_nil;
676 if (strcmp(buf, str_nil) == 0)
677 return 1;
678 if (external && strncmp(buf, "nil", 3) == 0)
679 return 3;
680 if (!GDKisdigit(buf[pos])) {
681 GDKerror("Syntax error in time.\n");
682 return -1;
683 }
684 for (hour = 0; GDKisdigit(buf[pos]); pos++) {
685 if (hour <= 24)
686 hour = (buf[pos] - '0') + hour * 10;
687 }
688 if ((buf[pos++] != ':') || !GDKisdigit(buf[pos])) {
689 GDKerror("Syntax error in time.\n");
690 return -1;
691 }
692 for (min = 0; GDKisdigit(buf[pos]); pos++) {
693 if (min <= 60)
694 min = (buf[pos] - '0') + min * 10;
695 }
696 if ((buf[pos] == ':') && GDKisdigit(buf[pos + 1])) {
697 for (pos++, sec = 0; GDKisdigit(buf[pos]); pos++) {
698 if (sec <= 60)
699 sec = (buf[pos] - '0') + sec * 10;
700 }
701 if ((buf[pos] == '.' || (
702#ifdef HAVE_SYNONYMS
703 synonyms &&
704#endif
705 buf[pos] == ':')) &&
706 GDKisdigit(buf[pos + 1])) {
707 pos++;
708 for (int i = 0; i < 6; i++) {
709 usec *= 10;
710 if (GDKisdigit(buf[pos])) {
711 usec += buf[pos] - '0';
712 pos++;
713 }
714 }
715#ifndef TRUNCATE_NUMBERS
716 if (GDKisdigit(buf[pos]) && buf[pos] >= '5') {
717 /* round the value */
718 if (++usec == 1000000) {
719 usec = 0;
720 if (++sec == 60) {
721 sec = 0;
722 if (++min == 60) {
723 min = 0;
724 if (++hour == 24) {
725 /* forget about rounding if it doesn't fit */
726 hour = 23;
727 min = 59;
728 sec = 59;
729 usec = 999999;
730 }
731 }
732 }
733 }
734 }
735#endif
736 while (GDKisdigit(buf[pos]))
737 pos++;
738 }
739 }
740 /* handle semantic error here (returns nil in that case) */
741 *dt = daytime_create(hour, min, sec, usec);
742 if (is_daytime_nil(*dt)) {
743 GDKerror("Semantic error in time.\n");
744 return -1;
745 }
746 return pos;
747}
748
749ssize_t
750daytime_fromstr(const char *buf, size_t *len, daytime **ret, bool external)
751{
752 if (*len < sizeof(daytime) || *ret == NULL) {
753 GDKfree(*ret);
754 *ret = (daytime *) GDKmalloc(*len = sizeof(daytime));
755 if (*ret == NULL)
756 return -1;
757 }
758 return parse_daytime(buf, *ret, external);
759}
760
761ssize_t
762daytime_tz_fromstr(const char *buf, size_t *len, daytime **ret, bool external)
763{
764 const char *s = buf;
765 ssize_t pos = daytime_fromstr(s, len, ret, external);
766 daytime val;
767 int offset = 0;
768
769 if (pos < 0 || is_daytime_nil(**ret))
770 return pos;
771
772 s = buf + pos;
773 pos = 0;
774 while (GDKisspace(*s))
775 s++;
776 /* in case of gmt we need to add the time zone */
777 if (fleximatch(s, "gmt", 0) == 3) {
778 s += 3;
779 }
780 if ((s[0] == '-' || s[0] == '+') &&
781 GDKisdigit(s[1]) && GDKisdigit(s[2]) && GDKisdigit(s[pos = 4]) &&
782 ((s[3] == ':' && GDKisdigit(s[5])) || GDKisdigit(s[pos = 3]))) {
783 offset = (((s[1] - '0') * 10 + (s[2] - '0')) * 60 + (s[pos] - '0') * 10 + (s[pos + 1] - '0')) * 60;
784 pos += 2;
785 if (s[0] != '-')
786 offset = -offset;
787 s += pos;
788 }
789 val = **ret + (lng) offset * 1000000;
790 if (val < 0)
791 **ret = DAY_USEC + val;
792 else if (val >= DAY_USEC)
793 **ret = val - DAY_USEC;
794 else
795 **ret = val;
796 return (ssize_t) (s - buf);
797}
798
799ssize_t
800daytime_precision_tostr(str *buf, size_t *len, const daytime dt,
801 int precision, bool external)
802{
803 int hour, min, sec, usec;
804
805 if (precision < 0)
806 precision = 0;
807 if (*len < 10 + (size_t) precision || *buf == NULL) {
808 GDKfree(*buf);
809 *buf = (str) GDKmalloc(*len = 10 + (size_t) precision);
810 if( *buf == NULL)
811 return -1;
812 }
813 if (is_daytime_nil(dt)) {
814 if (external) {
815 strcpy(*buf, "nil");
816 return 3;
817 }
818 strcpy(*buf, str_nil);
819 return 1;
820 }
821 usec = (int) (dt % 1000000);
822 sec = (int) (dt / 1000000);
823 hour = sec / 3600;
824 min = (sec % 3600) / 60;
825 sec %= 60;
826
827 if (precision == 0)
828 return snprintf(*buf, *len, "%02d:%02d:%02d", hour, min, sec);
829 else if (precision < 6) {
830 for (int i = 6; i > precision; i--)
831 usec /= 10;
832 return snprintf(*buf, *len, "%02d:%02d:%02d.%0*d", hour, min, sec, precision, usec);
833 } else {
834 ssize_t l = snprintf(*buf, *len, "%02d:%02d:%02d.%06d", hour, min, sec, usec);
835 while (precision > 6) {
836 precision--;
837 (*buf)[l++] = '0';
838 }
839 (*buf)[l] = '\0';
840 return l;
841 }
842}
843
844ssize_t
845daytime_tostr(str *buf, size_t *len, const daytime *val, bool external)
846{
847 return daytime_precision_tostr(buf, len, *val, 6, external);
848}
849
850ssize_t
851timestamp_fromstr(const char *buf, size_t *len, timestamp **ret, bool external)
852{
853 const char *s = buf;
854 ssize_t pos;
855 date dt;
856 daytime tm;
857
858 if (*len < sizeof(timestamp) || *ret == NULL) {
859 GDKfree(*ret);
860 *ret = (timestamp *) GDKmalloc(*len = sizeof(timestamp));
861 if (*ret == NULL)
862 return -1;
863 }
864 pos = parse_date(buf, &dt, external);
865 if (pos < 0)
866 return pos;
867 if (is_date_nil(dt)) {
868 **ret = timestamp_nil;
869 return pos;
870 }
871 s += pos;
872 if (*s == '@' || *s == ' ' || *s == '-' || *s == 'T') {
873 while (*++s == ' ')
874 ;
875 pos = parse_daytime(s, &tm, external);
876 if (pos < 0)
877 return pos;
878 s += pos;
879 if (is_daytime_nil(tm)) {
880 **ret = timestamp_nil;
881 return (ssize_t) (s - buf);
882 }
883 } else if (*s) {
884 tm = daytime_nil;
885 } else {
886 tm = mkdaytime(0, 0, 0, 0);
887 }
888 if (is_date_nil(dt) || is_daytime_nil(tm)) {
889 **ret = timestamp_nil;
890 } else {
891 lng offset = 0;
892
893 **ret = mktimestamp(dt, tm);
894 while (GDKisspace(*s))
895 s++;
896 /* in case of gmt we need to add the time zone */
897 if (fleximatch(s, "gmt", 0) == 3) {
898 s += 3;
899 }
900 if ((s[0] == '-' || s[0] == '+') &&
901 GDKisdigit(s[1]) && GDKisdigit(s[2]) && GDKisdigit(s[pos = 4]) &&
902 ((s[3] == ':' && GDKisdigit(s[5])) || GDKisdigit(s[pos = 3]))) {
903 offset = (((s[1] - '0') * LL_CONSTANT(10) + (s[2] - '0')) * LL_CONSTANT(60) + (s[pos] - '0') * LL_CONSTANT(10) + (s[pos + 1] - '0')) * LL_CONSTANT(60000000);
904 pos += 2;
905 if (s[0] != '-')
906 offset = -offset;
907 s += pos;
908 }
909 **ret = timestamp_add_usec(**ret, offset);
910 }
911 return (ssize_t) (s - buf);
912}
913
914ssize_t
915timestamp_tz_fromstr(const char *buf, size_t *len, timestamp **ret, bool external)
916{
917 const char *s = buf;
918 ssize_t pos = timestamp_fromstr(s, len, ret, external);
919 lng offset = 0;
920
921 if (pos < 0 || is_timestamp_nil(**ret))
922 return pos;
923
924 s = buf + pos;
925 pos = 0;
926 while (GDKisspace(*s))
927 s++;
928 /* incase of gmt we need to add the time zone */
929 if (fleximatch(s, "gmt", 0) == 3) {
930 s += 3;
931 }
932 if ((s[0] == '-' || s[0] == '+') &&
933 GDKisdigit(s[1]) && GDKisdigit(s[2]) && GDKisdigit(s[pos = 4]) &&
934 ((s[3] == ':' && GDKisdigit(s[5])) || GDKisdigit(s[pos = 3]))) {
935 offset = (((s[1] - '0') * LL_CONSTANT(10) + (s[2] - '0')) * LL_CONSTANT(60) + (s[pos] - '0') * LL_CONSTANT(10) + (s[pos + 1] - '0')) * LL_CONSTANT(60000000);
936 pos += 2;
937 if (s[0] != '-')
938 offset = -offset;
939 s += pos;
940 }
941 **ret = timestamp_add_usec(**ret, offset);
942 return (ssize_t) (s - buf);
943}
944
945ssize_t
946timestamp_precision_tostr(str *buf, size_t *len, timestamp val, int precision, bool external)
947{
948 ssize_t len1, len2;
949 size_t big = 128;
950 char buf1[128], buf2[128], *s = *buf, *s1 = buf1, *s2 = buf2;
951 date dt;
952 daytime tm;
953
954 if (is_timestamp_nil(val)) {
955 if (*len < 4 || *buf == NULL) {
956 GDKfree(*buf);
957 *buf = GDKmalloc(*len = 4);
958 if( *buf == NULL)
959 return -1;
960 }
961 if (external) {
962 strcpy(*buf, "nil");
963 return 3;
964 }
965 strcpy(*buf, str_nil);
966 return 1;
967 }
968
969 dt = ts_date(val);
970 tm = ts_time(val);
971 len1 = date_tostr(&s1, &big, &dt, false);
972 len2 = daytime_precision_tostr(&s2, &big, tm, precision, false);
973 if (len1 < 0 || len2 < 0)
974 return -1;
975
976 if (*len < 2 + (size_t) len1 + (size_t) len2 || *buf == NULL) {
977 GDKfree(*buf);
978 *buf = GDKmalloc(*len = (size_t) len1 + (size_t) len2 + 2);
979 if( *buf == NULL)
980 return -1;
981 }
982 s = *buf;
983 strcpy(s, buf1);
984 s += len1;
985 *s++ = ' ';
986 strcpy(s, buf2);
987 s += len2;
988 return (ssize_t) (s - *buf);
989}
990
991ssize_t
992timestamp_tostr(str *buf, size_t *len, const timestamp *val, bool external)
993{
994 return timestamp_precision_tostr(buf, len, *val, 6, external);
995}
996
997/* interfaces callable from MAL, not used from any C code */
998mal_export str MTIMEprelude(void *ret);
999mal_export str MTIMEcurrent_date(date *ret);
1000mal_export str MTIMEcurrent_time(daytime *ret);
1001mal_export str MTIMEcurrent_timestamp(timestamp *ret);
1002mal_export str MTIMEdate_sub_msec_interval(date *ret, const date *d, const lng *ms);
1003mal_export str MTIMEdate_add_msec_interval(date *ret, const date *d, const lng *ms);
1004mal_export str MTIMEtimestamp_sub_msec_interval(timestamp *ret, const timestamp *t, const lng *ms);
1005mal_export str MTIMEtimestamp_add_msec_interval(timestamp *ret, const timestamp *t, const lng *ms);
1006mal_export str MTIMEtimestamp_sub_month_interval(timestamp *ret, const timestamp *t, const int *m);
1007mal_export str MTIMEtimestamp_add_month_interval(timestamp *ret, const timestamp *t, const int *m);
1008mal_export str MTIMEtime_sub_msec_interval(daytime *ret, const daytime *t, const lng *ms);
1009mal_export str MTIMEtime_add_msec_interval(daytime *ret, const daytime *t, const lng *ms);
1010mal_export str MTIMEdaytime_diff_msec(lng *ret, const daytime *t1, const daytime *t2);
1011mal_export str MTIMEdate_submonths(date *ret, const date *d, const int *m);
1012mal_export str MTIMEdate_addmonths(date *ret, const date *d, const int *m);
1013mal_export str MTIMEdate_extract_dayofyear(int *ret, const date *d);
1014mal_export str MTIMEdate_extract_weekofyear(int *ret, const date *d);
1015mal_export str MTIMEdate_extract_dayofweek(int *ret, const date *d);
1016mal_export str MTIMEtimestamp_century(int *ret, const timestamp *t);
1017mal_export str MTIMEtimestamp_decade(int *ret, const timestamp *t);
1018mal_export str MTIMEtimestamp_year(int *ret, const timestamp *t);
1019mal_export str MTIMEtimestamp_quarter(int *ret, const timestamp *t);
1020mal_export str MTIMEtimestamp_month(int *ret, const timestamp *t);
1021mal_export str MTIMEtimestamp_day(int *ret, const timestamp *t);
1022mal_export str MTIMEtimestamp_hours(int *ret, const timestamp *t);
1023mal_export str MTIMEtimestamp_minutes(int *ret, const timestamp *t);
1024mal_export str MTIMEsql_year(int *ret, const int *months);
1025mal_export str MTIMEsql_month(int *ret, const int *months);
1026mal_export str MTIMEsql_day(lng *ret, const lng *msecs);
1027mal_export str MTIMEsql_hours(int *ret, const lng *msecs);
1028mal_export str MTIMEsql_minutes(int *ret, const lng *msecs);
1029mal_export str MTIMEsql_seconds(int *ret, const lng *msecs);
1030
1031mal_export str MTIMEdate_fromstr(date *ret, const char *const *s);
1032mal_export str MTIMEdate_date(date *dst, const date *src);
1033mal_export str MTIMEtimestamp_fromstr(timestamp *ret, const char *const *s);
1034mal_export str MTIMEtimestamp_timestamp(timestamp *dst, const timestamp *src);
1035mal_export str MTIMEseconds_since_epoch(int *ret, const timestamp *t);
1036mal_export str MTIMEdaytime_fromstr(daytime *ret, const char *const *s);
1037mal_export str MTIMEdaytime_daytime(daytime *dst, const daytime *src);
1038mal_export str MTIMEdaytime_fromseconds(daytime *ret, const lng *secs);
1039mal_export str MTIMEdaytime_fromseconds_bulk(bat *ret, bat *bid);
1040mal_export str MTIMElocal_timezone_msec(lng *ret);
1041mal_export str MTIMEstr_to_date(date *ret, const char *const *s, const char *const *format);
1042mal_export str MTIMEdate_to_str(str *ret, const date *d, const char *const *format);
1043mal_export str MTIMEstr_to_time(daytime *ret, const char *const *s, const char *const *format);
1044mal_export str MTIMEtime_to_str(str *ret, const daytime *d, const char *const *format);
1045mal_export str MTIMEstr_to_timestamp(timestamp *ret, const char *const *s, const char *const *format);
1046mal_export str MTIMEtimestamp_to_str(str *ret, const timestamp *d, const char *const *format);
1047
1048str
1049MTIMEprelude(void *ret)
1050{
1051 (void) ret;
1052
1053 TYPE_date = ATOMindex("date");
1054 TYPE_daytime = ATOMindex("daytime");
1055 TYPE_timestamp = ATOMindex("timestamp");
1056 return MAL_SUCCEED;
1057}
1058
1059str
1060MTIMEcurrent_date(date *ret)
1061{
1062 *ret = timestamp_date(timestamp_current());
1063 return MAL_SUCCEED;
1064}
1065
1066str
1067MTIMEcurrent_time(daytime *ret)
1068{
1069 *ret = timestamp_daytime(timestamp_current());
1070 return MAL_SUCCEED;
1071}
1072
1073str
1074MTIMEcurrent_timestamp(timestamp *ret)
1075{
1076 *ret = timestamp_current();
1077 return MAL_SUCCEED;
1078}
1079
1080#define COPYFLAGS do { bn->tsorted = b->tsorted; bn->trevsorted = b->trevsorted; } while (0)
1081#define SETFLAGS do { bn->tsorted = bn->trevsorted = n < 2; } while (0)
1082#define func1(NAME, NAMEBULK, MALFUNC, INTYPE, OUTYPE, FUNC, SETFLAGS) \
1083mal_export str NAME(OUTYPE *ret, const INTYPE *src); \
1084mal_export str NAMEBULK(bat *ret, const bat *bid); \
1085str \
1086NAME(OUTYPE *ret, const INTYPE *src) \
1087{ \
1088 if (is_##INTYPE##_nil(*src)) { \
1089 *ret = OUTYPE##_nil; \
1090 } else { \
1091 *ret = FUNC(*src); \
1092 } \
1093 return MAL_SUCCEED; \
1094} \
1095str \
1096NAMEBULK(bat *ret, const bat *bid) \
1097{ \
1098 BAT *b, *bn; \
1099 BUN n; \
1100 const INTYPE *src; \
1101 OUTYPE *dst; \
1102 \
1103 if ((b = BATdescriptor(*bid)) == NULL) \
1104 throw(MAL, "batmtime." MALFUNC, \
1105 SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); \
1106 n = BATcount(b); \
1107 if ((bn = COLnew(b->hseqbase, TYPE_##OUTYPE, n, TRANSIENT)) == NULL) { \
1108 BBPunfix(b->batCacheid); \
1109 throw(MAL, "batmtime." MALFUNC, SQLSTATE(HY001) MAL_MALLOC_FAIL); \
1110 } \
1111 src = Tloc(b, 0); \
1112 dst = Tloc(bn, 0); \
1113 bn->tnil = false; \
1114 for (BUN i = 0; i < n; i++) { \
1115 if (is_##INTYPE##_nil(src[i])) { \
1116 dst[i] = OUTYPE##_nil; \
1117 bn->tnil = true; \
1118 } else { \
1119 dst[i] = FUNC(src[i]); \
1120 } \
1121 } \
1122 bn->tnonil = !bn->tnil; \
1123 BATsetcount(bn, n); \
1124 SETFLAGS; \
1125 bn->tkey = false; \
1126 BBPunfix(b->batCacheid); \
1127 BBPkeepref(*ret = bn->batCacheid); \
1128 return MAL_SUCCEED; \
1129}
1130
1131#define func2(NAME, NAMEBULK, MALFUNC, INTYPE1, INTYPE2, OUTTYPE, FUNC) \
1132mal_export str NAME(OUTTYPE *ret, const INTYPE1 *v1, const INTYPE2 *v2); \
1133mal_export str NAMEBULK(bat *ret, const bat *bid1, const bat *bid2); \
1134str \
1135NAME(OUTTYPE *ret, const INTYPE1 *v1, const INTYPE2 *v2) \
1136{ \
1137 if (is_##INTYPE1##_nil(*v1) || is_##INTYPE2##_nil(*v2)) \
1138 *ret = OUTTYPE##_nil; \
1139 else \
1140 *ret = FUNC(*v1, *v2); \
1141 return MAL_SUCCEED; \
1142} \
1143str \
1144NAMEBULK(bat *ret, const bat *bid1, const bat *bid2) \
1145{ \
1146 BAT *b1, *b2, *bn; \
1147 BUN n; \
1148 const INTYPE1 *src1; \
1149 const INTYPE2 *src2; \
1150 OUTTYPE *dst; \
1151 \
1152 b1 = BATdescriptor(*bid1); \
1153 b2 = BATdescriptor(*bid2); \
1154 if (b1 == NULL || b2 == NULL) { \
1155 if (b1) \
1156 BBPunfix(b1->batCacheid); \
1157 if (b2) \
1158 BBPunfix(b2->batCacheid); \
1159 throw(MAL, "batmtime." MALFUNC, \
1160 SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); \
1161 } \
1162 n = BATcount(b1); \
1163 if (n != BATcount(b2)) { \
1164 BBPunfix(b1->batCacheid); \
1165 BBPunfix(b2->batCacheid); \
1166 throw(MAL, "batmtime." MALFUNC, "inputs not the same size"); \
1167 } \
1168 if ((bn = COLnew(b1->hseqbase, TYPE_##OUTTYPE, n, TRANSIENT)) == NULL) { \
1169 BBPunfix(b1->batCacheid); \
1170 BBPunfix(b2->batCacheid); \
1171 throw(MAL, "batmtime." MALFUNC, SQLSTATE(HY001) MAL_MALLOC_FAIL); \
1172 } \
1173 src1 = Tloc(b1, 0); \
1174 src2 = Tloc(b2, 0); \
1175 dst = Tloc(bn, 0); \
1176 bn->tnil = false; \
1177 for (BUN i = 0; i < n; i++) { \
1178 if (is_##INTYPE1##_nil(src1[i]) || is_##INTYPE2##_nil(src2[i])) { \
1179 dst[i] = OUTTYPE##_nil; \
1180 bn->tnil = true; \
1181 } else { \
1182 dst[i] = FUNC(src1[i], src2[i]); \
1183 } \
1184 } \
1185 bn->tnonil = !bn->tnil; \
1186 BATsetcount(bn, n); \
1187 bn->tsorted = n < 2; \
1188 bn->trevsorted = n < 2; \
1189 bn->tkey = false; \
1190 BBPunfix(b1->batCacheid); \
1191 BBPunfix(b2->batCacheid); \
1192 BBPkeepref(*ret = bn->batCacheid); \
1193 return MAL_SUCCEED; \
1194}
1195
1196#define DATEDIFF(d1, d2) (date_countdays(d1) - date_countdays(d2))
1197func2(MTIMEdate_diff, MTIMEdate_diff_bulk, "diff", date, date, int, DATEDIFF)
1198
1199#define func2chk(NAME, NAMEBULK, MALFUNC, INTYPE1, INTYPE2, OUTTYPE, FUNC) \
1200mal_export str NAME(OUTTYPE *ret, const INTYPE1 *v1, const INTYPE2 *v2); \
1201mal_export str NAMEBULK(bat *ret, const bat *bid1, const bat *bid2); \
1202str \
1203NAME(OUTTYPE *ret, const INTYPE1 *v1, const INTYPE2 *v2) \
1204{ \
1205 if (is_##INTYPE1##_nil(*v1) || is_##INTYPE2##_nil(*v2)) \
1206 *ret = OUTTYPE##_nil; \
1207 else { \
1208 *ret = FUNC(*v1, *v2); \
1209 if (is_##OUTTYPE##_nil(*ret)) \
1210 throw(MAL, "mtime." MALFUNC, \
1211 SQLSTATE(22003) "overflow in calculation"); \
1212 } \
1213 return MAL_SUCCEED; \
1214} \
1215str \
1216NAMEBULK(bat *ret, const bat *bid1, const bat *bid2) \
1217{ \
1218 BAT *b1, *b2, *bn; \
1219 BUN n; \
1220 const INTYPE1 *src1; \
1221 const INTYPE2 *src2; \
1222 OUTTYPE *dst; \
1223 \
1224 b1 = BATdescriptor(*bid1); \
1225 b2 = BATdescriptor(*bid2); \
1226 if (b1 == NULL || b2 == NULL) { \
1227 if (b1) \
1228 BBPunfix(b1->batCacheid); \
1229 if (b2) \
1230 BBPunfix(b2->batCacheid); \
1231 throw(MAL, "batmtime." MALFUNC, \
1232 SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); \
1233 } \
1234 n = BATcount(b1); \
1235 if (n != BATcount(b2)) { \
1236 BBPunfix(b1->batCacheid); \
1237 BBPunfix(b2->batCacheid); \
1238 throw(MAL, "batmtime." MALFUNC, "inputs not the same size"); \
1239 } \
1240 if ((bn = COLnew(b1->hseqbase, TYPE_##OUTTYPE, n, TRANSIENT)) == NULL) { \
1241 BBPunfix(b1->batCacheid); \
1242 BBPunfix(b2->batCacheid); \
1243 throw(MAL, "batmtime." MALFUNC, SQLSTATE(HY001) MAL_MALLOC_FAIL); \
1244 } \
1245 src1 = Tloc(b1, 0); \
1246 src2 = Tloc(b2, 0); \
1247 dst = Tloc(bn, 0); \
1248 bn->tnil = false; \
1249 for (BUN i = 0; i < n; i++) { \
1250 if (is_##INTYPE1##_nil(src1[i]) || is_##INTYPE2##_nil(src2[i])) { \
1251 dst[i] = OUTTYPE##_nil; \
1252 bn->tnil = true; \
1253 } else { \
1254 dst[i] = FUNC(src1[i], src2[i]); \
1255 if (is_##OUTTYPE##_nil(dst[i])) { \
1256 BBPunfix(b1->batCacheid); \
1257 BBPunfix(b2->batCacheid); \
1258 BBPreclaim(bn); \
1259 throw(MAL, "batmtime." MALFUNC, \
1260 SQLSTATE(22003) "overflow in calculation"); \
1261 } \
1262 } \
1263 } \
1264 bn->tnonil = !bn->tnil; \
1265 BATsetcount(bn, n); \
1266 bn->tsorted = n < 2; \
1267 bn->trevsorted = n < 2; \
1268 bn->tkey = false; \
1269 BBPunfix(b1->batCacheid); \
1270 BBPunfix(b2->batCacheid); \
1271 BBPkeepref(*ret = bn->batCacheid); \
1272 return MAL_SUCCEED; \
1273}
1274
1275str
1276MTIMEdate_sub_msec_interval(date *ret, const date *d, const lng *ms)
1277{
1278 if (is_date_nil(*d) || is_lng_nil(*ms))
1279 *ret = date_nil;
1280 else {
1281 *ret = date_add_day(*d, (int) (-*ms / (24*60*60*1000)));
1282 if (is_date_nil(*ret))
1283 throw(MAL, "mtime.date_sub_msec_interval",
1284 SQLSTATE(22003) "overflow in calculation");
1285 }
1286 return MAL_SUCCEED;
1287}
1288
1289str
1290MTIMEdate_add_msec_interval(date *ret, const date *d, const lng *ms)
1291{
1292 if (is_date_nil(*d) || is_lng_nil(*ms))
1293 *ret = date_nil;
1294 else {
1295 *ret = date_add_day(*d, (int) (*ms / (24*60*60*1000)));
1296 if (is_date_nil(*ret))
1297 throw(MAL, "mtime.date_add_msec_interval",
1298 SQLSTATE(22003) "overflow in calculation");
1299 }
1300 return MAL_SUCCEED;
1301}
1302
1303#define TSSUBMS(ts, ms) timestamp_add_usec((ts), -(ms) * 1000)
1304#define TSADDMS(ts, ms) timestamp_add_usec((ts), (ms) * 1000)
1305func2chk(MTIMEtimestamp_sub_msec_interval, MTIMEtimestamp_sub_msec_interval_bulk, "timestamp_sub_msec_interval", timestamp, lng, timestamp, TSSUBMS)
1306func2chk(MTIMEtimestamp_add_msec_interval, MTIMEtimestamp_add_msec_interval_bulk, "timestamp_add_msec_interval", timestamp, lng, timestamp, TSADDMS)
1307
1308str
1309MTIMEtimestamp_sub_month_interval(timestamp *ret, const timestamp *t, const int *m)
1310{
1311 if (is_timestamp_nil(*t) || is_int_nil(*m))
1312 *ret = timestamp_nil;
1313 else {
1314 *ret = timestamp_add_month(*t, -*m);
1315 if (is_timestamp_nil(*ret))
1316 throw(MAL, "mtime.timestamp_sub_month_interval",
1317 SQLSTATE(22003) "overflow in calculation");
1318 }
1319 return MAL_SUCCEED;
1320}
1321
1322str
1323MTIMEtimestamp_add_month_interval(timestamp *ret, const timestamp *t, const int *m)
1324{
1325 if (is_timestamp_nil(*t) || is_int_nil(*m))
1326 *ret = timestamp_nil;
1327 else {
1328 *ret = timestamp_add_month(*t, *m);
1329 if (is_timestamp_nil(*ret))
1330 throw(MAL, "mtime.timestamp_add_month_interval",
1331 SQLSTATE(22003) "overflow in calculation");
1332 }
1333 return MAL_SUCCEED;
1334}
1335
1336str
1337MTIMEtime_sub_msec_interval(daytime *ret, const daytime *t, const lng *ms)
1338{
1339 if (is_daytime_nil(*t) || is_lng_nil(*ms))
1340 *ret = daytime_nil;
1341 else {
1342 *ret = daytime_add_usec_modulo(*t, -*ms * 1000);
1343 }
1344 return MAL_SUCCEED;
1345}
1346
1347str
1348MTIMEtime_add_msec_interval(daytime *ret, const daytime *t, const lng *ms)
1349{
1350 if (is_daytime_nil(*t) || is_lng_nil(*ms))
1351 *ret = daytime_nil;
1352 else {
1353 *ret = daytime_add_usec_modulo(*t, *ms * 1000);
1354 }
1355 return MAL_SUCCEED;
1356}
1357
1358str
1359MTIMEdaytime_diff_msec(lng *ret, const daytime *t1, const daytime *t2)
1360{
1361 if (is_daytime_nil(*t1) || is_daytime_nil(*t2))
1362 *ret = lng_nil;
1363 else
1364 *ret = (*t1 - *t2) / 1000;
1365 return MAL_SUCCEED;
1366}
1367
1368str
1369MTIMEdate_submonths(date *ret, const date *d, const int *m)
1370{
1371 if (is_date_nil(*d) || is_int_nil(*m))
1372 *ret = date_nil;
1373 else {
1374 *ret = date_add_month(*d, -*m);
1375 if (is_date_nil(*ret))
1376 throw(MAL, "mtime.date_sub_month_interval",
1377 SQLSTATE(22003) "overflow in calculation");
1378 }
1379 return MAL_SUCCEED;
1380}
1381
1382str
1383MTIMEdate_addmonths(date *ret, const date *d, const int *m)
1384{
1385 if (is_date_nil(*d) || is_int_nil(*m))
1386 *ret = date_nil;
1387 else {
1388 *ret = date_add_month(*d, *m);
1389 if (is_date_nil(*ret))
1390 throw(MAL, "mtime.date_sub_month_interval",
1391 SQLSTATE(22003) "overflow in calculation");
1392 }
1393 return MAL_SUCCEED;
1394}
1395
1396func1(MTIMEdate_extract_century, MTIMEdate_extract_century_bulk, "century", date, int, date_extract_century, COPYFLAGS)
1397func1(MTIMEdate_extract_decade, MTIMEdate_extract_decade_bulk, "decade", date, int, date_extract_decade, COPYFLAGS)
1398func1(MTIMEdate_extract_year, MTIMEdate_extract_year_bulk, "year", date, int, date_extract_year, COPYFLAGS)
1399func1(MTIMEdate_extract_quarter, MTIMEdate_extract_quarter_bulk, "quarter", date, int, date_extract_quarter, SETFLAGS)
1400func1(MTIMEdate_extract_month, MTIMEdate_extract_month_bulk, "month", date, int, date_extract_month, SETFLAGS)
1401func1(MTIMEdate_extract_day, MTIMEdate_extract_day_bulk, "day", date, int, date_extract_day, SETFLAGS)
1402func1(MTIMEdaytime_extract_hours, MTIMEdaytime_extract_hours_bulk, "hours", daytime, int, daytime_extract_hour, COPYFLAGS)
1403func1(MTIMEdaytime_extract_minutes, MTIMEdaytime_extract_minutes_bulk, "minutes", daytime, int, daytime_extract_minute, SETFLAGS)
1404func1(MTIMEdaytime_extract_sql_seconds, MTIMEdaytime_extract_sql_seconds_bulk, "seconds", daytime, int, daytime_extract_usecond, SETFLAGS)
1405
1406str
1407MTIMEdate_extract_dayofyear(int *ret, const date *d)
1408{
1409 *ret = date_dayofyear(*d);
1410 return MAL_SUCCEED;
1411}
1412
1413str
1414MTIMEdate_extract_weekofyear(int *ret, const date *d)
1415{
1416 *ret = date_weekofyear(*d);
1417 return MAL_SUCCEED;
1418}
1419
1420str
1421MTIMEdate_extract_dayofweek(int *ret, const date *d)
1422{
1423 *ret = date_dayofweek(*d);
1424 return MAL_SUCCEED;
1425}
1426
1427static inline lng
1428TSDIFF(timestamp t1, timestamp t2)
1429{
1430 lng diff = ts_time(t1) - ts_time(t2) + DAY_USEC * date_diff(ts_date(t1), ts_date(t2));
1431#ifndef TRUNCATE_NUMBERS
1432 if (diff < 0)
1433 diff = -((-diff + 500) / 1000);
1434 else
1435 diff = (diff + 500) / 1000;
1436#else
1437 diff /= 1000;
1438#endif
1439 return diff;
1440}
1441func2(MTIMEtimestamp_diff_msec, MTIMEtimestamp_diff_msec_bulk, "diff", timestamp, timestamp, lng, TSDIFF)
1442
1443str
1444MTIMEtimestamp_century(int *ret, const timestamp *t)
1445{
1446 if (is_timestamp_nil(*t)) {
1447 *ret = int_nil;
1448 } else {
1449 int y = date_extract_year(ts_date(*t));
1450 if (y > 0)
1451 *ret = (y - 1) / 100 + 1;
1452 else
1453 *ret = -((-y - 1) / 100 + 1);
1454 }
1455 return MAL_SUCCEED;
1456}
1457
1458str
1459MTIMEtimestamp_decade(int *ret, const timestamp *t)
1460{
1461 if (is_timestamp_nil(*t)) {
1462 *ret = int_nil;
1463 } else {
1464 *ret = date_extract_year(ts_date(*t)) / 10;
1465 }
1466 return MAL_SUCCEED;
1467}
1468
1469str
1470MTIMEtimestamp_year(int *ret, const timestamp *t)
1471{
1472 *ret = date_year(timestamp_date(*t));
1473 return MAL_SUCCEED;
1474}
1475
1476str
1477MTIMEtimestamp_quarter(int *ret, const timestamp *t)
1478{
1479 *ret = is_timestamp_nil(*t) ? int_nil : (date_extract_month(ts_date(*t)) - 1) / 3 + 1;
1480 return MAL_SUCCEED;
1481}
1482
1483str
1484MTIMEtimestamp_month(int *ret, const timestamp *t)
1485{
1486 *ret = date_month(timestamp_date(*t));
1487 return MAL_SUCCEED;
1488}
1489
1490str
1491MTIMEtimestamp_day(int *ret, const timestamp *t)
1492{
1493 *ret = date_day(timestamp_date(*t));
1494 return MAL_SUCCEED;
1495}
1496
1497str
1498MTIMEtimestamp_hours(int *ret, const timestamp *t)
1499{
1500 *ret = daytime_hour(timestamp_daytime(*t));
1501 return MAL_SUCCEED;
1502}
1503
1504str
1505MTIMEtimestamp_minutes(int *ret, const timestamp *t)
1506{
1507 *ret = daytime_min(timestamp_daytime(*t));
1508 return MAL_SUCCEED;
1509}
1510
1511#define timestamp_extract_usecond(ts) daytime_extract_usecond(ts_time(ts))
1512func1(MTIMEtimestamp_sql_seconds, MTIMEtimestamp_sql_seconds_bulk, "sql_seconds", timestamp, int, timestamp_extract_usecond, SETFLAGS)
1513
1514str
1515MTIMEsql_year(int *ret, const int *months)
1516{
1517 *ret = is_int_nil(*months) ? int_nil : *months / 12;
1518 return MAL_SUCCEED;
1519}
1520
1521str
1522MTIMEsql_month(int *ret, const int *months)
1523{
1524 *ret = is_int_nil(*months) ? int_nil : *months % 12;
1525 return MAL_SUCCEED;
1526}
1527
1528str
1529MTIMEsql_day(lng *ret, const lng *msecs)
1530{
1531 *ret = is_lng_nil(*msecs) ? lng_nil : *msecs / (24*60*60*1000);
1532 return MAL_SUCCEED;
1533}
1534
1535str
1536MTIMEsql_hours(int *ret, const lng *msecs)
1537{
1538 *ret = is_lng_nil(*msecs) ? int_nil : (int) ((*msecs % (24*60*60*1000)) / (60*60*1000));
1539 return MAL_SUCCEED;
1540}
1541
1542str
1543MTIMEsql_minutes(int *ret, const lng *msecs)
1544{
1545 *ret = is_lng_nil(*msecs) ? int_nil : (int) ((*msecs % (60*60*1000)) / (60*1000));
1546 return MAL_SUCCEED;
1547}
1548
1549str
1550MTIMEsql_seconds(int *ret, const lng *msecs)
1551{
1552 *ret = is_lng_nil(*msecs) ? int_nil : (int) ((*msecs % (60*1000)) / 1000);
1553 return MAL_SUCCEED;
1554}
1555
1556str
1557MTIMEdate_fromstr(date *ret, const char *const *s)
1558{
1559 if (date_fromstr(*s, &(size_t){sizeof(date)}, &ret, true) < 0)
1560 throw(MAL, "cald.date", GDK_EXCEPTION);
1561 return MAL_SUCCEED;
1562}
1563
1564str
1565MTIMEdate_date(date *dst, const date *src)
1566{
1567 *dst = *src;
1568 return MAL_SUCCEED;
1569}
1570
1571func1(MTIMEtimestamp_extract_date, MTIMEtimestamp_extract_date_bulk, "date", timestamp, date, ts_date, COPYFLAGS)
1572
1573str
1574MTIMEtimestamp_fromstr(timestamp *ret, const char *const *s)
1575{
1576 if (timestamp_fromstr(*s, &(size_t){sizeof(timestamp)}, &ret, true) < 0)
1577 throw(MAL, "calc.timestamp", GDK_EXCEPTION);
1578 return MAL_SUCCEED;
1579}
1580
1581str
1582MTIMEtimestamp_timestamp(timestamp *dst, const timestamp *src)
1583{
1584 *dst = *src;
1585 return MAL_SUCCEED;
1586}
1587
1588#define mkts(dt) mktimestamp(dt, mkdaytime(0, 0, 0, 0))
1589func1(MTIMEtimestamp_fromdate, MTIMEtimestamp_fromdate_bulk, "timestamp", date, timestamp, mkts, COPYFLAGS)
1590
1591str
1592MTIMEseconds_since_epoch(int *ret, const timestamp *t)
1593{
1594 lng df = timestamp_diff(*t, unixepoch);
1595 *ret = is_lng_nil(df) ? int_nil : (int) (df / 1000000);
1596 return MAL_SUCCEED;
1597}
1598
1599#define mktsfromsec(sec) timestamp_add_usec(unixepoch, sec * LL_CONSTANT(1000000))
1600#define mktsfrommsec(msec) timestamp_add_usec(unixepoch, msec * 1000)
1601func1(MTIMEtimestamp_fromsecond, MTIMEtimestamp_fromsecond_bulk, "timestamp", int, timestamp, mktsfromsec, COPYFLAGS)
1602func1(MTIMEtimestamp_frommsec, MTIMEtimestamp_frommsec_bulk, "timestamp", lng, timestamp, mktsfrommsec, COPYFLAGS)
1603
1604str
1605MTIMEdaytime_fromstr(daytime *ret, const char *const *s)
1606{
1607 if (daytime_fromstr(*s, &(size_t){sizeof(daytime)}, &ret, true) < 0)
1608 throw(MAL, "calc.daytime", GDK_EXCEPTION);
1609 return MAL_SUCCEED;
1610}
1611
1612str
1613MTIMEdaytime_daytime(daytime *dst, const daytime *src)
1614{
1615 *dst = *src;
1616 return MAL_SUCCEED;
1617}
1618
1619str
1620MTIMEdaytime_fromseconds(daytime *ret, const lng *secs)
1621{
1622 if (is_lng_nil(*secs))
1623 *ret = daytime_nil;
1624 else if (*secs < 0 || *secs >= 24*60*60)
1625 throw(MAL, "calc.daytime", SQLSTATE(42000) ILLEGAL_ARGUMENT);
1626 else
1627 *ret = (daytime) (*secs * 1000000);
1628 return MAL_SUCCEED;
1629}
1630
1631str
1632MTIMEdaytime_fromseconds_bulk(bat *ret, bat *bid)
1633{
1634 BAT *b, *bn;
1635 BUN n;
1636 const lng *s;
1637 daytime *d;
1638
1639 if ((b = BATdescriptor(*bid)) == NULL)
1640 throw(MAL, "batcalc.daytime", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
1641 n = BATcount(b);
1642 if ((bn = COLnew(b->hseqbase, TYPE_daytime, n, TRANSIENT)) == NULL) {
1643 BBPunfix(b->batCacheid);
1644 throw(MAL, "batcalc.daytime", SQLSTATE(HY001) MAL_MALLOC_FAIL);
1645 }
1646 s = Tloc(b, 0);
1647 d = Tloc(bn, 0);
1648 bn->tnil = false;
1649 for (BUN i = 0; i < n; i++) {
1650 if (is_lng_nil(s[i])) {
1651 bn->tnil = true;
1652 d[i] = daytime_nil;
1653 } else if (s[i] < 0 || s[i] >= 24*60*60) {
1654 BBPunfix(b->batCacheid);
1655 BBPreclaim(bn);
1656 throw(MAL, "batcalc.daytime", SQLSTATE(42000) ILLEGAL_ARGUMENT);
1657 } else {
1658 d[i] = (daytime) (s[i] * 1000000);
1659 }
1660 }
1661 bn->tnonil = !bn->tnil;
1662 BATsetcount(bn, n);
1663 bn->tsorted = b->tsorted;
1664 bn->trevsorted = b->trevsorted;
1665 bn->tkey = false;
1666 BBPunfix(b->batCacheid);
1667 BBPkeepref(*ret = bn->batCacheid);
1668 return MAL_SUCCEED;
1669}
1670
1671func1(MTIMEtimestamp_extract_daytime, MTIMEtimestamp_extract_daytime_bulk, "daytime", timestamp, daytime, ts_time, SETFLAGS)
1672
1673str
1674MTIMElocal_timezone_msec(lng *ret)
1675{
1676 int tzone;
1677
1678#if defined(_MSC_VER)
1679 DYNAMIC_TIME_ZONE_INFORMATION tzinf;
1680
1681 /* documentation says: UTC = localtime + Bias (in minutes),
1682 * but experimentation during DST period says, UTC = localtime
1683 * + Bias + DaylightBias, and presumably during non DST
1684 * period, UTC = localtime + Bias */
1685 switch (GetDynamicTimeZoneInformation(&tzinf)) {
1686 case TIME_ZONE_ID_STANDARD:
1687 case TIME_ZONE_ID_UNKNOWN:
1688 tzone = -(int) tzinf.Bias * 60;
1689 break;
1690 case TIME_ZONE_ID_DAYLIGHT:
1691 tzone = -(int) (tzinf.Bias + tzinf.DaylightBias) * 60;
1692 break;
1693 default:
1694 /* call failed, we don't know the time zone */
1695 tzone = 0;
1696 break;
1697 }
1698#elif defined(HAVE_STRUCT_TM_TM_ZONE)
1699 time_t t;
1700 struct tm *tmp;
1701
1702 t = time(NULL);
1703 tmp = localtime(&t);
1704 tzone = (int) tmp->tm_gmtoff;
1705#else
1706 time_t t;
1707 timestamp lt, gt;
1708 struct tm tm, *tmp;
1709
1710 t = time(NULL);
1711#ifdef HAVE_GMTIME_R
1712 tmp = gmtime_r(&t, &tm);
1713#else
1714 MT_lock_set(&timelock);
1715 tmp = gmtime(&t);
1716 tm = *tmp;
1717 tmp = &tm;
1718 MT_lock_unset(&timelock);
1719#endif
1720 gt = mktimestamp(mkdate(tmp->tm_year + 1900,
1721 tmp->tm_mon + 1,
1722 tmp->tm_mday),
1723 mkdaytime(tmp->tm_hour,
1724 tmp->tm_min,
1725 tmp->tm_sec == 60 ? 59 : tmp->tm_sec,
1726 0));
1727#ifdef HAVE_LOCALTIME_R
1728 tmp = localtime_r(&t, &tm);
1729#else
1730 MT_lock_set(&timelock);
1731 tmp = localtime(&t);
1732 tm = *tmp;
1733 tmp = &tm;
1734 MT_lock_unset(&timelock);
1735#endif
1736 lt = mktimestamp(mkdate(tmp->tm_year + 1900,
1737 tmp->tm_mon + 1,
1738 tmp->tm_mday),
1739 mkdaytime(tmp->tm_hour,
1740 tmp->tm_min,
1741 tmp->tm_sec == 60 ? 59 : tmp->tm_sec,
1742 0));
1743 tzone = (int) (timestamp_diff(lt, gt) / 1000000);
1744#endif
1745 *ret = tzone * 1000;
1746 return MAL_SUCCEED;
1747}
1748
1749str
1750MTIMEstr_to_date(date *ret, const char *const *s, const char *const *format)
1751{
1752 struct tm tm;
1753
1754 if (GDK_STRNIL(*s) || GDK_STRNIL(*format)) {
1755 *ret = date_nil;
1756 return MAL_SUCCEED;
1757 }
1758 if (strptime(*s, *format, &tm) == NULL)
1759 throw(MAL, "mtime.str_to_date", "format '%s', doesn't match date '%s'",
1760 *format, *s);
1761 *ret = mkdate(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
1762 if (is_date_nil(*ret))
1763 throw(MAL, "mtime.str_to_date", "bad date '%s'", *s);
1764 return MAL_SUCCEED;
1765}
1766
1767str
1768MTIMEdate_to_str(str *ret, const date *d, const char *const *format)
1769{
1770 char buf[512];
1771 struct tm tm;
1772
1773 if (is_date_nil(*d) || GDK_STRNIL(*format)) {
1774 *ret = GDKstrdup(str_nil);
1775 if (*ret == NULL)
1776 throw(MAL, "mtime.date_to_str", SQLSTATE(HY001) MAL_MALLOC_FAIL);
1777 return MAL_SUCCEED;
1778 }
1779 tm = (struct tm) {
1780 .tm_year = date_extract_year(*d) - 1900,
1781 .tm_mon = date_extract_month(*d) - 1,
1782 .tm_mday = date_extract_day(*d),
1783 .tm_isdst = -1,
1784 };
1785 if (mktime(&tm) == (time_t) -1)
1786 throw(MAL, "mtime.date_to_str", "cannot convert date");
1787 if (strftime(buf, sizeof(buf), *format, &tm) == 0)
1788 throw(MAL, "mtime.date_to_str", "cannot convert date");
1789 *ret = GDKstrdup(buf);
1790 if (*ret == NULL)
1791 throw(MAL, "mtime.date_to_str", SQLSTATE(HY001) MAL_MALLOC_FAIL);
1792 return MAL_SUCCEED;
1793}
1794
1795str
1796MTIMEstr_to_time(daytime *ret, const char *const *s, const char *const *format)
1797{
1798 struct tm tm;
1799
1800 if (GDK_STRNIL(*s) || GDK_STRNIL(*format)) {
1801 *ret = daytime_nil;
1802 return MAL_SUCCEED;
1803 }
1804 if (strptime(*s, *format, &tm) == NULL)
1805 throw(MAL, "mtime.str_to_time", "format '%s', doesn't match time '%s'",
1806 *format, *s);
1807 *ret = mkdaytime(tm.tm_hour, tm.tm_min, tm.tm_sec == 60 ? 59 : tm.tm_sec, 0);
1808 if (is_daytime_nil(*ret))
1809 throw(MAL, "mtime.str_to_time", "bad time '%s'", *s);
1810 return MAL_SUCCEED;
1811}
1812
1813str
1814MTIMEtime_to_str(str *ret, const daytime *d, const char *const *format)
1815{
1816 char buf[512];
1817 daytime dt = *d;
1818 struct tm tm;
1819
1820 if (is_daytime_nil(dt) || GDK_STRNIL(*format)) {
1821 *ret = GDKstrdup(str_nil);
1822 if (*ret == NULL)
1823 throw(MAL, "mtime.time_to_str", SQLSTATE(HY001) MAL_MALLOC_FAIL);
1824 return MAL_SUCCEED;
1825 }
1826 time_t now = time(NULL);
1827 /* fill in current date in struct tm */
1828#ifdef HAVE_LOCALTIME_R
1829 localtime_r(&now, &tm);
1830#else
1831 MT_lock_set(&timelock);
1832 tm = *localtime(&now);
1833 MT_lock_unset(&timelock);
1834#endif
1835 /* replace time with requested time */
1836 dt /= 1000000;
1837 tm.tm_sec = dt % 60;
1838 dt /= 60;
1839 tm.tm_min = dt % 60;
1840 dt /= 60;
1841 tm.tm_hour = (int) dt;
1842 if (mktime(&tm) == (time_t) -1)
1843 throw(MAL, "mtime.time_to_str", "cannot convert time");
1844 if (strftime(buf, sizeof(buf), *format, &tm) == 0)
1845 throw(MAL, "mtime.time_to_str", "cannot convert time");
1846 *ret = GDKstrdup(buf);
1847 if (*ret == NULL)
1848 throw(MAL, "mtime.time_to_str", SQLSTATE(HY001) MAL_MALLOC_FAIL);
1849 return MAL_SUCCEED;
1850}
1851
1852str
1853MTIMEstr_to_timestamp(timestamp *ret, const char *const *s, const char *const *format)
1854{
1855 struct tm tm = (struct tm) {0};
1856
1857 if (GDK_STRNIL(*s) || GDK_STRNIL(*format)) {
1858 *ret = timestamp_nil;
1859 return MAL_SUCCEED;
1860 }
1861 if (strptime(*s, *format, &tm) == NULL)
1862 throw(MAL, "mtime.str_to_timestamp",
1863 "format '%s', doesn't match timestamp '%s'", *format, *s);
1864 *ret = mktimestamp(mkdate(tm.tm_year + 1900,
1865 tm.tm_mon + 1,
1866 tm.tm_mday),
1867 mkdaytime(tm.tm_hour,
1868 tm.tm_min,
1869 tm.tm_sec == 60 ? 59 : tm.tm_sec,
1870 0));
1871 if (is_timestamp_nil(*ret))
1872 throw(MAL, "mtime.str_to_timestamp", "bad timestamp '%s'", *s);
1873 return MAL_SUCCEED;
1874}
1875
1876str
1877MTIMEtimestamp_to_str(str *ret, const timestamp *d, const char *const *format)
1878{
1879 char buf[512];
1880 date dt;
1881 daytime t;
1882 struct tm tm;
1883
1884 if (is_timestamp_nil(*d) || GDK_STRNIL(*format)) {
1885 *ret = GDKstrdup(str_nil);
1886 if (*ret == NULL)
1887 throw(MAL, "mtime.timestamp_to_str", SQLSTATE(HY001) MAL_MALLOC_FAIL);
1888 return MAL_SUCCEED;
1889 }
1890 dt = ts_date(*d);
1891 t = ts_time(*d);
1892 tm = (struct tm) {
1893 .tm_year = date_extract_year(dt) - 1900,
1894 .tm_mon = date_extract_month(dt) - 1,
1895 .tm_mday = date_extract_day(dt),
1896 .tm_isdst = -1,
1897 };
1898 t /= 1000000;
1899 tm.tm_sec = t % 60;
1900 t /= 60;
1901 tm.tm_min = t % 60;
1902 t /= 60;
1903 tm.tm_hour = (int) t;
1904 if (mktime(&tm) == (time_t) -1)
1905 throw(MAL, "mtime.timestamp_to_str", "cannot convert timestamp");
1906 if (strftime(buf, sizeof(buf), *format, &tm) == 0)
1907 throw(MAL, "mtime.timestamp_to_str", "cannot convert timestamp");
1908 *ret = GDKstrdup(buf);
1909 if (*ret == NULL)
1910 throw(MAL, "mtime.timestamp_to_str", SQLSTATE(HY001) MAL_MALLOC_FAIL);
1911 return MAL_SUCCEED;
1912}
1913