OSDN Git Service

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