1 /* src/interfaces/ecpg/pgtypeslib/datetime.c */
2
3 #include "postgres_fe.h"
4
5 #include <time.h>
6 #include <ctype.h>
7 #include <float.h>
8 #include <limits.h>
9
10 #include "extern.h"
11 #include "dt.h"
12 #include "pgtypes_error.h"
13 #include "pgtypes_date.h"
14
15 date *
PGTYPESdate_new(void)16 PGTYPESdate_new(void)
17 {
18 date *result;
19
20 result = (date *) pgtypes_alloc(sizeof(date));
21 /* result can be NULL if we run out of memory */
22 return result;
23 }
24
25 void
PGTYPESdate_free(date * d)26 PGTYPESdate_free(date * d)
27 {
28 free(d);
29 }
30
31 date
PGTYPESdate_from_timestamp(timestamp dt)32 PGTYPESdate_from_timestamp(timestamp dt)
33 {
34 date dDate;
35
36 dDate = 0; /* suppress compiler warning */
37
38 if (!TIMESTAMP_NOT_FINITE(dt))
39 {
40 #ifdef HAVE_INT64_TIMESTAMP
41 /* Microseconds to days */
42 dDate = (dt / USECS_PER_DAY);
43 #else
44 /* Seconds to days */
45 dDate = (dt / (double) SECS_PER_DAY);
46 #endif
47 }
48
49 return dDate;
50 }
51
52 date
PGTYPESdate_from_asc(char * str,char ** endptr)53 PGTYPESdate_from_asc(char *str, char **endptr)
54 {
55 date dDate;
56 fsec_t fsec;
57 struct tm tt,
58 *tm = &tt;
59 int dtype;
60 int nf;
61 char *field[MAXDATEFIELDS];
62 int ftype[MAXDATEFIELDS];
63 char lowstr[MAXDATELEN + MAXDATEFIELDS];
64 char *realptr;
65 char **ptr = (endptr != NULL) ? endptr : &realptr;
66
67 bool EuroDates = FALSE;
68
69 errno = 0;
70 if (strlen(str) > MAXDATELEN)
71 {
72 errno = PGTYPES_DATE_BAD_DATE;
73 return INT_MIN;
74 }
75
76 if (ParseDateTime(str, lowstr, field, ftype, &nf, ptr) != 0 ||
77 DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, EuroDates) != 0)
78 {
79 errno = PGTYPES_DATE_BAD_DATE;
80 return INT_MIN;
81 }
82
83 switch (dtype)
84 {
85 case DTK_DATE:
86 break;
87
88 case DTK_EPOCH:
89 if (GetEpochTime(tm) < 0)
90 {
91 errno = PGTYPES_DATE_BAD_DATE;
92 return INT_MIN;
93 }
94 break;
95
96 default:
97 errno = PGTYPES_DATE_BAD_DATE;
98 return INT_MIN;
99 }
100
101 dDate = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));
102
103 return dDate;
104 }
105
106 char *
PGTYPESdate_to_asc(date dDate)107 PGTYPESdate_to_asc(date dDate)
108 {
109 struct tm tt,
110 *tm = &tt;
111 char buf[MAXDATELEN + 1];
112 int DateStyle = 1;
113 bool EuroDates = FALSE;
114
115 j2date(dDate + date2j(2000, 1, 1), &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
116 EncodeDateOnly(tm, DateStyle, buf, EuroDates);
117 return pgtypes_strdup(buf);
118 }
119
120 void
PGTYPESdate_julmdy(date jd,int * mdy)121 PGTYPESdate_julmdy(date jd, int *mdy)
122 {
123 int y,
124 m,
125 d;
126
127 j2date((int) (jd + date2j(2000, 1, 1)), &y, &m, &d);
128 mdy[0] = m;
129 mdy[1] = d;
130 mdy[2] = y;
131 }
132
133 void
PGTYPESdate_mdyjul(int * mdy,date * jdate)134 PGTYPESdate_mdyjul(int *mdy, date * jdate)
135 {
136 /* month is mdy[0] */
137 /* day is mdy[1] */
138 /* year is mdy[2] */
139
140 *jdate = (date) (date2j(mdy[2], mdy[0], mdy[1]) - date2j(2000, 1, 1));
141 }
142
143 int
PGTYPESdate_dayofweek(date dDate)144 PGTYPESdate_dayofweek(date dDate)
145 {
146 /*
147 * Sunday: 0 Monday: 1 Tuesday: 2 Wednesday: 3 Thursday: 4
148 * Friday: 5 Saturday: 6
149 */
150 return (int) (dDate + date2j(2000, 1, 1) + 1) % 7;
151 }
152
153 void
PGTYPESdate_today(date * d)154 PGTYPESdate_today(date * d)
155 {
156 struct tm ts;
157
158 GetCurrentDateTime(&ts);
159 if (errno == 0)
160 *d = date2j(ts.tm_year, ts.tm_mon, ts.tm_mday) - date2j(2000, 1, 1);
161 return;
162 }
163
164 #define PGTYPES_DATE_NUM_MAX_DIGITS 20 /* should suffice for most
165 * years... */
166
167 #define PGTYPES_FMTDATE_DAY_DIGITS_LZ 1 /* LZ means "leading zeroes" */
168 #define PGTYPES_FMTDATE_DOW_LITERAL_SHORT 2
169 #define PGTYPES_FMTDATE_MONTH_DIGITS_LZ 3
170 #define PGTYPES_FMTDATE_MONTH_LITERAL_SHORT 4
171 #define PGTYPES_FMTDATE_YEAR_DIGITS_SHORT 5
172 #define PGTYPES_FMTDATE_YEAR_DIGITS_LONG 6
173
174 int
PGTYPESdate_fmt_asc(date dDate,const char * fmtstring,char * outbuf)175 PGTYPESdate_fmt_asc(date dDate, const char *fmtstring, char *outbuf)
176 {
177 static struct
178 {
179 char *format;
180 int component;
181 } mapping[] =
182 {
183 /*
184 * format items have to be sorted according to their length, since the
185 * first pattern that matches gets replaced by its value
186 */
187 {
188 "ddd", PGTYPES_FMTDATE_DOW_LITERAL_SHORT
189 },
190 {
191 "dd", PGTYPES_FMTDATE_DAY_DIGITS_LZ
192 },
193 {
194 "mmm", PGTYPES_FMTDATE_MONTH_LITERAL_SHORT
195 },
196 {
197 "mm", PGTYPES_FMTDATE_MONTH_DIGITS_LZ
198 },
199 {
200 "yyyy", PGTYPES_FMTDATE_YEAR_DIGITS_LONG
201 },
202 {
203 "yy", PGTYPES_FMTDATE_YEAR_DIGITS_SHORT
204 },
205 {
206 NULL, 0
207 }
208 };
209
210 union un_fmt_comb replace_val;
211 int replace_type;
212
213 int i;
214 int dow;
215 char *start_pattern;
216 struct tm tm;
217
218 /* copy the string over */
219 strcpy(outbuf, fmtstring);
220
221 /* get the date */
222 j2date(dDate + date2j(2000, 1, 1), &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
223 dow = PGTYPESdate_dayofweek(dDate);
224
225 for (i = 0; mapping[i].format != NULL; i++)
226 {
227 while ((start_pattern = strstr(outbuf, mapping[i].format)) != NULL)
228 {
229 switch (mapping[i].component)
230 {
231 case PGTYPES_FMTDATE_DOW_LITERAL_SHORT:
232 replace_val.str_val = pgtypes_date_weekdays_short[dow];
233 replace_type = PGTYPES_TYPE_STRING_CONSTANT;
234 break;
235 case PGTYPES_FMTDATE_DAY_DIGITS_LZ:
236 replace_val.uint_val = tm.tm_mday;
237 replace_type = PGTYPES_TYPE_UINT_2_LZ;
238 break;
239 case PGTYPES_FMTDATE_MONTH_LITERAL_SHORT:
240 replace_val.str_val = months[tm.tm_mon - 1];
241 replace_type = PGTYPES_TYPE_STRING_CONSTANT;
242 break;
243 case PGTYPES_FMTDATE_MONTH_DIGITS_LZ:
244 replace_val.uint_val = tm.tm_mon;
245 replace_type = PGTYPES_TYPE_UINT_2_LZ;
246 break;
247 case PGTYPES_FMTDATE_YEAR_DIGITS_LONG:
248 replace_val.uint_val = tm.tm_year;
249 replace_type = PGTYPES_TYPE_UINT_4_LZ;
250 break;
251 case PGTYPES_FMTDATE_YEAR_DIGITS_SHORT:
252 replace_val.uint_val = tm.tm_year % 100;
253 replace_type = PGTYPES_TYPE_UINT_2_LZ;
254 break;
255 default:
256
257 /*
258 * should not happen, set something anyway
259 */
260 replace_val.str_val = " ";
261 replace_type = PGTYPES_TYPE_STRING_CONSTANT;
262 }
263 switch (replace_type)
264 {
265 case PGTYPES_TYPE_STRING_MALLOCED:
266 case PGTYPES_TYPE_STRING_CONSTANT:
267 memcpy(start_pattern, replace_val.str_val,
268 strlen(replace_val.str_val));
269 if (replace_type == PGTYPES_TYPE_STRING_MALLOCED)
270 free(replace_val.str_val);
271 break;
272 case PGTYPES_TYPE_UINT:
273 {
274 char *t = pgtypes_alloc(PGTYPES_DATE_NUM_MAX_DIGITS);
275
276 if (!t)
277 return -1;
278 snprintf(t, PGTYPES_DATE_NUM_MAX_DIGITS,
279 "%u", replace_val.uint_val);
280 memcpy(start_pattern, t, strlen(t));
281 free(t);
282 }
283 break;
284 case PGTYPES_TYPE_UINT_2_LZ:
285 {
286 char *t = pgtypes_alloc(PGTYPES_DATE_NUM_MAX_DIGITS);
287
288 if (!t)
289 return -1;
290 snprintf(t, PGTYPES_DATE_NUM_MAX_DIGITS,
291 "%02u", replace_val.uint_val);
292 memcpy(start_pattern, t, strlen(t));
293 free(t);
294 }
295 break;
296 case PGTYPES_TYPE_UINT_4_LZ:
297 {
298 char *t = pgtypes_alloc(PGTYPES_DATE_NUM_MAX_DIGITS);
299
300 if (!t)
301 return -1;
302 snprintf(t, PGTYPES_DATE_NUM_MAX_DIGITS,
303 "%04u", replace_val.uint_val);
304 memcpy(start_pattern, t, strlen(t));
305 free(t);
306 }
307 break;
308 default:
309
310 /*
311 * doesn't happen (we set replace_type to
312 * PGTYPES_TYPE_STRING_CONSTANT in case of an error above)
313 */
314 break;
315 }
316 }
317 }
318 return 0;
319 }
320
321
322 /*
323 * PGTYPESdate_defmt_asc
324 *
325 * function works as follows:
326 * - first we analyze the parameters
327 * - if this is a special case with no delimiters, add delimiters
328 * - find the tokens. First we look for numerical values. If we have found
329 * less than 3 tokens, we check for the months' names and thereafter for
330 * the abbreviations of the months' names.
331 * - then we see which parameter should be the date, the month and the
332 * year and from these values we calculate the date
333 */
334
335 #define PGTYPES_DATE_MONTH_MAXLENGTH 20 /* probably even less :-) */
336 int
PGTYPESdate_defmt_asc(date * d,const char * fmt,char * str)337 PGTYPESdate_defmt_asc(date * d, const char *fmt, char *str)
338 {
339 /*
340 * token[2] = { 4,6 } means that token 2 starts at position 4 and ends at
341 * (including) position 6
342 */
343 int token[3][2];
344 int token_values[3] = {-1, -1, -1};
345 char *fmt_token_order;
346 char *fmt_ystart,
347 *fmt_mstart,
348 *fmt_dstart;
349 unsigned int i;
350 int reading_digit;
351 int token_count;
352 char *str_copy;
353 struct tm tm;
354
355 tm.tm_year = tm.tm_mon = tm.tm_mday = 0; /* keep compiler quiet */
356
357 if (!d || !str || !fmt)
358 {
359 errno = PGTYPES_DATE_ERR_EARGS;
360 return -1;
361 }
362
363 /* analyze the fmt string */
364 fmt_ystart = strstr(fmt, "yy");
365 fmt_mstart = strstr(fmt, "mm");
366 fmt_dstart = strstr(fmt, "dd");
367
368 if (!fmt_ystart || !fmt_mstart || !fmt_dstart)
369 {
370 errno = PGTYPES_DATE_ERR_EARGS;
371 return -1;
372 }
373
374 if (fmt_ystart < fmt_mstart)
375 {
376 /* y m */
377 if (fmt_dstart < fmt_ystart)
378 {
379 /* d y m */
380 fmt_token_order = "dym";
381 }
382 else if (fmt_dstart > fmt_mstart)
383 {
384 /* y m d */
385 fmt_token_order = "ymd";
386 }
387 else
388 {
389 /* y d m */
390 fmt_token_order = "ydm";
391 }
392 }
393 else
394 {
395 /* fmt_ystart > fmt_mstart */
396 /* m y */
397 if (fmt_dstart < fmt_mstart)
398 {
399 /* d m y */
400 fmt_token_order = "dmy";
401 }
402 else if (fmt_dstart > fmt_ystart)
403 {
404 /* m y d */
405 fmt_token_order = "myd";
406 }
407 else
408 {
409 /* m d y */
410 fmt_token_order = "mdy";
411 }
412 }
413
414 /*
415 * handle the special cases where there is no delimiter between the
416 * digits. If we see this:
417 *
418 * only digits, 6 or 8 bytes then it might be ddmmyy and ddmmyyyy (or
419 * similar)
420 *
421 * we reduce it to a string with delimiters and continue processing
422 */
423
424 /* check if we have only digits */
425 reading_digit = 1;
426 for (i = 0; str[i]; i++)
427 {
428 if (!isdigit((unsigned char) str[i]))
429 {
430 reading_digit = 0;
431 break;
432 }
433 }
434 if (reading_digit)
435 {
436 int frag_length[3];
437 int target_pos;
438
439 i = strlen(str);
440 if (i != 8 && i != 6)
441 {
442 errno = PGTYPES_DATE_ERR_ENOSHORTDATE;
443 return -1;
444 }
445 /* okay, this really is the special case */
446
447 /*
448 * as long as the string, one additional byte for the terminator and 2
449 * for the delimiters between the 3 fiedls
450 */
451 str_copy = pgtypes_alloc(strlen(str) + 1 + 2);
452 if (!str_copy)
453 return -1;
454
455 /* determine length of the fragments */
456 if (i == 6)
457 {
458 frag_length[0] = 2;
459 frag_length[1] = 2;
460 frag_length[2] = 2;
461 }
462 else
463 {
464 if (fmt_token_order[0] == 'y')
465 {
466 frag_length[0] = 4;
467 frag_length[1] = 2;
468 frag_length[2] = 2;
469 }
470 else if (fmt_token_order[1] == 'y')
471 {
472 frag_length[0] = 2;
473 frag_length[1] = 4;
474 frag_length[2] = 2;
475 }
476 else
477 {
478 frag_length[0] = 2;
479 frag_length[1] = 2;
480 frag_length[2] = 4;
481 }
482 }
483 target_pos = 0;
484
485 /*
486 * XXX: Here we could calculate the positions of the tokens and save
487 * the for loop down there where we again check with isdigit() for
488 * digits.
489 */
490 for (i = 0; i < 3; i++)
491 {
492 int start_pos = 0;
493
494 if (i >= 1)
495 start_pos += frag_length[0];
496 if (i == 2)
497 start_pos += frag_length[1];
498
499 strncpy(str_copy + target_pos, str + start_pos,
500 frag_length[i]);
501 target_pos += frag_length[i];
502 if (i != 2)
503 {
504 str_copy[target_pos] = ' ';
505 target_pos++;
506 }
507 }
508 str_copy[target_pos] = '\0';
509 }
510 else
511 {
512 str_copy = pgtypes_strdup(str);
513 if (!str_copy)
514 return -1;
515
516 /* convert the whole string to lower case */
517 for (i = 0; str_copy[i]; i++)
518 str_copy[i] = (char) pg_tolower((unsigned char) str_copy[i]);
519 }
520
521 /* look for numerical tokens */
522 reading_digit = 0;
523 token_count = 0;
524 for (i = 0; i < strlen(str_copy); i++)
525 {
526 if (!isdigit((unsigned char) str_copy[i]) && reading_digit)
527 {
528 /* the token is finished */
529 token[token_count][1] = i - 1;
530 reading_digit = 0;
531 token_count++;
532 }
533 else if (isdigit((unsigned char) str_copy[i]) && !reading_digit)
534 {
535 /* we have found a token */
536 token[token_count][0] = i;
537 reading_digit = 1;
538 }
539 }
540
541 /*
542 * we're at the end of the input string, but maybe we are still reading a
543 * number...
544 */
545 if (reading_digit)
546 {
547 token[token_count][1] = i - 1;
548 token_count++;
549 }
550
551
552 if (token_count < 2)
553 {
554 /*
555 * not all tokens found, no way to find 2 missing tokens with string
556 * matches
557 */
558 free(str_copy);
559 errno = PGTYPES_DATE_ERR_ENOSHORTDATE;
560 return -1;
561 }
562
563 if (token_count != 3)
564 {
565 /*
566 * not all tokens found but we may find another one with string
567 * matches by testing for the months names and months abbreviations
568 */
569 char *month_lower_tmp = pgtypes_alloc(PGTYPES_DATE_MONTH_MAXLENGTH);
570 char *start_pos;
571 int j;
572 int offset;
573 int found = 0;
574 char **list;
575
576 if (!month_lower_tmp)
577 {
578 /* free variables we alloc'ed before */
579 free(str_copy);
580 return -1;
581 }
582 list = pgtypes_date_months;
583 for (i = 0; list[i]; i++)
584 {
585 for (j = 0; j < PGTYPES_DATE_MONTH_MAXLENGTH; j++)
586 {
587 month_lower_tmp[j] = (char) pg_tolower((unsigned char) list[i][j]);
588 if (!month_lower_tmp[j])
589 {
590 /* properly terminated */
591 break;
592 }
593 }
594 if ((start_pos = strstr(str_copy, month_lower_tmp)))
595 {
596 offset = start_pos - str_copy;
597
598 /*
599 * sort the new token into the numeric tokens, shift them if
600 * necessary
601 */
602 if (offset < token[0][0])
603 {
604 token[2][0] = token[1][0];
605 token[2][1] = token[1][1];
606 token[1][0] = token[0][0];
607 token[1][1] = token[0][1];
608 token_count = 0;
609 }
610 else if (offset < token[1][0])
611 {
612 token[2][0] = token[1][0];
613 token[2][1] = token[1][1];
614 token_count = 1;
615 }
616 else
617 token_count = 2;
618 token[token_count][0] = offset;
619 token[token_count][1] = offset + strlen(month_lower_tmp) - 1;
620
621 /*
622 * the value is the index of the month in the array of months
623 * + 1 (January is month 0)
624 */
625 token_values[token_count] = i + 1;
626 found = 1;
627 break;
628 }
629
630 /*
631 * evil[tm] hack: if we read the pgtypes_date_months and haven't
632 * found a match, reset list to point to pgtypes_date_months_short
633 * and reset the counter variable i
634 */
635 if (list == pgtypes_date_months)
636 {
637 if (list[i + 1] == NULL)
638 {
639 list = months;
640 i = -1;
641 }
642 }
643 }
644 if (!found)
645 {
646 free(month_lower_tmp);
647 free(str_copy);
648 errno = PGTYPES_DATE_ERR_ENOTDMY;
649 return -1;
650 }
651
652 /*
653 * here we found a month. token[token_count] and
654 * token_values[token_count] reflect the month's details.
655 *
656 * only the month can be specified with a literal. Here we can do a
657 * quick check if the month is at the right position according to the
658 * format string because we can check if the token that we expect to
659 * be the month is at the position of the only token that already has
660 * a value. If we wouldn't check here we could say "December 4 1990"
661 * with a fmt string of "dd mm yy" for 12 April 1990.
662 */
663 if (fmt_token_order[token_count] != 'm')
664 {
665 /* deal with the error later on */
666 token_values[token_count] = -1;
667 }
668 free(month_lower_tmp);
669 }
670
671 /* terminate the tokens with ASCII-0 and get their values */
672 for (i = 0; i < 3; i++)
673 {
674 *(str_copy + token[i][1] + 1) = '\0';
675 /* A month already has a value set, check for token_value == -1 */
676 if (token_values[i] == -1)
677 {
678 errno = 0;
679 token_values[i] = strtol(str_copy + token[i][0], (char **) NULL, 10);
680 /* strtol sets errno in case of an error */
681 if (errno)
682 token_values[i] = -1;
683 }
684 if (fmt_token_order[i] == 'd')
685 tm.tm_mday = token_values[i];
686 else if (fmt_token_order[i] == 'm')
687 tm.tm_mon = token_values[i];
688 else if (fmt_token_order[i] == 'y')
689 tm.tm_year = token_values[i];
690 }
691 free(str_copy);
692
693 if (tm.tm_mday < 1 || tm.tm_mday > 31)
694 {
695 errno = PGTYPES_DATE_BAD_DAY;
696 return -1;
697 }
698
699 if (tm.tm_mon < 1 || tm.tm_mon > MONTHS_PER_YEAR)
700 {
701 errno = PGTYPES_DATE_BAD_MONTH;
702 return -1;
703 }
704
705 if (tm.tm_mday == 31 && (tm.tm_mon == 4 || tm.tm_mon == 6 || tm.tm_mon == 9 || tm.tm_mon == 11))
706 {
707 errno = PGTYPES_DATE_BAD_DAY;
708 return -1;
709 }
710
711 if (tm.tm_mon == 2 && tm.tm_mday > 29)
712 {
713 errno = PGTYPES_DATE_BAD_DAY;
714 return -1;
715 }
716
717 *d = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - date2j(2000, 1, 1);
718
719 return 0;
720 }
721