LCOV - code coverage report
Current view: top level - src/interfaces/ecpg/pgtypeslib - datetime.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 224 269 83.3 %
Date: 2019-11-21 12:06:29 Functions: 10 11 90.9 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13