Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : #include "monetdb_config.h"
14 : #include "gdk.h"
15 : #include "gdk_time.h"
16 :
17 : #define YEAR_MIN (-4712) /* 4713 BC */
18 :
19 : #define YEAR_OFFSET (-YEAR_MIN)
20 : #define DTDAY_WIDTH 5 /* 1..28/29/30/31, depending on month/year */
21 : #define DTDAY_SHIFT 0
22 : #define DTMONTH_WIDTH 21 /* enough for 174761 years (and 8 months) */
23 : #define DTMONTH_SHIFT (DTDAY_WIDTH+DTDAY_SHIFT)
24 :
25 : #define YEAR_MAX (YEAR_MIN+(1<<DTMONTH_WIDTH)/12-1)
26 :
27 : #define isdate(y, m, d) ((m) > 0 && (m) <= 12 && (d) > 0 && (y) >= YEAR_MIN && (y) <= YEAR_MAX && (d) <= monthdays(y, m))
28 : #define mkdate(y, m, d) ((date) (((uint32_t) (((y) + YEAR_OFFSET) * 12 + (m) - 1) << DTMONTH_SHIFT) \
29 : | ((uint32_t) (d) << DTDAY_SHIFT)))
30 : #define date_extract_day(dt) ((int) (((uint32_t) (dt) >> DTDAY_SHIFT) & ((1 << DTDAY_WIDTH) - 1)))
31 : #define date_extract_month(dt) ((int) ((((uint32_t) (dt) >> DTMONTH_SHIFT) & ((1 << DTMONTH_WIDTH) - 1)) % 12 + 1))
32 : #define date_extract_year(dt) ((int) ((((uint32_t) (dt) >> DTMONTH_SHIFT) & ((1 << DTMONTH_WIDTH) - 1)) / 12 - YEAR_OFFSET))
33 :
34 : #define istime(h,m,s,u) ((h) >= 0 && (h) < 24 && (m) >= 0 && (m) < 60 && (s) >= 0 && (s) <= 60 && (u) >= 0 && (u) < 1000000)
35 : #define mkdaytime(h,m,s,u) (((((daytime) (h) * 60 + (m)) * 60) + (s)) * LL_CONSTANT(1000000) + (u))
36 :
37 : #define daytime_extract_hour(tm) ((int) (tm / HOUR_USEC))
38 : #define daytime_extract_minute(tm) ((int) ((tm / 60000000) % 60))
39 : #define daytime_extract_usecond(tm) ((int) (tm % 60000000)) /* includes seconds */
40 :
41 : #define TSTIME_WIDTH 37 /* [0..24*60*60*1000000) */
42 : #define TSTIME_SHIFT 0
43 : #define TSDATE_WIDTH (DTDAY_WIDTH+DTMONTH_WIDTH)
44 : #define TSDATE_SHIFT (TSTIME_SHIFT+TSTIME_WIDTH)
45 : #define ts_time(ts) ((daytime) (((uint64_t) (ts) >> TSTIME_SHIFT) & ((LL_CONSTANT(1) << TSTIME_WIDTH) - 1)))
46 : #define ts_date(ts) ((date) (((uint64_t) (ts) >> TSDATE_SHIFT) & ((1 << TSDATE_WIDTH) - 1)))
47 : #define mktimestamp(d, t) ((timestamp) (((uint64_t) (d) << TSDATE_SHIFT) | \
48 : ((uint64_t) (t) << TSTIME_SHIFT)))
49 :
50 : static const int leapdays[13] = { /* days per month in leap year */
51 : 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
52 : };
53 : static const int cumdays[13] = { /* cumulative days in non leap year */
54 : 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
55 : };
56 : #define isleapyear(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
57 : #define monthdays(y, m) (leapdays[m] - ((m) == 2 && !isleapyear(y)))
58 :
59 : const timestamp unixepoch = mktimestamp(mkdate(1970, 1, 1), mkdaytime(0, 0, 0, 0));
60 :
61 : date
62 4497723 : date_create(int year, int month, int day)
63 : {
64 : /* note that isdate returns false if any argument is nil */
65 4755190 : return isdate(year, month, day) ? mkdate(year, month, day) : date_nil;
66 : }
67 :
68 : int
69 28 : date_century(date dt)
70 : {
71 28 : int yr;
72 28 : if (is_date_nil(dt))
73 : return int_nil;
74 27 : yr = date_extract_year(dt);
75 27 : if (yr > 0)
76 24 : return (yr - 1) / 100 + 1;
77 : else
78 3 : return -((-yr - 1) / 100 + 1);
79 : }
80 :
81 : int
82 8 : date_decade(date dt)
83 : {
84 8 : if (is_date_nil(dt))
85 : return int_nil;
86 8 : return date_extract_year(dt) / 10;
87 : }
88 :
89 : int
90 7080465 : date_year(date dt)
91 : {
92 7080465 : if (is_date_nil(dt))
93 : return int_nil;
94 6709428 : return date_extract_year(dt);
95 : }
96 :
97 : bte
98 43 : date_quarter(date dt)
99 : {
100 43 : if (is_date_nil(dt))
101 1 : return bte_nil;
102 42 : return (date_extract_month(dt) - 1) / 3 + 1;
103 : }
104 :
105 : bte
106 6775667 : date_month(date dt)
107 : {
108 6775667 : if (is_date_nil(dt))
109 356948 : return bte_nil;
110 6418719 : return date_extract_month(dt);
111 : }
112 :
113 : bte
114 7103020 : date_day(date dt)
115 : {
116 7103020 : if (is_date_nil(dt))
117 375411 : return bte_nil;
118 6727609 : return date_extract_day(dt);
119 : }
120 :
121 : date
122 2871 : date_add_day(date dt, int days)
123 : {
124 2871 : if (is_date_nil(dt) || is_int_nil(days))
125 : return date_nil;
126 :
127 2871 : if (abs(days) >= 1 << (DTDAY_WIDTH + DTMONTH_WIDTH))
128 : return date_nil; /* overflow for sure */
129 :
130 2871 : int y = date_extract_year(dt);
131 2871 : int m = date_extract_month(dt);
132 2871 : int d = date_extract_day(dt);
133 :
134 2871 : d += days;
135 2965 : while (d <= 0) {
136 94 : if (--m == 0) {
137 20 : m = 12;
138 20 : if (--y < YEAR_MIN)
139 : return date_nil;
140 : }
141 95 : d += monthdays(y, m);
142 : }
143 7265 : while (d > monthdays(y, m)) {
144 4028 : d -= monthdays(y, m);
145 4028 : if (++m > 12) {
146 386 : m = 1;
147 386 : if (++y > YEAR_MAX)
148 : return date_nil;
149 : }
150 : }
151 2871 : return mkdate(y, m, d);
152 : }
153 :
154 : date
155 304 : date_add_month(date dt, int months)
156 : {
157 304 : if (is_date_nil(dt) || is_int_nil(months))
158 : return date_nil;
159 :
160 304 : if (abs(months) >= 1 << DTMONTH_WIDTH)
161 : return date_nil; /* overflow for sure */
162 :
163 303 : int y = date_extract_year(dt);
164 303 : int m = date_extract_month(dt);
165 303 : int d = date_extract_day(dt);
166 303 : m += months;
167 303 : if (m <= 0) {
168 26 : y -= (12 - m) / 12;
169 26 : if (y < YEAR_MIN)
170 : return date_nil;
171 26 : m = 12 - (-m % 12);
172 277 : } else if (m > 12) {
173 25 : y += (m - 1) / 12;
174 25 : if (y > YEAR_MAX)
175 : return date_nil;
176 25 : m = (m - 1) % 12 + 1;
177 : }
178 327 : if (d > monthdays(y, m)) {
179 12 : d = monthdays(y, m);
180 : }
181 303 : return mkdate(y, m, d);
182 : }
183 :
184 : /* count days (including leap days) since some time before YEAR_MIN */
185 : #define CNT_OFF (((YEAR_OFFSET+399)/400)*400)
186 : static inline int
187 883 : date_countdays(date dt)
188 : {
189 883 : static_assert(CNT_OFF % 400 == 0, /* for leapyear function to work */
190 : "CNT_OFF must be multiple of 400");
191 883 : assert(!is_date_nil(dt));
192 883 : int y = date_extract_year(dt);
193 883 : int m = date_extract_month(dt);
194 883 : int y1 = y + CNT_OFF - 1;
195 883 : return date_extract_day(dt)
196 883 : + (y+CNT_OFF)*365 + y1/4 - y1/100 + y1/400
197 883 : + cumdays[m-1] + (m > 2 && isleapyear(y));
198 : }
199 :
200 : /* return the difference in days between the two dates */
201 : int
202 280 : date_diff(date d1, date d2)
203 : {
204 280 : if (is_date_nil(d1) || is_date_nil(d2))
205 : return int_nil;
206 275 : return date_countdays(d1) - date_countdays(d2);
207 : }
208 :
209 : /* 21 April 2019 is a Sunday, we can calculate the offset for the
210 : * day-of-week calculation below from this fact */
211 : #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))
212 : /* return day of week of given date; Monday = 1, Sunday = 7 */
213 : bte
214 89 : date_dayofweek(date dt)
215 : {
216 89 : if (is_date_nil(dt))
217 3 : return bte_nil;
218 : /* calculate number of days since the start of the year -CNT_OFF */
219 86 : int d = date_countdays(dt);
220 : /* then simply take the remainder from 7 and convert to correct
221 : * weekday */
222 86 : return (d + DOW_OFF) % 7 + 1;
223 : }
224 :
225 : /* week 1 is the week (Monday to Sunday) in which January 4 falls; if
226 : * January 1 to 3 fall in the week before the 4th, they are in the
227 : * last week of the previous year; the last days of the year may fall
228 : * in week 1 of the following year */
229 : bte
230 131 : date_weekofyear(date dt)
231 : {
232 131 : if (is_date_nil(dt))
233 9 : return bte_nil;
234 122 : int y = date_extract_year(dt);
235 122 : int m = date_extract_month(dt);
236 122 : int d = date_extract_day(dt);
237 122 : int cnt1 = date_countdays(mkdate(y, 1, 4));
238 122 : int wd1 = (cnt1 + DOW_OFF) % 7 + 1; /* date_dayofweek(mkdate(y, 1, 4)) */
239 122 : int cnt2 = date_countdays(dt);
240 122 : int wd2 = (cnt2 + DOW_OFF) % 7 + 1; /* date_dayofweek(dt) */
241 122 : if (wd2 > wd1 && m == 1 && d < 4) {
242 : /* last week of previous year */
243 2 : cnt1 = date_countdays(mkdate(y - 1, 1, 4));
244 2 : wd1 = (cnt1 + DOW_OFF) % 7 + 1; /* date_dayofweek(mkdate(y-1, 1, 4)) */
245 120 : } else if (m == 12 && wd2 + 31 - d < 4)
246 : return 1;
247 122 : if (wd2 < wd1)
248 74 : cnt2 += 6;
249 122 : return (cnt2 - cnt1) / 7 + 1;
250 : }
251 :
252 : /* In the US they have to do it differently, of course.
253 : * Week 1 is the week (Sunday to Saturday) in which January 1 falls */
254 : bte
255 1 : date_usweekofyear(date dt)
256 : {
257 1 : if (is_date_nil(dt))
258 0 : return bte_nil;
259 1 : int y = date_extract_year(dt);
260 1 : int m = date_extract_month(dt);
261 : /* day of year (date_dayofyear without nil check) */
262 2 : int doy = date_extract_day(dt) + cumdays[m-1]
263 1 : + (m > 2 && isleapyear(y));
264 1 : int jan1 = mkdate(y, 1, 1);
265 1 : int jan1days = date_countdays(jan1);
266 1 : int jan1dow = (jan1days + DOW_OFF + 1) % 7; /* Sunday=0, Saturday=6 */
267 1 : return (doy + jan1dow - 1) / 7 + 1;
268 : }
269 :
270 : sht
271 80 : date_dayofyear(date dt)
272 : {
273 80 : if (is_date_nil(dt))
274 3 : return sht_nil;
275 77 : int m = date_extract_month(dt);
276 77 : return date_extract_day(dt) + cumdays[m-1]
277 91 : + (m > 2 && isleapyear(date_extract_year(dt)));
278 : }
279 :
280 : daytime
281 3748195 : daytime_create(int hour, int min, int sec, int usec)
282 : {
283 3748195 : return istime(hour, min, sec, usec) ? mkdaytime(hour, min, sec, usec) : daytime_nil;
284 : }
285 :
286 : /* return the difference in milliseconds between the two daytimes */
287 : lng
288 34 : daytime_diff(daytime d1, daytime d2)
289 : {
290 34 : if (is_daytime_nil(d1) || is_daytime_nil(d2))
291 : return lng_nil;
292 28 : return (d1 - d2) / 1000;
293 : }
294 :
295 : bte
296 7130285 : daytime_hour(daytime tm)
297 : {
298 7130285 : if (is_daytime_nil(tm))
299 376938 : return bte_nil;
300 6753347 : return daytime_extract_hour(tm);
301 : }
302 :
303 : bte
304 6895261 : daytime_min(daytime tm)
305 : {
306 6895261 : if (is_daytime_nil(tm))
307 363612 : return bte_nil;
308 6531649 : return daytime_extract_minute(tm);
309 : }
310 :
311 : int
312 3601267 : daytime_sec(daytime tm)
313 : {
314 3601267 : if (is_daytime_nil(tm))
315 17 : return int_nil;
316 3601250 : return (int) ((tm / 1000000) % 60);
317 : }
318 :
319 : int
320 3601224 : daytime_usec(daytime tm)
321 : {
322 3601224 : if (is_daytime_nil(tm))
323 17 : return int_nil;
324 3601207 : return (int) (tm % 1000000);
325 : }
326 :
327 : int
328 3485721 : daytime_sec_usec(daytime tm)
329 : {
330 3485721 : if (is_daytime_nil(tm))
331 374619 : return int_nil;
332 3111102 : return daytime_extract_usecond(tm);
333 : }
334 :
335 : daytime
336 124 : daytime_add_usec(daytime t, lng usec)
337 : {
338 124 : if (is_daytime_nil(t) || is_lng_nil(usec))
339 : return daytime_nil;
340 124 : if (llabs(usec) >= DAY_USEC)
341 : return daytime_nil; /* overflow for sure */
342 124 : t += usec;
343 124 : if (t < 0 || t >= DAY_USEC)
344 4 : return daytime_nil;
345 : return t;
346 : }
347 :
348 : daytime
349 260 : daytime_add_usec_modulo(daytime t, lng usec)
350 : {
351 260 : if (is_daytime_nil(t) || is_lng_nil(usec))
352 : return daytime_nil;
353 : /* if usec < 0, usec%DAY_USEC < 0 */
354 249 : t += usec % DAY_USEC;
355 249 : if (t < 0)
356 3 : t += DAY_USEC;
357 246 : else if (t >= DAY_USEC)
358 40 : t -= DAY_USEC;
359 : return t;
360 : }
361 :
362 : /* convert a value returned by the system time() function to a timestamp */
363 : timestamp
364 2898 : timestamp_fromtime(time_t timeval)
365 : {
366 2898 : struct tm tm = (struct tm) {0};
367 2898 : date d;
368 2898 : daytime t;
369 :
370 2898 : if (timeval == (time_t) -1 || gmtime_r(&timeval, &tm) == NULL)
371 0 : return timestamp_nil;
372 2898 : if (tm.tm_sec >= 60)
373 0 : tm.tm_sec = 59; /* ignore leap seconds */
374 2898 : d = date_create(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
375 2898 : t = daytime_create(tm.tm_hour, tm.tm_min, tm.tm_sec, 0);
376 2898 : if (is_date_nil(d) || is_daytime_nil(t))
377 0 : return timestamp_nil;
378 2898 : return mktimestamp(d, t);
379 : }
380 :
381 : /* convert a value returned by GDKusec() to a timestamp */
382 : timestamp
383 0 : timestamp_fromusec(lng usec)
384 : {
385 0 : if (is_lng_nil(usec))
386 0 : return timestamp_nil;
387 0 : return timestamp_add_usec(mktimestamp(mkdate(1970, 1, 1),
388 : mkdaytime(0, 0, 0, 0)),
389 : usec);
390 : }
391 :
392 : timestamp
393 388 : timestamp_fromdate(date dt)
394 : {
395 388 : if (is_date_nil(dt))
396 17 : return timestamp_nil;
397 371 : return mktimestamp(dt, mkdaytime(0, 0, 0, 0));
398 : }
399 :
400 : timestamp
401 2001851 : timestamp_create(date dt, daytime tm)
402 : {
403 2001851 : if (is_date_nil(dt) || is_daytime_nil(tm))
404 199410 : return timestamp_nil;
405 1802441 : return mktimestamp(dt, tm);
406 : }
407 :
408 : /* return the current time in UTC */
409 : timestamp
410 474 : timestamp_current(void)
411 : {
412 : #if defined(_MSC_VER)
413 : FILETIME ft;
414 : ULARGE_INTEGER l;
415 : GetSystemTimeAsFileTime(&ft);
416 : l.LowPart = ft.dwLowDateTime;
417 : l.HighPart = ft.dwHighDateTime;
418 : return timestamp_add_usec(mktimestamp(mkdate(1601, 1, 1),
419 : mkdaytime(0, 0, 0, 0)),
420 : (lng) (l.QuadPart / 10));
421 : #elif defined(HAVE_CLOCK_GETTIME)
422 474 : struct timespec ts;
423 474 : clock_gettime(CLOCK_REALTIME, &ts);
424 474 : return timestamp_add_usec(timestamp_fromtime(ts.tv_sec),
425 474 : (lng) (ts.tv_nsec / 1000));
426 : #elif defined(HAVE_GETTIMEOFDAY)
427 : struct timeval tv;
428 : gettimeofday(&tv, NULL);
429 : return timestamp_add_usec(timestamp_fromtime(tv.tv_sec), (lng) tv.tv_usec);
430 : #else
431 : return timestamp_fromtime(time(NULL));
432 : #endif
433 : }
434 :
435 : timestamp
436 20775 : timestamp_add_usec(timestamp t, lng usec)
437 : {
438 20775 : if (is_timestamp_nil(t) || is_lng_nil(usec))
439 : return timestamp_nil;
440 :
441 20767 : daytime tm = ts_time(t);
442 20767 : date dt = ts_date(t);
443 :
444 20767 : tm += usec;
445 20767 : if (tm < 0) {
446 181 : int add = (int) ((DAY_USEC - 1 - tm) / DAY_USEC);
447 181 : tm += add * DAY_USEC;
448 181 : dt = date_add_day(dt, -add);
449 20586 : } else if (tm >= DAY_USEC) {
450 269 : dt = date_add_day(dt, (int) (tm / DAY_USEC));
451 269 : tm %= DAY_USEC;
452 : }
453 20767 : if (is_date_nil(dt))
454 : return timestamp_nil;
455 20767 : return mktimestamp(dt, tm);
456 : }
457 :
458 : timestamp
459 120 : timestamp_add_month(timestamp t, int m)
460 : {
461 120 : if (is_timestamp_nil(t) || is_int_nil(m))
462 : return timestamp_nil;
463 :
464 120 : daytime tm = ts_time(t);
465 120 : date dt = ts_date(t);
466 :
467 120 : dt = date_add_month(dt, m);
468 120 : if (is_date_nil(dt))
469 : return timestamp_nil;
470 120 : return mktimestamp(dt, tm);
471 : }
472 :
473 : date
474 6918331 : timestamp_date(timestamp t)
475 : {
476 6918331 : if (is_timestamp_nil(t))
477 559628 : return date_nil;
478 6358703 : return ts_date(t);
479 : }
480 :
481 : daytime
482 6978104 : timestamp_daytime(timestamp t)
483 : {
484 6978104 : if (is_timestamp_nil(t))
485 : return daytime_nil;
486 6417336 : return ts_time(t);
487 : }
488 :
489 : lng
490 204 : timestamp_diff(timestamp t1, timestamp t2)
491 : {
492 204 : if (is_timestamp_nil(t1) || is_timestamp_nil(t2))
493 : return lng_nil;
494 200 : return ts_time(t1) - ts_time(t2) + DAY_USEC * date_diff(ts_date(t1), ts_date(t2));
495 : }
496 :
497 : /* GDK level atom functions with some helpers */
498 : static ssize_t
499 23982 : fleximatch(const char *s, const char *pat, size_t min)
500 : {
501 23982 : size_t hit;
502 23982 : bool spacy = false;
503 :
504 23982 : if (min == 0) {
505 21984 : min = (int) strlen(pat); /* default minimum required hits */
506 : }
507 24777 : for (hit = 0; *pat; hit++) {
508 24758 : if (tolower((unsigned char) s[hit]) != (unsigned char) *pat) {
509 23963 : if (GDKisspace(s[hit]) && spacy) {
510 0 : min++;
511 0 : continue; /* extra spaces */
512 : }
513 : break;
514 : }
515 795 : spacy = GDKisspace(*pat);
516 795 : pat++;
517 : }
518 23982 : return (hit >= min) ? hit : 0;
519 : }
520 :
521 : static ssize_t
522 319 : parse_substr(int *ret, const char *s, size_t min, const char *list[], int size)
523 : {
524 319 : ssize_t j = 0;
525 319 : int i = 0;
526 :
527 319 : *ret = int_nil;
528 2106 : while (++i <= size) {
529 1998 : if ((j = fleximatch(s, list[i], min)) > 0) {
530 211 : *ret = i;
531 211 : break;
532 : }
533 : }
534 319 : return j;
535 : }
536 :
537 : static const char *MONTHS[13] = {
538 : NULL, "january", "february", "march", "april", "may", "june",
539 : "july", "august", "september", "october", "november", "december"
540 : };
541 :
542 : static ssize_t
543 581643 : parse_date(const char *buf, date *d, bool external)
544 : {
545 581643 : int day = 0, month = int_nil;
546 581643 : int year = 0;
547 581643 : bool yearneg, yearlast = false;
548 581643 : ssize_t pos = 0;
549 581643 : int sep;
550 :
551 581643 : *d = date_nil;
552 581643 : if (strNil(buf))
553 : return 1;
554 581640 : if (external && strncmp(buf, "nil", 3) == 0)
555 : return 3;
556 581640 : if ((yearneg = (buf[0] == '-')))
557 9 : pos++;
558 581640 : if (!yearneg && !GDKisdigit(buf[pos])) {
559 : yearlast = true;
560 : sep = ' ';
561 : } else {
562 2899194 : for (; GDKisdigit(buf[pos]); pos++) {
563 2317730 : year = (buf[pos] - '0') + year * 10;
564 2317730 : if (year > YEAR_MAX)
565 : break;
566 : }
567 581484 : sep = (unsigned char) buf[pos++];
568 581484 : sep = tolower(sep);
569 581484 : if (sep >= 'a' && sep <= 'z') {
570 : sep = 0;
571 581499 : } else if (sep == ' ') {
572 14 : while (buf[pos] == ' ')
573 0 : pos++;
574 581485 : } else if (sep != '-' && sep != '/' && sep != '\\') {
575 47 : GDKerror("Syntax error in date.\n");
576 47 : return -1;
577 : }
578 : }
579 581593 : if (GDKisdigit(buf[pos])) {
580 581274 : month = buf[pos++] - '0';
581 581274 : if (GDKisdigit(buf[pos])) {
582 584784 : month = (buf[pos++] - '0') + month * 10;
583 : }
584 : } else {
585 319 : pos += parse_substr(&month, buf + pos, 3, MONTHS, 12);
586 : }
587 581593 : if (is_int_nil(month) || (sep && buf[pos++] != sep)) {
588 115 : GDKerror("Syntax error in date.\n");
589 115 : return -1;
590 : }
591 585252 : if (sep == ' ') {
592 201 : while (buf[pos] == ' ')
593 0 : pos++;
594 : }
595 581478 : if (!GDKisdigit(buf[pos])) {
596 4 : GDKerror("Syntax error in date.\n");
597 4 : return -1;
598 : }
599 1752099 : while (GDKisdigit(buf[pos])) {
600 1170650 : day = (buf[pos++] - '0') + day * 10;
601 1170650 : if (day > 31)
602 : break;
603 : }
604 581474 : if (yearlast && (buf[pos] == ',' || buf[pos] == ' ')) {
605 192 : while (buf[++pos] == ' ')
606 : ;
607 187 : if (buf[pos] == '-') {
608 0 : yearneg = true;
609 0 : pos++;
610 : }
611 566 : while (GDKisdigit(buf[pos])) {
612 379 : year = (buf[pos++] - '0') + year * 10;
613 379 : if (year > YEAR_MAX)
614 : break;
615 : }
616 : }
617 581474 : if (!yearneg && buf[pos] == ' ') {
618 : ssize_t opos = pos;
619 19165 : while (buf[++pos] == ' ')
620 : ;
621 19165 : if (strncasecmp(buf + pos, "BCE", 3) == 0) {
622 : /* Before Common Era */
623 0 : yearneg = true;
624 0 : pos += 3;
625 19165 : } else if (strncasecmp(buf + pos, "BC", 2) == 0) {
626 : /* Before Christ */
627 13 : yearneg = true;
628 13 : pos += 2;
629 19152 : } else if (strncasecmp(buf + pos, "CE", 2) == 0) {
630 : /* Common Era */
631 0 : pos += 2;
632 19152 : } else if (strncasecmp(buf + pos, "AD", 2) == 0) {
633 : /* Anno Domino */
634 1 : pos += 2;
635 : } else {
636 : pos = opos;
637 : }
638 : }
639 : /* handle semantic error here */
640 581474 : *d = date_create(yearneg ? -year : year, month, day);
641 581474 : if (is_date_nil(*d)) {
642 47 : GDKerror("Semantic error in date.\n");
643 47 : return -1;
644 : }
645 : return pos;
646 : }
647 :
648 : ssize_t
649 563958 : date_fromstr(const char *buf, size_t *len, date **d, bool external)
650 : {
651 563958 : if (*len < sizeof(date) || *d == NULL) {
652 2441 : GDKfree(*d);
653 2441 : *d = (date *) GDKmalloc(*len = sizeof(date));
654 2441 : if( *d == NULL)
655 : return -1;
656 : }
657 563958 : ssize_t n = 0;
658 563958 : while (buf[n] && GDKisspace(buf[n]))
659 0 : n++;
660 563958 : ssize_t l = parse_date(buf + n, *d, external);
661 564997 : if (l < 0)
662 : return l;
663 564957 : n += l;
664 564959 : while (buf[n] && GDKisspace(buf[n]))
665 2 : n++;
666 : return n;
667 : }
668 :
669 : static ssize_t
670 5720 : do_date_tostr(char *buf, size_t len, const date *val, bool external)
671 : {
672 5720 : assert(len >= 15);
673 5720 : if (is_date_nil(*val)) {
674 1 : if (external) {
675 1 : strcpy(buf, "nil");
676 1 : return 3;
677 : }
678 0 : strcpy(buf, str_nil);
679 0 : return 1;
680 : }
681 5719 : return (ssize_t) snprintf(buf, len, "%d-%02d-%02d",
682 5719 : date_extract_year(*val),
683 5719 : date_extract_month(*val),
684 : date_extract_day(*val));
685 : }
686 :
687 : ssize_t
688 5043 : date_tostr(str *buf, size_t *len, const date *val, bool external)
689 : {
690 : /* 15 bytes is more than enough */
691 5043 : if (*len < 15 || *buf == NULL) {
692 21 : GDKfree(*buf);
693 21 : *buf = GDKmalloc(15);
694 23 : if( *buf == NULL)
695 : return -1;
696 23 : *len = 15;
697 : }
698 5045 : return do_date_tostr(*buf, *len, val, external);
699 : }
700 :
701 : static ssize_t
702 21907 : parse_daytime(const char *buf, daytime *dt, bool external)
703 : {
704 21907 : unsigned int hour, min, sec = 0, usec = 0;
705 21907 : int n1, n2;
706 21907 : ssize_t pos = 0;
707 :
708 21907 : *dt = daytime_nil;
709 21907 : if (strNil(buf))
710 : return 1;
711 21907 : if (external && strncmp(buf, "nil", 3) == 0)
712 : return 3;
713 : /* accept plenty (6) of digits, but the range is still limited */
714 21907 : switch (sscanf(buf, "%6u:%6u%n:%6u%n", &hour, &min, &n1, &sec, &n2)) {
715 16 : default:
716 16 : GDKerror("Syntax error in time.\n");
717 16 : return -1;
718 251 : case 2:
719 : /* read hour and min, but not sec */
720 251 : if (hour >= 24 || min >= 60) {
721 0 : GDKerror("Syntax error in time.\n");
722 0 : return -1;
723 : }
724 251 : pos += n1;
725 251 : break;
726 21640 : case 3:
727 : /* read hour, min, and sec */
728 21640 : if (hour >= 24 || min >= 60 || sec > 60) {
729 0 : GDKerror("Syntax error in time.\n");
730 0 : return -1;
731 : }
732 21640 : pos += n2;
733 21640 : if (buf[pos] == '.' && GDKisdigit(buf[pos+1])) {
734 18454 : if (sscanf(buf + pos + 1, "%7u%n", &usec, &n1) < 1) {
735 : /* cannot happen: buf[pos+1] is a digit */
736 0 : GDKerror("Syntax error in time.\n");
737 0 : return -1;
738 : }
739 18454 : pos += n1 + 1;
740 19512 : while (n1 < 6) {
741 1058 : usec *= 10;
742 1058 : n1++;
743 : }
744 18454 : if (n1 == 7) {
745 : #ifdef TRUNCATE_NUMBERS
746 : usec /= 10;
747 : #else
748 0 : usec = (usec + 5) / 10;
749 0 : if (usec == 1000000) {
750 0 : usec = 0;
751 0 : if (++sec == 60) {
752 0 : sec = 0;
753 0 : if (++min == 60) {
754 0 : min = 0;
755 0 : if (++hour == 24) {
756 0 : hour = 23;
757 0 : min = 59;
758 0 : sec = 59;
759 0 : usec = 999999;
760 : }
761 : }
762 : }
763 : }
764 : #endif
765 : }
766 : /* ignore excess digits */
767 18454 : while (GDKisdigit(buf[pos]))
768 0 : pos++;
769 : }
770 : break;
771 : }
772 21891 : *dt = daytime_create(hour, min, sec, usec);
773 21891 : if (is_daytime_nil(*dt)) {
774 0 : GDKerror("Semantic error in time.\n");
775 0 : return -1;
776 : }
777 : return pos;
778 : }
779 :
780 : ssize_t
781 2750 : daytime_fromstr(const char *buf, size_t *len, daytime **ret, bool external)
782 : {
783 2750 : if (*len < sizeof(daytime) || *ret == NULL) {
784 5 : GDKfree(*ret);
785 5 : *ret = (daytime *) GDKmalloc(*len = sizeof(daytime));
786 5 : if (*ret == NULL)
787 : return -1;
788 : }
789 2750 : ssize_t n = 0;
790 2754 : while (buf[n] && GDKisspace(buf[n]))
791 4 : n++;
792 2750 : ssize_t l = parse_daytime(buf + n, *ret, external);
793 2750 : if (l < 0)
794 : return l;
795 2734 : n += l;
796 2770 : while (buf[n] && GDKisspace(buf[n]))
797 36 : n++;
798 : return n;
799 : }
800 :
801 : static ssize_t
802 2747 : daytime_tz_fromstr_internal(const char *buf, size_t *len, daytime **ret, long tz_sec, bool tzlocal, bool external)
803 : {
804 2747 : const char *s = buf;
805 2747 : ssize_t pos;
806 2747 : daytime val;
807 2747 : int offset = 0;
808 2747 : bool has_tz = false;
809 :
810 2747 : pos = daytime_fromstr(s, len, ret, external);
811 2747 : if (pos < 0 || is_daytime_nil(**ret))
812 : return pos;
813 :
814 2731 : s = buf + pos;
815 2731 : pos = 0;
816 2731 : while (GDKisspace(*s))
817 0 : s++;
818 : /* for GMT we need to add the time zone */
819 2731 : if (fleximatch(s, "gmt", 0) == 3) {
820 0 : s += 3;
821 : }
822 2731 : if ((s[0] == '-' || s[0] == '+') &&
823 49 : GDKisdigit(s[1]) && GDKisdigit(s[2]) && GDKisdigit(s[pos = 4]) &&
824 33 : ((s[3] == ':' && GDKisdigit(s[5])) || GDKisdigit(s[pos = 3]))) {
825 33 : offset = (((s[1] - '0') * 10 + (s[2] - '0')) * 60 + (s[pos] - '0') * 10 + (s[pos + 1] - '0')) * 60;
826 33 : pos += 2;
827 33 : if (s[0] == '+')
828 29 : offset = -offset; /* East of Greenwich */
829 33 : s += pos;
830 33 : has_tz = true;
831 : }
832 2731 : val = **ret;
833 2731 : (void)tz_sec;
834 2731 : if (!tzlocal && has_tz) /* convert into utc */
835 33 : val += offset * LL_CONSTANT(1000000);
836 2731 : if (!tzlocal && !has_tz) /* convert into utc */
837 360 : val -= tz_sec * LL_CONSTANT(1000000);
838 2731 : if (val < 0)
839 10 : val += DAY_USEC;
840 2721 : else if (val >= DAY_USEC)
841 0 : val -= DAY_USEC;
842 : /* and return */
843 2731 : **ret = val;
844 2731 : while (*s && GDKisspace(*s))
845 0 : s++;
846 2731 : return (ssize_t) (s - buf);
847 : }
848 :
849 : ssize_t
850 319 : daytime_tz_fromstr(const char *buf, size_t *len, daytime **ret, bool external)
851 : {
852 319 : return daytime_tz_fromstr_internal(buf, len, ret, 0, false, external);
853 : }
854 :
855 : ssize_t
856 2428 : sql_daytime_fromstr(const char *buf, daytime *ret, long tz_sec, bool tclocal)
857 : {
858 2428 : size_t len = sizeof(daytime);
859 2428 : return daytime_tz_fromstr_internal(buf, &len, &ret, tz_sec, tclocal, false);
860 : }
861 :
862 : static ssize_t
863 4820 : do_daytime_precision_tostr(char *buf, size_t len, const daytime dt,
864 : int precision, bool external)
865 : {
866 4820 : int hour, min, sec, usec;
867 :
868 4820 : if (precision < 0)
869 : precision = 0;
870 4820 : if (len < 10 + (size_t) precision) {
871 : return -1;
872 : }
873 4820 : if (is_daytime_nil(dt)) {
874 0 : if (external) {
875 0 : strcpy(buf, "nil");
876 0 : return 3;
877 : }
878 0 : strcpy(buf, str_nil);
879 0 : return 1;
880 : }
881 4820 : usec = (int) (dt % 1000000);
882 4820 : sec = (int) (dt / 1000000);
883 4820 : hour = sec / 3600;
884 4820 : min = (sec % 3600) / 60;
885 4820 : sec %= 60;
886 :
887 4820 : if (precision == 0)
888 2512 : return snprintf(buf, len, "%02d:%02d:%02d", hour, min, sec);
889 2308 : else if (precision < 6) {
890 2800 : for (int i = 6; i > precision; i--)
891 2095 : usec /= 10;
892 : #if defined(__GNUC__) && __GNUC__ < 9 && __GNUC__ > 4
893 : /* the %0*d format gives an incorrect warning in gcc on at least gcc 8.3.1
894 : * old gcc (at least 4.8.5) does not have the option */
895 : GCC_Pragma("GCC diagnostic ignored \"-Wformat-truncation\"")
896 : #endif
897 705 : return snprintf(buf, len, "%02d:%02d:%02d.%0*d", hour, min, sec, precision, usec);
898 : } else {
899 1603 : ssize_t l = snprintf(buf, len, "%02d:%02d:%02d.%06d", hour, min, sec, usec);
900 1603 : while (precision > 6) {
901 0 : precision--;
902 0 : buf[l++] = '0';
903 : }
904 1603 : buf[l] = '\0';
905 1603 : return l;
906 : }
907 : }
908 :
909 : ssize_t
910 4145 : daytime_precision_tostr(str *buf, size_t *len, const daytime dt,
911 : int precision, bool external)
912 : {
913 4145 : if (precision < 0)
914 : precision = 0;
915 4145 : if (*len < 10 + (size_t) precision || *buf == NULL) {
916 5 : GDKfree(*buf);
917 5 : *buf = (str) GDKmalloc(*len = 10 + (size_t) precision);
918 5 : if( *buf == NULL)
919 : return -1;
920 : }
921 4145 : return do_daytime_precision_tostr(*buf, *len, dt, precision, external);
922 : }
923 :
924 : ssize_t
925 11 : daytime_tostr(str *buf, size_t *len, const daytime *val, bool external)
926 : {
927 11 : return daytime_precision_tostr(buf, len, *val, 6, external);
928 : }
929 :
930 : static ssize_t
931 19596 : timestamp_fromstr_internal(const char *buf, size_t *len, timestamp **ret, bool external, bool parse_offset)
932 : {
933 19596 : const char *s = buf;
934 19596 : ssize_t pos;
935 19596 : date dt;
936 19596 : daytime tm;
937 :
938 19596 : if (*len < sizeof(timestamp) || *ret == NULL) {
939 0 : GDKfree(*ret);
940 0 : *ret = (timestamp *) GDKmalloc(*len = sizeof(timestamp));
941 0 : if (*ret == NULL)
942 : return -1;
943 : }
944 19596 : while (*s && GDKisspace(*s))
945 0 : s++;
946 19596 : pos = parse_date(s, &dt, external);
947 19596 : if (pos < 0)
948 : return pos;
949 19423 : if (is_date_nil(dt)) {
950 0 : **ret = timestamp_nil;
951 0 : return pos;
952 : }
953 19423 : s += pos;
954 19423 : if (*s == '@' || *s == ' ' || *s == '-' || *s == 'T') {
955 19157 : while (*++s == ' ')
956 : ;
957 19157 : pos = parse_daytime(s, &tm, external);
958 19157 : if (pos < 0)
959 : return pos;
960 19157 : s += pos;
961 19157 : if (is_daytime_nil(tm)) {
962 0 : **ret = timestamp_nil;
963 0 : return (ssize_t) (s - buf);
964 : }
965 266 : } else if (*s) {
966 170 : tm = daytime_nil;
967 : } else {
968 96 : tm = mkdaytime(0, 0, 0, 0);
969 : }
970 19423 : if (is_date_nil(dt) || is_daytime_nil(tm)) {
971 170 : **ret = timestamp_nil;
972 : } else {
973 19253 : lng offset = 0;
974 :
975 19253 : **ret = mktimestamp(dt, tm);
976 19253 : if (parse_offset) {
977 18551 : while (GDKisspace(*s))
978 10 : s++;
979 : /* in case of gmt we need to add the time zone */
980 18541 : if (fleximatch(s, "gmt", 0) == 3) {
981 3 : s += 3;
982 : }
983 18541 : if ((s[0] == '-' || s[0] == '+') &&
984 20 : GDKisdigit(s[1]) && GDKisdigit(s[2]) && GDKisdigit(s[pos = 4]) &&
985 17 : ((s[3] == ':' && GDKisdigit(s[5])) || GDKisdigit(s[pos = 3]))) {
986 17 : 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);
987 17 : pos += 2;
988 17 : if (s[0] != '-')
989 13 : offset = -offset;
990 17 : s += pos;
991 : }
992 18541 : **ret = timestamp_add_usec(**ret, offset);
993 : }
994 : }
995 19461 : while (*s && GDKisspace(*s))
996 38 : s++;
997 19423 : return (ssize_t) (s - buf);
998 : }
999 :
1000 : ssize_t
1001 18601 : timestamp_fromstr(const char *buf, size_t *len, timestamp **ret, bool external)
1002 : {
1003 18601 : return timestamp_fromstr_internal(buf, len, ret, external, true);
1004 : }
1005 :
1006 : static ssize_t
1007 995 : timestamp_tz_fromstr_internal(const char *buf, size_t *len, timestamp **ret, long tz_sec, bool tzlocal, bool external)
1008 : {
1009 995 : const char *s = buf;
1010 995 : ssize_t pos = timestamp_fromstr_internal(s, len, ret, external, false);
1011 995 : lng offset = 0;
1012 995 : bool has_tz = false;
1013 :
1014 995 : if (pos < 0 || is_timestamp_nil(**ret))
1015 : return pos;
1016 :
1017 712 : s = buf + pos;
1018 712 : pos = 0;
1019 712 : while (GDKisspace(*s))
1020 0 : s++;
1021 : /* in case of gmt we need to add the time zone */
1022 712 : if (fleximatch(s, "gmt", 0) == 3) {
1023 14 : s += 3;
1024 : }
1025 712 : if ((s[0] == '-' || s[0] == '+') &&
1026 53 : GDKisdigit(s[1]) && GDKisdigit(s[2]) && GDKisdigit(s[pos = 4]) &&
1027 41 : ((s[3] == ':' && GDKisdigit(s[5])) || GDKisdigit(s[pos = 3]))) {
1028 41 : 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);
1029 41 : pos += 2;
1030 41 : if (s[0] != '-')
1031 26 : offset = -offset;
1032 41 : s += pos;
1033 41 : has_tz = true;
1034 : }
1035 712 : if (!tzlocal && has_tz) /* convert into utc */
1036 33 : **ret = timestamp_add_usec(**ret, offset);
1037 712 : if (!tzlocal && !has_tz)
1038 86 : **ret = timestamp_add_usec(**ret, -tz_sec * LL_CONSTANT(1000000));
1039 716 : while (*s && GDKisspace(*s))
1040 4 : s++;
1041 712 : return (ssize_t) (s - buf);
1042 : }
1043 :
1044 : ssize_t
1045 0 : timestamp_tz_fromstr(const char *buf, size_t *len, timestamp **ret, bool external)
1046 : {
1047 0 : return timestamp_tz_fromstr_internal(buf, len, ret, 0, false, external);
1048 : }
1049 :
1050 : /* timestamp from str (return timestamp in local time */
1051 : ssize_t
1052 995 : sql_timestamp_fromstr(const char *buf, timestamp *ret, long tz_sec, bool tzlocal)
1053 : {
1054 995 : size_t len = sizeof(timestamp);
1055 995 : return timestamp_tz_fromstr_internal(buf, &len, &ret, tz_sec, tzlocal, false);
1056 : }
1057 :
1058 : ssize_t
1059 678 : timestamp_precision_tostr(str *buf, size_t *len, timestamp val, int precision, bool external)
1060 : {
1061 678 : ssize_t len1, len2;
1062 678 : char buf1[128], buf2[128];
1063 678 : date dt;
1064 678 : daytime tm;
1065 :
1066 678 : if (is_timestamp_nil(val)) {
1067 3 : if (*len < 4 || *buf == NULL) {
1068 3 : GDKfree(*buf);
1069 3 : *buf = GDKmalloc(*len = 4);
1070 3 : if( *buf == NULL)
1071 : return -1;
1072 : }
1073 3 : if (external) {
1074 3 : strcpy(*buf, "nil");
1075 3 : return 3;
1076 : }
1077 0 : strcpy(*buf, str_nil);
1078 0 : return 1;
1079 : }
1080 :
1081 675 : dt = ts_date(val);
1082 675 : tm = ts_time(val);
1083 675 : len1 = do_date_tostr(buf1, sizeof(buf1), &dt, false);
1084 675 : len2 = do_daytime_precision_tostr(buf2, sizeof(buf2), tm,
1085 : precision, false);
1086 675 : if (len1 < 0 || len2 < 0)
1087 : return -1;
1088 :
1089 675 : if (*len < 2 + (size_t) len1 + (size_t) len2 || *buf == NULL) {
1090 672 : GDKfree(*buf);
1091 672 : *buf = GDKmalloc(*len = (size_t) len1 + (size_t) len2 + 2);
1092 672 : if( *buf == NULL)
1093 : return -1;
1094 : }
1095 675 : return (ssize_t) strconcat_len(*buf, *len, buf1, " ", buf2, NULL);
1096 : }
1097 :
1098 : ssize_t
1099 678 : timestamp_tostr(str *buf, size_t *len, const timestamp *val, bool external)
1100 : {
1101 678 : return timestamp_precision_tostr(buf, len, *val, 6, external);
1102 : }
|