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 4232468 : date_create(int year, int month, int day)
63 : {
64 : /* note that isdate returns false if any argument is nil */
65 4486133 : 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 7560013 : date_year(date dt)
91 : {
92 7560013 : if (is_date_nil(dt))
93 : return int_nil;
94 7163604 : 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 7602537 : date_month(date dt)
107 : {
108 7602537 : if (is_date_nil(dt))
109 399012 : return bte_nil;
110 7203525 : return date_extract_month(dt);
111 : }
112 :
113 : bte
114 7517876 : date_day(date dt)
115 : {
116 7517876 : if (is_date_nil(dt))
117 395964 : return bte_nil;
118 7121912 : return date_extract_day(dt);
119 : }
120 :
121 : date
122 3115 : date_add_day(date dt, int days)
123 : {
124 3115 : if (is_date_nil(dt) || is_int_nil(days))
125 : return date_nil;
126 :
127 3115 : if (abs(days) >= 1 << (DTDAY_WIDTH + DTMONTH_WIDTH))
128 : return date_nil; /* overflow for sure */
129 :
130 3115 : int y = date_extract_year(dt);
131 3115 : int m = date_extract_month(dt);
132 3115 : int d = date_extract_day(dt);
133 :
134 3115 : d += days;
135 3284 : while (d <= 0) {
136 169 : if (--m == 0) {
137 88 : m = 12;
138 88 : if (--y < YEAR_MIN)
139 : return date_nil;
140 : }
141 171 : d += monthdays(y, m);
142 : }
143 38978 : while (d > monthdays(y, m)) {
144 33617 : d -= monthdays(y, m);
145 33617 : if (++m > 12) {
146 2826 : m = 1;
147 2826 : if (++y > YEAR_MAX)
148 : return date_nil;
149 : }
150 : }
151 3115 : return mkdate(y, m, d);
152 : }
153 :
154 : date
155 328 : date_add_month(date dt, int months)
156 : {
157 328 : if (is_date_nil(dt) || is_int_nil(months))
158 : return date_nil;
159 :
160 328 : if (abs(months) >= 1 << DTMONTH_WIDTH)
161 : return date_nil; /* overflow for sure */
162 :
163 327 : int y = date_extract_year(dt);
164 327 : int m = date_extract_month(dt);
165 327 : int d = date_extract_day(dt);
166 327 : m += months;
167 327 : 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 301 : } else if (m > 12) {
173 27 : y += (m - 1) / 12;
174 27 : if (y > YEAR_MAX)
175 : return date_nil;
176 27 : m = (m - 1) % 12 + 1;
177 : }
178 353 : if (d > monthdays(y, m)) {
179 12 : d = monthdays(y, m);
180 : }
181 327 : 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 975 : date_countdays(date dt)
188 : {
189 975 : static_assert(CNT_OFF % 400 == 0, /* for leap year function to work */
190 : "CNT_OFF must be multiple of 400");
191 975 : assert(!is_date_nil(dt));
192 975 : int y = date_extract_year(dt);
193 975 : int m = date_extract_month(dt);
194 975 : int y1 = y + CNT_OFF - 1;
195 975 : return date_extract_day(dt)
196 975 : + (y+CNT_OFF)*365 + y1/4 - y1/100 + y1/400
197 975 : + cumdays[m-1] + (m > 2 && isleapyear(y));
198 : }
199 :
200 : /* return the difference in days between the two dates */
201 : int
202 313 : date_diff(date d1, date d2)
203 : {
204 313 : if (is_date_nil(d1) || is_date_nil(d2))
205 : return int_nil;
206 308 : 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 115 : date_dayofweek(date dt)
215 : {
216 115 : if (is_date_nil(dt))
217 3 : return bte_nil;
218 : /* calculate number of days since the start of the year -CNT_OFF */
219 112 : int d = date_countdays(dt);
220 : /* then simply take the remainder from 7 and convert to correct
221 : * weekday */
222 113 : 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 106 : date_dayofyear(date dt)
272 : {
273 106 : if (is_date_nil(dt))
274 3 : return sht_nil;
275 103 : int m = date_extract_month(dt);
276 103 : return date_extract_day(dt) + cumdays[m-1]
277 130 : + (m > 2 && isleapyear(date_extract_year(dt)));
278 : }
279 :
280 : daytime
281 3579683 : daytime_create(int hour, int min, int sec, int usec)
282 : {
283 3579683 : 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 7601426 : daytime_hour(daytime tm)
297 : {
298 7601426 : if (is_daytime_nil(tm))
299 398832 : return bte_nil;
300 7202594 : return daytime_extract_hour(tm);
301 : }
302 :
303 : bte
304 7601417 : daytime_min(daytime tm)
305 : {
306 7601417 : if (is_daytime_nil(tm))
307 398832 : return bte_nil;
308 7202585 : return daytime_extract_minute(tm);
309 : }
310 :
311 : int
312 3601294 : daytime_sec(daytime tm)
313 : {
314 3601294 : if (is_daytime_nil(tm))
315 16 : return int_nil;
316 3601278 : return (int) ((tm / 1000000) % 60);
317 : }
318 :
319 : int
320 3601224 : daytime_usec(daytime tm)
321 : {
322 3601224 : if (is_daytime_nil(tm))
323 16 : return int_nil;
324 3601208 : return (int) (tm % 1000000);
325 : }
326 :
327 : int
328 4000132 : daytime_sec_usec(daytime tm)
329 : {
330 4000132 : if (is_daytime_nil(tm))
331 398816 : return int_nil;
332 3601316 : 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 266 : daytime_add_usec_modulo(daytime t, lng usec)
350 : {
351 266 : if (is_daytime_nil(t) || is_lng_nil(usec))
352 : return daytime_nil;
353 : /* if usec < 0, usec%DAY_USEC < 0 */
354 255 : t += usec % DAY_USEC;
355 255 : if (t < 0)
356 3 : t += DAY_USEC;
357 252 : else if (t >= DAY_USEC)
358 50 : t -= DAY_USEC;
359 : return t;
360 : }
361 :
362 : /* convert a value returned by the system time() function to a timestamp */
363 : timestamp
364 3421 : timestamp_fromtime(time_t timeval)
365 : {
366 3421 : struct tm tm = (struct tm) {0};
367 3421 : date d;
368 3421 : daytime t;
369 :
370 3421 : if (timeval == (time_t) -1 || gmtime_r(&timeval, &tm) == NULL)
371 0 : return timestamp_nil;
372 3421 : if (tm.tm_sec >= 60)
373 0 : tm.tm_sec = 59; /* ignore leap seconds */
374 3421 : d = date_create(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
375 3421 : t = daytime_create(tm.tm_hour, tm.tm_min, tm.tm_sec, 0);
376 3421 : if (is_date_nil(d) || is_daytime_nil(t))
377 0 : return timestamp_nil;
378 3421 : 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 2003993 : timestamp_create(date dt, daytime tm)
402 : {
403 2003993 : if (is_date_nil(dt) || is_daytime_nil(tm))
404 199410 : return timestamp_nil;
405 1804583 : return mktimestamp(dt, tm);
406 : }
407 :
408 : /* return the current time in UTC */
409 : timestamp
410 496 : 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 496 : struct timespec ts;
423 496 : clock_gettime(CLOCK_REALTIME, &ts);
424 496 : return timestamp_add_usec(timestamp_fromtime(ts.tv_sec),
425 496 : (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 24072 : timestamp_add_usec(timestamp t, lng usec)
437 : {
438 24072 : if (is_timestamp_nil(t) || is_lng_nil(usec))
439 : return timestamp_nil;
440 :
441 24064 : daytime tm = ts_time(t);
442 24064 : date dt = ts_date(t);
443 :
444 24064 : tm += usec;
445 24064 : if (tm < 0) {
446 316 : int add = (int) ((DAY_USEC - 1 - tm) / DAY_USEC);
447 316 : tm += add * DAY_USEC;
448 316 : dt = date_add_day(dt, -add);
449 23748 : } else if (tm >= DAY_USEC) {
450 338 : dt = date_add_day(dt, (int) (tm / DAY_USEC));
451 338 : tm %= DAY_USEC;
452 : }
453 24064 : if (is_date_nil(dt))
454 : return timestamp_nil;
455 24064 : return mktimestamp(dt, tm);
456 : }
457 :
458 : timestamp
459 123 : timestamp_add_month(timestamp t, int m)
460 : {
461 123 : if (is_timestamp_nil(t) || is_int_nil(m))
462 : return timestamp_nil;
463 :
464 123 : daytime tm = ts_time(t);
465 123 : date dt = ts_date(t);
466 :
467 123 : dt = date_add_month(dt, m);
468 123 : if (is_date_nil(dt))
469 : return timestamp_nil;
470 123 : return mktimestamp(dt, tm);
471 : }
472 :
473 : date
474 7674023 : timestamp_date(timestamp t)
475 : {
476 7674023 : if (is_timestamp_nil(t))
477 593010 : return date_nil;
478 7081013 : return ts_date(t);
479 : }
480 :
481 : daytime
482 7806360 : timestamp_daytime(timestamp t)
483 : {
484 7806360 : if (is_timestamp_nil(t))
485 : return daytime_nil;
486 7208124 : return ts_time(t);
487 : }
488 :
489 : lng
490 222 : timestamp_diff(timestamp t1, timestamp t2)
491 : {
492 222 : if (is_timestamp_nil(t1) || is_timestamp_nil(t2))
493 : return lng_nil;
494 218 : 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 27031 : fleximatch(const char *s, const char *pat, size_t min)
500 : {
501 27031 : size_t hit;
502 27031 : bool spacy = false;
503 :
504 27031 : if (min == 0) {
505 25033 : min = (int) strlen(pat); /* default minimum required hits */
506 : }
507 27827 : for (hit = 0; *pat; hit++) {
508 27808 : if (tolower((unsigned char) s[hit]) != (unsigned char) *pat) {
509 27012 : if (GDKisspace(s[hit]) && spacy) {
510 0 : min++;
511 0 : continue; /* extra spaces */
512 : }
513 : break;
514 : }
515 796 : spacy = GDKisspace(*pat);
516 796 : pat++;
517 : }
518 27031 : 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 590606 : parse_date(const char *buf, date *d, bool external)
544 : {
545 590606 : int day = 0, month = int_nil;
546 590606 : int year = 0;
547 590606 : bool yearneg, yearlast = false;
548 590606 : ssize_t pos = 0;
549 590606 : int sep;
550 :
551 590606 : *d = date_nil;
552 590606 : if (strNil(buf))
553 : return 1;
554 590603 : if (external && strncmp(buf, "nil", 3) == 0)
555 : return 3;
556 590603 : if ((yearneg = (buf[0] == '-')))
557 9 : pos++;
558 590603 : if (!yearneg && !GDKisdigit(buf[pos])) {
559 : yearlast = true;
560 : sep = ' ';
561 : } else {
562 2951463 : for (; GDKisdigit(buf[pos]); pos++) {
563 2361082 : year = (buf[pos] - '0') + year * 10;
564 2361082 : if (year > YEAR_MAX)
565 : break;
566 : }
567 590401 : sep = (unsigned char) buf[pos++];
568 590401 : sep = tolower(sep);
569 590401 : if (sep >= 'a' && sep <= 'z') {
570 : sep = 0;
571 590389 : } else if (sep == ' ') {
572 14 : while (buf[pos] == ' ')
573 0 : pos++;
574 590375 : } else if (sep != '-' && sep != '/' && sep != '\\') {
575 53 : GDKerror("Syntax error in date.\n");
576 53 : return -1;
577 : }
578 : }
579 590550 : if (GDKisdigit(buf[pos])) {
580 590231 : month = buf[pos++] - '0';
581 590231 : if (GDKisdigit(buf[pos])) {
582 590303 : month = (buf[pos++] - '0') + month * 10;
583 : }
584 : } else {
585 319 : pos += parse_substr(&month, buf + pos, 3, MONTHS, 12);
586 : }
587 590550 : if (is_int_nil(month) || (sep && buf[pos++] != sep)) {
588 115 : GDKerror("Syntax error in date.\n");
589 115 : return -1;
590 : }
591 590466 : if (sep == ' ') {
592 201 : while (buf[pos] == ' ')
593 0 : pos++;
594 : }
595 590435 : if (!GDKisdigit(buf[pos])) {
596 4 : GDKerror("Syntax error in date.\n");
597 4 : return -1;
598 : }
599 1771336 : while (GDKisdigit(buf[pos])) {
600 1180930 : day = (buf[pos++] - '0') + day * 10;
601 1180930 : if (day > 31)
602 : break;
603 : }
604 590431 : 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 590431 : if (!yearneg && buf[pos] == ' ') {
618 : ssize_t opos = pos;
619 22089 : while (buf[++pos] == ' ')
620 : ;
621 22089 : if (strncasecmp(buf + pos, "BCE", 3) == 0) {
622 : /* Before Common Era */
623 0 : yearneg = true;
624 0 : pos += 3;
625 22089 : } else if (strncasecmp(buf + pos, "BC", 2) == 0) {
626 : /* Before Christ */
627 13 : yearneg = true;
628 13 : pos += 2;
629 22076 : } else if (strncasecmp(buf + pos, "CE", 2) == 0) {
630 : /* Common Era */
631 0 : pos += 2;
632 22076 : } 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 590431 : *d = date_create(yearneg ? -year : year, month, day);
641 590431 : if (is_date_nil(*d)) {
642 48 : GDKerror("Semantic error in date.\n");
643 48 : return -1;
644 : }
645 : return pos;
646 : }
647 :
648 : ssize_t
649 569288 : date_fromstr(const char *buf, size_t *len, date **d, bool external)
650 : {
651 569288 : if (*len < sizeof(date) || *d == NULL) {
652 2475 : GDKfree(*d);
653 2475 : *d = (date *) GDKmalloc(*len = sizeof(date));
654 2475 : if( *d == NULL)
655 : return -1;
656 : }
657 569288 : ssize_t n = 0;
658 569288 : while (buf[n] && GDKisspace(buf[n]))
659 0 : n++;
660 569288 : ssize_t l = parse_date(buf + n, *d, external);
661 567385 : if (l < 0)
662 : return l;
663 567346 : n += l;
664 567348 : while (buf[n] && GDKisspace(buf[n]))
665 2 : n++;
666 : return n;
667 : }
668 :
669 : static ssize_t
670 5946 : do_date_tostr(char *buf, size_t len, const date *val, bool external)
671 : {
672 5946 : assert(len >= 15);
673 5946 : 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 5945 : return (ssize_t) snprintf(buf, len, "%d-%02d-%02d",
682 5945 : date_extract_year(*val),
683 5945 : date_extract_month(*val),
684 : date_extract_day(*val));
685 : }
686 :
687 : ssize_t
688 5267 : date_tostr(str *buf, size_t *len, const date *val, bool external)
689 : {
690 : /* 15 bytes is more than enough */
691 5267 : if (*len < 15 || *buf == NULL) {
692 23 : GDKfree(*buf);
693 23 : *buf = GDKmalloc(15);
694 23 : if( *buf == NULL)
695 : return -1;
696 23 : *len = 15;
697 : }
698 5267 : return do_date_tostr(*buf, *len, val, external);
699 : }
700 :
701 : static ssize_t
702 24872 : parse_daytime(const char *buf, daytime *dt, bool external)
703 : {
704 24872 : unsigned int hour, min, sec = 0, usec = 0;
705 24872 : int n1, n2;
706 24872 : ssize_t pos = 0;
707 :
708 24872 : *dt = daytime_nil;
709 24872 : if (strNil(buf))
710 : return 1;
711 24872 : if (external && strncmp(buf, "nil", 3) == 0)
712 : return 3;
713 : /* accept plenty (6) of digits, but the range is still limited */
714 24872 : switch (sscanf(buf, "%6u:%6u%n:%6u%n", &hour, &min, &n1, &sec, &n2)) {
715 22 : default:
716 22 : GDKerror("Syntax error in time.\n");
717 22 : return -1;
718 249 : case 2:
719 : /* read hour and min, but not sec */
720 249 : if (hour >= 24 || min >= 60) {
721 0 : GDKerror("Syntax error in time.\n");
722 0 : return -1;
723 : }
724 249 : pos += n1;
725 249 : break;
726 24601 : case 3:
727 : /* read hour, min, and sec */
728 24601 : if (hour >= 24 || min >= 60 || sec > 60) {
729 0 : GDKerror("Syntax error in time.\n");
730 0 : return -1;
731 : }
732 24601 : pos += n2;
733 24601 : if (buf[pos] == '.' && GDKisdigit(buf[pos+1])) {
734 19503 : 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 19503 : pos += n1 + 1;
740 20588 : while (n1 < 6) {
741 1085 : usec *= 10;
742 1085 : n1++;
743 : }
744 19503 : if (n1 == 7) {
745 : #ifdef TRUNCATE_NUMBERS
746 : usec /= 10;
747 : #else
748 1 : usec = (usec + 5) / 10;
749 1 : 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 19503 : while (GDKisdigit(buf[pos]))
768 0 : pos++;
769 : }
770 : break;
771 : }
772 24850 : *dt = daytime_create(hour, min, sec, usec);
773 24850 : 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 2765 : daytime_fromstr(const char *buf, size_t *len, daytime **ret, bool external)
782 : {
783 2765 : 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 2765 : ssize_t n = 0;
790 2769 : while (buf[n] && GDKisspace(buf[n]))
791 4 : n++;
792 2765 : ssize_t l = parse_daytime(buf + n, *ret, external);
793 2765 : if (l < 0)
794 : return l;
795 2743 : n += l;
796 2779 : while (buf[n] && GDKisspace(buf[n]))
797 36 : n++;
798 : return n;
799 : }
800 :
801 : static ssize_t
802 2762 : daytime_tz_fromstr_internal(const char *buf, size_t *len, daytime **ret, long tz_sec, bool tzlocal, bool external)
803 : {
804 2762 : const char *s = buf;
805 2762 : ssize_t pos;
806 2762 : daytime val;
807 2762 : int offset = 0;
808 2762 : bool has_tz = false;
809 :
810 2762 : pos = daytime_fromstr(s, len, ret, external);
811 2762 : if (pos < 0 || is_daytime_nil(**ret))
812 : return pos;
813 :
814 2740 : s = buf + pos;
815 2740 : pos = 0;
816 2740 : while (GDKisspace(*s))
817 0 : s++;
818 : /* for GMT we need to add the time zone */
819 2740 : if (fleximatch(s, "gmt", 0) == 3) {
820 0 : s += 3;
821 : }
822 2740 : if ((s[0] == '-' || s[0] == '+') &&
823 45 : 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 2740 : val = **ret;
833 2740 : (void)tz_sec;
834 2740 : if (!tzlocal && has_tz) /* convert into utc */
835 33 : val += offset * LL_CONSTANT(1000000);
836 2740 : if (!tzlocal && !has_tz) /* convert into utc */
837 362 : val -= tz_sec * LL_CONSTANT(1000000);
838 2740 : if (val < 0)
839 12 : val += DAY_USEC;
840 2728 : else if (val >= DAY_USEC)
841 0 : val -= DAY_USEC;
842 : /* and return */
843 2740 : **ret = val;
844 2740 : while (*s && GDKisspace(*s))
845 0 : s++;
846 2740 : 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 2443 : sql_daytime_fromstr(const char *buf, daytime *ret, long tz_sec, bool tclocal)
857 : {
858 2443 : size_t len = sizeof(daytime);
859 2443 : return daytime_tz_fromstr_internal(buf, &len, &ret, tz_sec, tclocal, false);
860 : }
861 :
862 : static ssize_t
863 4943 : do_daytime_precision_tostr(char *buf, size_t len, const daytime dt,
864 : int precision, bool external)
865 : {
866 4943 : int hour, min, sec, usec;
867 :
868 4943 : if (precision < 0)
869 : precision = 0;
870 4943 : if (len < 10 + (size_t) precision) {
871 : return -1;
872 : }
873 4943 : 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 4943 : usec = (int) (dt % 1000000);
882 4943 : sec = (int) (dt / 1000000);
883 4943 : hour = sec / 3600;
884 4943 : min = (sec % 3600) / 60;
885 4943 : sec %= 60;
886 :
887 4943 : if (precision == 0)
888 2504 : return snprintf(buf, len, "%02d:%02d:%02d", hour, min, sec);
889 2439 : 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 1734 : ssize_t l = snprintf(buf, len, "%02d:%02d:%02d.%06d", hour, min, sec, usec);
900 1734 : while (precision > 6) {
901 0 : precision--;
902 0 : buf[l++] = '0';
903 : }
904 1734 : buf[l] = '\0';
905 1734 : return l;
906 : }
907 : }
908 :
909 : ssize_t
910 4264 : daytime_precision_tostr(str *buf, size_t *len, const daytime dt,
911 : int precision, bool external)
912 : {
913 4264 : if (precision < 0)
914 : precision = 0;
915 4264 : 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 4264 : 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 22644 : timestamp_fromstr_internal(const char *buf, size_t *len, timestamp **ret, bool external, bool parse_offset)
932 : {
933 22644 : const char *s = buf;
934 22644 : ssize_t pos;
935 22644 : date dt;
936 22644 : daytime tm;
937 :
938 22644 : 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 22644 : while (*s && GDKisspace(*s))
945 0 : s++;
946 22644 : pos = parse_date(s, &dt, external);
947 22644 : if (pos < 0)
948 : return pos;
949 22463 : if (is_date_nil(dt)) {
950 0 : **ret = timestamp_nil;
951 0 : return pos;
952 : }
953 22463 : s += pos;
954 22463 : if (*s == '@' || *s == ' ' || *s == '-' || *s == 'T') {
955 22107 : while (*++s == ' ')
956 : ;
957 22107 : pos = parse_daytime(s, &tm, external);
958 22107 : if (pos < 0)
959 : return pos;
960 22107 : s += pos;
961 22107 : if (is_daytime_nil(tm)) {
962 0 : **ret = timestamp_nil;
963 0 : return (ssize_t) (s - buf);
964 : }
965 356 : } else if (*s) {
966 170 : tm = daytime_nil;
967 : } else {
968 186 : tm = mkdaytime(0, 0, 0, 0);
969 : }
970 22463 : if (is_date_nil(dt) || is_daytime_nil(tm)) {
971 170 : **ret = timestamp_nil;
972 : } else {
973 22293 : lng offset = 0;
974 :
975 22293 : **ret = mktimestamp(dt, tm);
976 22293 : if (parse_offset) {
977 21465 : while (GDKisspace(*s))
978 10 : s++;
979 : /* in case of gmt we need to add the time zone */
980 21455 : if (fleximatch(s, "gmt", 0) == 3) {
981 3 : s += 3;
982 : }
983 21455 : 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 21455 : **ret = timestamp_add_usec(**ret, offset);
993 : }
994 : }
995 22501 : while (*s && GDKisspace(*s))
996 38 : s++;
997 22463 : return (ssize_t) (s - buf);
998 : }
999 :
1000 : ssize_t
1001 21515 : timestamp_fromstr(const char *buf, size_t *len, timestamp **ret, bool external)
1002 : {
1003 21515 : return timestamp_fromstr_internal(buf, len, ret, external, true);
1004 : }
1005 :
1006 : static ssize_t
1007 1129 : timestamp_tz_fromstr_internal(const char *buf, size_t *len, timestamp **ret, long tz_sec, bool tzlocal, bool external)
1008 : {
1009 1129 : const char *s = buf;
1010 1129 : ssize_t pos = timestamp_fromstr_internal(s, len, ret, external, false);
1011 1129 : lng offset = 0;
1012 1129 : bool has_tz = false;
1013 :
1014 1129 : if (pos < 0 || is_timestamp_nil(**ret))
1015 : return pos;
1016 :
1017 838 : s = buf + pos;
1018 838 : pos = 0;
1019 838 : while (GDKisspace(*s))
1020 0 : s++;
1021 : /* in case of gmt we need to add the time zone */
1022 838 : if (fleximatch(s, "gmt", 0) == 3) {
1023 14 : s += 3;
1024 : }
1025 838 : if ((s[0] == '-' || s[0] == '+') &&
1026 62 : GDKisdigit(s[1]) && GDKisdigit(s[2]) && GDKisdigit(s[pos = 4]) &&
1027 50 : ((s[3] == ':' && GDKisdigit(s[5])) || GDKisdigit(s[pos = 3]))) {
1028 50 : 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 50 : pos += 2;
1030 50 : if (s[0] != '-')
1031 35 : offset = -offset;
1032 50 : s += pos;
1033 50 : has_tz = true;
1034 : }
1035 838 : if (!tzlocal && has_tz) /* convert into utc */
1036 33 : **ret = timestamp_add_usec(**ret, offset);
1037 838 : if (!tzlocal && !has_tz)
1038 188 : **ret = timestamp_add_usec(**ret, -tz_sec * LL_CONSTANT(1000000));
1039 842 : while (*s && GDKisspace(*s))
1040 4 : s++;
1041 838 : 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 1129 : sql_timestamp_fromstr(const char *buf, timestamp *ret, long tz_sec, bool tzlocal)
1053 : {
1054 1129 : size_t len = sizeof(timestamp);
1055 1129 : return timestamp_tz_fromstr_internal(buf, &len, &ret, tz_sec, tzlocal, false);
1056 : }
1057 :
1058 : ssize_t
1059 682 : timestamp_precision_tostr(str *buf, size_t *len, timestamp val, int precision, bool external)
1060 : {
1061 682 : ssize_t len1, len2;
1062 682 : char buf1[128], buf2[128];
1063 682 : date dt;
1064 682 : daytime tm;
1065 :
1066 682 : 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 679 : dt = ts_date(val);
1082 679 : tm = ts_time(val);
1083 679 : len1 = do_date_tostr(buf1, sizeof(buf1), &dt, false);
1084 679 : len2 = do_daytime_precision_tostr(buf2, sizeof(buf2), tm,
1085 : precision, false);
1086 679 : if (len1 < 0 || len2 < 0)
1087 : return -1;
1088 :
1089 679 : if (*len < 2 + (size_t) len1 + (size_t) len2 || *buf == NULL) {
1090 676 : GDKfree(*buf);
1091 676 : *buf = GDKmalloc(*len = (size_t) len1 + (size_t) len2 + 2);
1092 676 : if( *buf == NULL)
1093 : return -1;
1094 : }
1095 679 : return (ssize_t) strconcat_len(*buf, *len, buf1, " ", buf2, NULL);
1096 : }
1097 :
1098 : ssize_t
1099 682 : timestamp_tostr(str *buf, size_t *len, const timestamp *val, bool external)
1100 : {
1101 682 : return timestamp_precision_tostr(buf, len, *val, 6, external);
1102 : }
|