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

Generated by: LCOV version 1.14