LCOV - code coverage report
Current view: top level - src/interfaces/ecpg/pgtypeslib - timestamp.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 37.4 % 449 168
Test Date: 2026-07-25 22:15:46 Functions: 73.3 % 15 11
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 27.2 % 239 65

             Branch data     Line data    Source code
       1                 :             : /*
       2                 :             :  * src/interfaces/ecpg/pgtypeslib/timestamp.c
       3                 :             :  */
       4                 :             : #include "postgres_fe.h"
       5                 :             : 
       6                 :             : #include <time.h>
       7                 :             : #include <limits.h>
       8                 :             : #include <math.h>
       9                 :             : 
      10                 :             : #include "common/int.h"
      11                 :             : #include "dt.h"
      12                 :             : #include "pgtypes_date.h"
      13                 :             : #include "pgtypes_timestamp.h"
      14                 :             : #include "pgtypeslib_extern.h"
      15                 :             : 
      16                 :             : static int64
      17                 :         294 : time2t(const int hour, const int min, const int sec, const fsec_t fsec)
      18                 :             : {
      19                 :         294 :     return (((((hour * MINS_PER_HOUR) + min) * SECS_PER_MINUTE) + sec) * USECS_PER_SEC) + fsec;
      20                 :             : }                               /* time2t() */
      21                 :             : 
      22                 :             : static timestamp
      23                 :          42 : dt2local(timestamp dt, int tz)
      24                 :             : {
      25                 :          42 :     dt -= (tz * USECS_PER_SEC);
      26                 :          42 :     return dt;
      27                 :             : }                               /* dt2local() */
      28                 :             : 
      29                 :             : /*
      30                 :             :  * tm2timestamp()
      31                 :             :  * Convert a tm structure to a timestamp data type.
      32                 :             :  * Note that year is _not_ 1900-based, but is an explicit full value.
      33                 :             :  * Also, month is one-based, _not_ zero-based.
      34                 :             :  *
      35                 :             :  * Returns -1 on failure (overflow).
      36                 :             :  */
      37                 :             : int
      38                 :         294 : tm2timestamp(struct tm *tm, fsec_t fsec, int *tzp, timestamp * result)
      39                 :             : {
      40                 :             :     int         dDate;
      41                 :             :     int64       time;
      42                 :             : 
      43                 :             :     /* Prevent overflow in Julian-day routines */
      44   [ -  +  -  -  :         294 :     if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
          -  -  -  +  -  
                -  -  - ]
      45                 :           0 :         return -1;
      46                 :             : 
      47                 :         294 :     dDate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
      48                 :         294 :     time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
      49   [ +  -  -  +  :         294 :     if (unlikely(pg_mul_s64_overflow(dDate, USECS_PER_DAY, result) ||
                   -  + ]
      50                 :             :                  pg_add_s64_overflow(*result, time, result)))
      51                 :           0 :         return -1;
      52         [ +  + ]:         294 :     if (tzp != NULL)
      53                 :          42 :         *result = dt2local(*result, -(*tzp));
      54                 :             : 
      55                 :             :     /* final range check catches just-out-of-range timestamps */
      56   [ +  -  -  + ]:         294 :     if (!IS_VALID_TIMESTAMP(*result))
      57                 :           0 :         return -1;
      58                 :             : 
      59                 :         294 :     return 0;
      60                 :             : }                               /* tm2timestamp() */
      61                 :             : 
      62                 :             : static timestamp
      63                 :           0 : SetEpochTimestamp(void)
      64                 :             : {
      65                 :           0 :     int64       noresult = 0;
      66                 :             :     timestamp   dt;
      67                 :             :     struct tm   tt,
      68                 :           0 :                *tm = &tt;
      69                 :             : 
      70         [ #  # ]:           0 :     if (GetEpochTime(tm) < 0)
      71                 :           0 :         return noresult;
      72                 :             : 
      73                 :           0 :     tm2timestamp(tm, 0, NULL, &dt);
      74                 :           0 :     return dt;
      75                 :             : }                               /* SetEpochTimestamp() */
      76                 :             : 
      77                 :             : /*
      78                 :             :  * timestamp2tm()
      79                 :             :  * Convert timestamp data type to POSIX time structure.
      80                 :             :  * Note that year is _not_ 1900-based, but is an explicit full value.
      81                 :             :  * Also, month is one-based, _not_ zero-based.
      82                 :             :  * Returns:
      83                 :             :  *   0 on success
      84                 :             :  *  -1 on out of range
      85                 :             :  *
      86                 :             :  * For dates within the system-supported time_t range, convert to the
      87                 :             :  *  local time zone. If out of this range, leave as GMT. - tgl 97/05/27
      88                 :             :  */
      89                 :             : static int
      90                 :         328 : timestamp2tm(timestamp dt, int *tzp, struct tm *tm, fsec_t *fsec, const char **tzn)
      91                 :             : {
      92                 :             :     int64       dDate,
      93                 :             :                 date0;
      94                 :             :     int64       time;
      95                 :             : #if defined(HAVE_STRUCT_TM_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
      96                 :             :     time_t      utime;
      97                 :             :     struct tm  *tx;
      98                 :             : #endif
      99                 :             : 
     100                 :         328 :     date0 = date2j(2000, 1, 1);
     101                 :             : 
     102                 :         328 :     time = dt;
     103         [ +  + ]:         328 :     TMODULO(time, dDate, USECS_PER_DAY);
     104                 :             : 
     105         [ +  + ]:         328 :     if (time < INT64CONST(0))
     106                 :             :     {
     107                 :         184 :         time += USECS_PER_DAY;
     108                 :         184 :         dDate -= 1;
     109                 :             :     }
     110                 :             : 
     111                 :             :     /* add offset to go from J2000 back to standard Julian date */
     112                 :         328 :     dDate += date0;
     113                 :             : 
     114                 :             :     /* Julian day routine does not work for negative Julian days */
     115   [ +  -  -  + ]:         328 :     if (dDate < 0 || dDate > (timestamp) INT_MAX)
     116                 :           0 :         return -1;
     117                 :             : 
     118                 :         328 :     j2date((int) dDate, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
     119                 :         328 :     dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
     120                 :             : 
     121         [ -  + ]:         328 :     if (tzp != NULL)
     122                 :             :     {
     123                 :             :         /*
     124                 :             :          * Does this fall within the capabilities of the localtime()
     125                 :             :          * interface? Then use this to rotate to the local time zone.
     126                 :             :          */
     127   [ #  #  #  #  :           0 :         if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                      # ]
     128                 :           0 :         {
     129                 :             : #if defined(HAVE_STRUCT_TM_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
     130                 :             :             struct tm   tmbuf;
     131                 :             : 
     132                 :           0 :             utime = dt / USECS_PER_SEC +
     133                 :           0 :                 ((date0 - date2j(1970, 1, 1)) * INT64CONST(86400));
     134                 :             : 
     135                 :           0 :             tx = localtime_r(&utime, &tmbuf);
     136                 :           0 :             tm->tm_year = tx->tm_year + 1900;
     137                 :           0 :             tm->tm_mon = tx->tm_mon + 1;
     138                 :           0 :             tm->tm_mday = tx->tm_mday;
     139                 :           0 :             tm->tm_hour = tx->tm_hour;
     140                 :           0 :             tm->tm_min = tx->tm_min;
     141                 :           0 :             tm->tm_isdst = tx->tm_isdst;
     142                 :             : 
     143                 :             : #if defined(HAVE_STRUCT_TM_TM_ZONE)
     144                 :           0 :             tm->tm_gmtoff = tx->tm_gmtoff;
     145                 :           0 :             tm->tm_zone = tx->tm_zone;
     146                 :             : 
     147                 :           0 :             *tzp = -tm->tm_gmtoff;   /* tm_gmtoff is Sun/DEC-ism */
     148         [ #  # ]:           0 :             if (tzn != NULL)
     149                 :           0 :                 *tzn = tm->tm_zone;
     150                 :             : #elif defined(HAVE_INT_TIMEZONE)
     151                 :             :             *tzp = (tm->tm_isdst > 0) ? TIMEZONE_GLOBAL - SECS_PER_HOUR : TIMEZONE_GLOBAL;
     152                 :             :             if (tzn != NULL)
     153                 :             :                 *tzn = TZNAME_GLOBAL[(tm->tm_isdst > 0)];
     154                 :             : #endif
     155                 :             : #else                           /* not (HAVE_STRUCT_TM_TM_ZONE ||
     156                 :             :                                  * HAVE_INT_TIMEZONE) */
     157                 :             :             *tzp = 0;
     158                 :             :             /* Mark this as *no* time zone available */
     159                 :             :             tm->tm_isdst = -1;
     160                 :             :             if (tzn != NULL)
     161                 :             :                 *tzn = NULL;
     162                 :             : #endif
     163                 :             :         }
     164                 :             :         else
     165                 :             :         {
     166                 :           0 :             *tzp = 0;
     167                 :             :             /* Mark this as *no* time zone available */
     168                 :           0 :             tm->tm_isdst = -1;
     169         [ #  # ]:           0 :             if (tzn != NULL)
     170                 :           0 :                 *tzn = NULL;
     171                 :             :         }
     172                 :             :     }
     173                 :             :     else
     174                 :             :     {
     175                 :         328 :         tm->tm_isdst = -1;
     176         [ -  + ]:         328 :         if (tzn != NULL)
     177                 :           0 :             *tzn = NULL;
     178                 :             :     }
     179                 :             : 
     180                 :         328 :     tm->tm_yday = dDate - date2j(tm->tm_year, 1, 1) + 1;
     181                 :             : 
     182                 :         328 :     return 0;
     183                 :             : }                               /* timestamp2tm() */
     184                 :             : 
     185                 :             : /*
     186                 :             :  * EncodeSpecialTimestamp()
     187                 :             :  *  * Convert reserved timestamp data type to string.
     188                 :             :  *
     189                 :             :  */
     190                 :             : static void
     191                 :           0 : EncodeSpecialTimestamp(timestamp dt, char *str)
     192                 :             : {
     193         [ #  # ]:           0 :     if (TIMESTAMP_IS_NOBEGIN(dt))
     194                 :           0 :         strcpy(str, EARLY);
     195         [ #  # ]:           0 :     else if (TIMESTAMP_IS_NOEND(dt))
     196                 :           0 :         strcpy(str, LATE);
     197                 :             :     else
     198                 :           0 :         abort();                /* shouldn't happen */
     199                 :           0 : }
     200                 :             : 
     201                 :             : timestamp
     202                 :         252 : PGTYPEStimestamp_from_asc(char *str, char **endptr)
     203                 :             : {
     204                 :             :     timestamp   result;
     205                 :         252 :     int64       noresult = 0;
     206                 :             :     fsec_t      fsec;
     207                 :             :     struct tm   tt,
     208                 :         252 :                *tm = &tt;
     209                 :             :     int         dtype;
     210                 :             :     int         nf;
     211                 :             :     char       *field[MAXDATEFIELDS];
     212                 :             :     int         ftype[MAXDATEFIELDS];
     213                 :             :     char        lowstr[MAXDATELEN + MAXDATEFIELDS];
     214                 :             :     char       *realptr;
     215         [ +  + ]:         252 :     char      **ptr = (endptr != NULL) ? endptr : &realptr;
     216                 :             : 
     217         [ -  + ]:         252 :     if (strlen(str) > MAXDATELEN)
     218                 :             :     {
     219                 :           0 :         errno = PGTYPES_TS_BAD_TIMESTAMP;
     220                 :           0 :         return noresult;
     221                 :             :     }
     222                 :             : 
     223   [ +  -  +  + ]:         504 :     if (ParseDateTime(str, lowstr, field, ftype, &nf, ptr) != 0 ||
     224                 :         252 :         DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, 0) != 0)
     225                 :             :     {
     226                 :           4 :         errno = PGTYPES_TS_BAD_TIMESTAMP;
     227                 :           4 :         return noresult;
     228                 :             :     }
     229                 :             : 
     230   [ +  -  -  -  :         248 :     switch (dtype)
                      - ]
     231                 :             :     {
     232                 :         248 :         case DTK_DATE:
     233         [ -  + ]:         248 :             if (tm2timestamp(tm, fsec, NULL, &result) != 0)
     234                 :             :             {
     235                 :           0 :                 errno = PGTYPES_TS_BAD_TIMESTAMP;
     236                 :           0 :                 return noresult;
     237                 :             :             }
     238                 :         248 :             break;
     239                 :             : 
     240                 :           0 :         case DTK_EPOCH:
     241                 :           0 :             result = SetEpochTimestamp();
     242                 :           0 :             break;
     243                 :             : 
     244                 :           0 :         case DTK_LATE:
     245                 :           0 :             TIMESTAMP_NOEND(result);
     246                 :           0 :             break;
     247                 :             : 
     248                 :           0 :         case DTK_EARLY:
     249                 :           0 :             TIMESTAMP_NOBEGIN(result);
     250                 :           0 :             break;
     251                 :             : 
     252                 :           0 :         default:
     253                 :           0 :             errno = PGTYPES_TS_BAD_TIMESTAMP;
     254                 :           0 :             return noresult;
     255                 :             :     }
     256                 :             : 
     257                 :             :     /* AdjustTimestampForTypmod(&result, typmod); */
     258                 :             : 
     259                 :             :     /*
     260                 :             :      * Since it's difficult to test for noresult, make sure errno is 0 if no
     261                 :             :      * error occurred.
     262                 :             :      */
     263                 :         248 :     errno = 0;
     264                 :         248 :     return result;
     265                 :             : }
     266                 :             : 
     267                 :             : char *
     268                 :         318 : PGTYPEStimestamp_to_asc(timestamp tstamp)
     269                 :             : {
     270                 :             :     struct tm   tt,
     271                 :         318 :                *tm = &tt;
     272                 :             :     char        buf[MAXDATELEN + 1];
     273                 :             :     fsec_t      fsec;
     274                 :         318 :     int         DateStyle = 1;  /* this defaults to USE_ISO_DATES, shall we
     275                 :             :                                  * make it an option? */
     276                 :             : 
     277   [ +  -  -  + ]:         318 :     if (TIMESTAMP_NOT_FINITE(tstamp))
     278                 :           0 :         EncodeSpecialTimestamp(tstamp, buf);
     279         [ +  - ]:         318 :     else if (timestamp2tm(tstamp, NULL, tm, &fsec, NULL) == 0)
     280                 :         318 :         EncodeDateTime(tm, fsec, false, 0, NULL, DateStyle, buf, 0);
     281                 :             :     else
     282                 :             :     {
     283                 :           0 :         errno = PGTYPES_TS_BAD_TIMESTAMP;
     284                 :           0 :         return NULL;
     285                 :             :     }
     286                 :         318 :     return pgtypes_strdup(buf);
     287                 :             : }
     288                 :             : 
     289                 :             : void
     290                 :           2 : PGTYPEStimestamp_current(timestamp * ts)
     291                 :             : {
     292                 :             :     struct tm   tm;
     293                 :             : 
     294                 :           2 :     GetCurrentDateTime(&tm);
     295         [ +  - ]:           2 :     if (errno == 0)
     296                 :           2 :         tm2timestamp(&tm, 0, NULL, ts);
     297                 :           2 : }
     298                 :             : 
     299                 :             : static int
     300                 :           8 : dttofmtasc_replace(timestamp * ts, date dDate, int dow, struct tm *tm,
     301                 :             :                    char *output, int *pstr_len, const char *fmtstr)
     302                 :             : {
     303                 :             :     union un_fmt_comb replace_val;
     304                 :             :     int         replace_type;
     305                 :             :     int         i;
     306                 :           8 :     const char *p = fmtstr;
     307                 :           8 :     char       *q = output;
     308                 :             : 
     309         [ +  + ]:         146 :     while (*p)
     310                 :             :     {
     311         [ +  + ]:         138 :         if (*p == '%')
     312                 :             :         {
     313                 :          38 :             p++;
     314                 :             :             /* fix compiler warning */
     315                 :          38 :             replace_type = PGTYPES_TYPE_NOTHING;
     316   [ +  -  +  -  :          38 :             switch (*p)
          -  -  +  -  -  
          -  -  -  +  -  
          +  -  -  -  +  
          -  -  -  -  -  
          -  +  -  -  -  
          -  -  -  -  +  
          +  -  +  -  -  
                +  -  - ]
     317                 :             :             {
     318                 :             :                     /* the abbreviated name of the day in the week */
     319                 :             :                     /* XXX should be locale aware */
     320                 :           4 :                 case 'a':
     321                 :           4 :                     replace_val.str_val = pgtypes_date_weekdays_short[dow];
     322                 :           4 :                     replace_type = PGTYPES_TYPE_STRING_CONSTANT;
     323                 :           4 :                     break;
     324                 :             :                     /* the full name of the day in the week */
     325                 :             :                     /* XXX should be locale aware */
     326                 :           0 :                 case 'A':
     327                 :           0 :                     replace_val.str_val = days[dow];
     328                 :           0 :                     replace_type = PGTYPES_TYPE_STRING_CONSTANT;
     329                 :           0 :                     break;
     330                 :             :                     /* the abbreviated name of the month */
     331                 :             :                     /* XXX should be locale aware */
     332                 :           4 :                 case 'b':
     333                 :             :                 case 'h':
     334                 :           4 :                     replace_val.str_val = months[tm->tm_mon - 1];
     335                 :           4 :                     replace_type = PGTYPES_TYPE_STRING_CONSTANT;
     336                 :           4 :                     break;
     337                 :             :                     /* the full name of the month */
     338                 :             :                     /* XXX should be locale aware */
     339                 :           0 :                 case 'B':
     340                 :           0 :                     replace_val.str_val = pgtypes_date_months[tm->tm_mon - 1];
     341                 :           0 :                     replace_type = PGTYPES_TYPE_STRING_CONSTANT;
     342                 :           0 :                     break;
     343                 :             : 
     344                 :             :                     /*
     345                 :             :                      * The preferred date and time representation for the
     346                 :             :                      * current locale.
     347                 :             :                      */
     348                 :           0 :                 case 'c':
     349                 :             :                     /* XXX */
     350                 :           0 :                     break;
     351                 :             :                     /* the century number with leading zeroes */
     352                 :           0 :                 case 'C':
     353                 :           0 :                     replace_val.uint_val = tm->tm_year / 100;
     354                 :           0 :                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
     355                 :           0 :                     break;
     356                 :             :                     /* day with leading zeroes (01 - 31) */
     357                 :           4 :                 case 'd':
     358                 :           4 :                     replace_val.uint_val = tm->tm_mday;
     359                 :           4 :                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
     360                 :           4 :                     break;
     361                 :             :                     /* the date in the format mm/dd/yy */
     362                 :           0 :                 case 'D':
     363                 :             : 
     364                 :             :                     /*
     365                 :             :                      * ts, dDate, dow, tm is information about the timestamp
     366                 :             :                      *
     367                 :             :                      * q is the start of the current output buffer
     368                 :             :                      *
     369                 :             :                      * pstr_len is a pointer to the remaining size of output,
     370                 :             :                      * i.e. the size of q
     371                 :             :                      */
     372                 :           0 :                     i = dttofmtasc_replace(ts, dDate, dow, tm,
     373                 :             :                                            q, pstr_len,
     374                 :             :                                            "%m/%d/%y");
     375         [ #  # ]:           0 :                     if (i)
     376                 :           0 :                         return i;
     377                 :           0 :                     break;
     378                 :             :                     /* day with leading spaces (01 - 31) */
     379                 :           0 :                 case 'e':
     380                 :           0 :                     replace_val.uint_val = tm->tm_mday;
     381                 :           0 :                     replace_type = PGTYPES_TYPE_UINT_2_LS;
     382                 :           0 :                     break;
     383                 :             : 
     384                 :             :                     /*
     385                 :             :                      * alternative format modifier
     386                 :             :                      */
     387                 :           0 :                 case 'E':
     388                 :             :                     {
     389                 :           0 :                         char        tmp[4] = "%Ex";
     390                 :             : 
     391                 :           0 :                         p++;
     392         [ #  # ]:           0 :                         if (*p == '\0')
     393                 :           0 :                             return -1;
     394                 :           0 :                         tmp[2] = *p;
     395                 :             : 
     396                 :             :                         /*
     397                 :             :                          * strftime's month is 0 based, ours is 1 based
     398                 :             :                          */
     399                 :           0 :                         tm->tm_mon -= 1;
     400                 :           0 :                         i = strftime(q, *pstr_len, tmp, tm);
     401         [ #  # ]:           0 :                         if (i == 0)
     402                 :           0 :                             return -1;
     403         [ #  # ]:           0 :                         while (*q)
     404                 :             :                         {
     405                 :           0 :                             q++;
     406                 :           0 :                             (*pstr_len)--;
     407                 :             :                         }
     408                 :           0 :                         tm->tm_mon += 1;
     409                 :           0 :                         replace_type = PGTYPES_TYPE_NOTHING;
     410                 :           0 :                         break;
     411                 :             :                     }
     412                 :             : 
     413                 :             :                     /*
     414                 :             :                      * The ISO 8601 year with century as a decimal number. The
     415                 :             :                      * 4-digit year corresponding to the ISO week number.
     416                 :             :                      */
     417                 :           0 :                 case 'G':
     418                 :             :                     {
     419                 :             :                         /* Keep compiler quiet - Don't use a literal format */
     420                 :           0 :                         const char *fmt = "%G";
     421                 :             : 
     422                 :           0 :                         tm->tm_mon -= 1;
     423                 :           0 :                         i = strftime(q, *pstr_len, fmt, tm);
     424         [ #  # ]:           0 :                         if (i == 0)
     425                 :           0 :                             return -1;
     426         [ #  # ]:           0 :                         while (*q)
     427                 :             :                         {
     428                 :           0 :                             q++;
     429                 :           0 :                             (*pstr_len)--;
     430                 :             :                         }
     431                 :           0 :                         tm->tm_mon += 1;
     432                 :           0 :                         replace_type = PGTYPES_TYPE_NOTHING;
     433                 :             :                     }
     434                 :           0 :                     break;
     435                 :             : 
     436                 :             :                     /*
     437                 :             :                      * Like %G, but without century, i.e., with a 2-digit year
     438                 :             :                      * (00-99).
     439                 :             :                      */
     440                 :           0 :                 case 'g':
     441                 :             :                     {
     442                 :           0 :                         const char *fmt = "%g"; /* Keep compiler quiet about
     443                 :             :                                                  * 2-digit year */
     444                 :             : 
     445                 :           0 :                         tm->tm_mon -= 1;
     446                 :           0 :                         i = strftime(q, *pstr_len, fmt, tm);
     447         [ #  # ]:           0 :                         if (i == 0)
     448                 :           0 :                             return -1;
     449         [ #  # ]:           0 :                         while (*q)
     450                 :             :                         {
     451                 :           0 :                             q++;
     452                 :           0 :                             (*pstr_len)--;
     453                 :             :                         }
     454                 :           0 :                         tm->tm_mon += 1;
     455                 :           0 :                         replace_type = PGTYPES_TYPE_NOTHING;
     456                 :             :                     }
     457                 :           0 :                     break;
     458                 :             :                     /* hour (24 hour clock) with leading zeroes */
     459                 :           4 :                 case 'H':
     460                 :           4 :                     replace_val.uint_val = tm->tm_hour;
     461                 :           4 :                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
     462                 :           4 :                     break;
     463                 :             :                     /* hour (12 hour clock) with leading zeroes */
     464                 :           0 :                 case 'I':
     465                 :           0 :                     replace_val.uint_val = tm->tm_hour % 12;
     466                 :           0 :                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
     467                 :           0 :                     break;
     468                 :             : 
     469                 :             :                     /*
     470                 :             :                      * The day of the year as a decimal number with leading
     471                 :             :                      * zeroes. It ranges from 001 to 366.
     472                 :             :                      */
     473                 :           2 :                 case 'j':
     474                 :           2 :                     replace_val.uint_val = tm->tm_yday;
     475                 :           2 :                     replace_type = PGTYPES_TYPE_UINT_3_LZ;
     476                 :           2 :                     break;
     477                 :             : 
     478                 :             :                     /*
     479                 :             :                      * The hour (24 hour clock). Leading zeroes will be turned
     480                 :             :                      * into spaces.
     481                 :             :                      */
     482                 :           0 :                 case 'k':
     483                 :           0 :                     replace_val.uint_val = tm->tm_hour;
     484                 :           0 :                     replace_type = PGTYPES_TYPE_UINT_2_LS;
     485                 :           0 :                     break;
     486                 :             : 
     487                 :             :                     /*
     488                 :             :                      * The hour (12 hour clock). Leading zeroes will be turned
     489                 :             :                      * into spaces.
     490                 :             :                      */
     491                 :           0 :                 case 'l':
     492                 :           0 :                     replace_val.uint_val = tm->tm_hour % 12;
     493                 :           0 :                     replace_type = PGTYPES_TYPE_UINT_2_LS;
     494                 :           0 :                     break;
     495                 :             :                     /* The month as a decimal number with a leading zero */
     496                 :           0 :                 case 'm':
     497                 :           0 :                     replace_val.uint_val = tm->tm_mon;
     498                 :           0 :                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
     499                 :           0 :                     break;
     500                 :             :                     /* The minute as a decimal number with a leading zero */
     501                 :           4 :                 case 'M':
     502                 :           4 :                     replace_val.uint_val = tm->tm_min;
     503                 :           4 :                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
     504                 :           4 :                     break;
     505                 :             :                     /* A newline character */
     506                 :           0 :                 case 'n':
     507                 :           0 :                     replace_val.char_val = '\n';
     508                 :           0 :                     replace_type = PGTYPES_TYPE_CHAR;
     509                 :           0 :                     break;
     510                 :             :                     /* the AM/PM specifier (uppercase) */
     511                 :             :                     /* XXX should be locale aware */
     512                 :           0 :                 case 'p':
     513         [ #  # ]:           0 :                     if (tm->tm_hour < 12)
     514                 :           0 :                         replace_val.str_val = "AM";
     515                 :             :                     else
     516                 :           0 :                         replace_val.str_val = "PM";
     517                 :           0 :                     replace_type = PGTYPES_TYPE_STRING_CONSTANT;
     518                 :           0 :                     break;
     519                 :             :                     /* the AM/PM specifier (lowercase) */
     520                 :             :                     /* XXX should be locale aware */
     521                 :           0 :                 case 'P':
     522         [ #  # ]:           0 :                     if (tm->tm_hour < 12)
     523                 :           0 :                         replace_val.str_val = "am";
     524                 :             :                     else
     525                 :           0 :                         replace_val.str_val = "pm";
     526                 :           0 :                     replace_type = PGTYPES_TYPE_STRING_CONSTANT;
     527                 :           0 :                     break;
     528                 :             :                     /* the time in the format %I:%M:%S %p */
     529                 :             :                     /* XXX should be locale aware */
     530                 :           0 :                 case 'r':
     531                 :           0 :                     i = dttofmtasc_replace(ts, dDate, dow, tm,
     532                 :             :                                            q, pstr_len,
     533                 :             :                                            "%I:%M:%S %p");
     534         [ #  # ]:           0 :                     if (i)
     535                 :           0 :                         return i;
     536                 :           0 :                     break;
     537                 :             :                     /* The time in 24 hour notation (%H:%M) */
     538                 :           0 :                 case 'R':
     539                 :           0 :                     i = dttofmtasc_replace(ts, dDate, dow, tm,
     540                 :             :                                            q, pstr_len,
     541                 :             :                                            "%H:%M");
     542         [ #  # ]:           0 :                     if (i)
     543                 :           0 :                         return i;
     544                 :           0 :                     break;
     545                 :             :                     /* The number of seconds since the Epoch (1970-01-01) */
     546                 :           0 :                 case 's':
     547                 :           0 :                     replace_val.int64_val = (*ts - SetEpochTimestamp()) / 1000000.0;
     548                 :           0 :                     replace_type = PGTYPES_TYPE_INT64;
     549                 :           0 :                     break;
     550                 :             :                     /* seconds as a decimal number with leading zeroes */
     551                 :           4 :                 case 'S':
     552                 :           4 :                     replace_val.uint_val = tm->tm_sec;
     553                 :           4 :                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
     554                 :           4 :                     break;
     555                 :             :                     /* A tabulator */
     556                 :           0 :                 case 't':
     557                 :           0 :                     replace_val.char_val = '\t';
     558                 :           0 :                     replace_type = PGTYPES_TYPE_CHAR;
     559                 :           0 :                     break;
     560                 :             :                     /* The time in 24 hour notation (%H:%M:%S) */
     561                 :           0 :                 case 'T':
     562                 :           0 :                     i = dttofmtasc_replace(ts, dDate, dow, tm,
     563                 :             :                                            q, pstr_len,
     564                 :             :                                            "%H:%M:%S");
     565         [ #  # ]:           0 :                     if (i)
     566                 :           0 :                         return i;
     567                 :           0 :                     break;
     568                 :             : 
     569                 :             :                     /*
     570                 :             :                      * The day of the week as a decimal, Monday = 1, Sunday =
     571                 :             :                      * 7
     572                 :             :                      */
     573                 :           0 :                 case 'u':
     574                 :           0 :                     replace_val.uint_val = dow;
     575         [ #  # ]:           0 :                     if (replace_val.uint_val == 0)
     576                 :           0 :                         replace_val.uint_val = 7;
     577                 :           0 :                     replace_type = PGTYPES_TYPE_UINT;
     578                 :           0 :                     break;
     579                 :             :                     /* The week number of the year as a decimal number */
     580                 :           0 :                 case 'U':
     581                 :           0 :                     tm->tm_mon -= 1;
     582                 :           0 :                     i = strftime(q, *pstr_len, "%U", tm);
     583         [ #  # ]:           0 :                     if (i == 0)
     584                 :           0 :                         return -1;
     585         [ #  # ]:           0 :                     while (*q)
     586                 :             :                     {
     587                 :           0 :                         q++;
     588                 :           0 :                         (*pstr_len)--;
     589                 :             :                     }
     590                 :           0 :                     tm->tm_mon += 1;
     591                 :           0 :                     replace_type = PGTYPES_TYPE_NOTHING;
     592                 :           0 :                     break;
     593                 :             : 
     594                 :             :                     /*
     595                 :             :                      * The ISO 8601:1988 week number of the current year as a
     596                 :             :                      * decimal number.
     597                 :             :                      */
     598                 :           0 :                 case 'V':
     599                 :             :                     {
     600                 :             :                         /* Keep compiler quiet - Don't use a literal format */
     601                 :           0 :                         const char *fmt = "%V";
     602                 :             : 
     603                 :           0 :                         i = strftime(q, *pstr_len, fmt, tm);
     604         [ #  # ]:           0 :                         if (i == 0)
     605                 :           0 :                             return -1;
     606         [ #  # ]:           0 :                         while (*q)
     607                 :             :                         {
     608                 :           0 :                             q++;
     609                 :           0 :                             (*pstr_len)--;
     610                 :             :                         }
     611                 :           0 :                         replace_type = PGTYPES_TYPE_NOTHING;
     612                 :             :                     }
     613                 :           0 :                     break;
     614                 :             : 
     615                 :             :                     /*
     616                 :             :                      * The day of the week as a decimal, Sunday being 0 and
     617                 :             :                      * Monday 1.
     618                 :             :                      */
     619                 :           0 :                 case 'w':
     620                 :           0 :                     replace_val.uint_val = dow;
     621                 :           0 :                     replace_type = PGTYPES_TYPE_UINT;
     622                 :           0 :                     break;
     623                 :             :                     /* The week number of the year (another definition) */
     624                 :           0 :                 case 'W':
     625                 :           0 :                     tm->tm_mon -= 1;
     626                 :           0 :                     i = strftime(q, *pstr_len, "%U", tm);
     627         [ #  # ]:           0 :                     if (i == 0)
     628                 :           0 :                         return -1;
     629         [ #  # ]:           0 :                     while (*q)
     630                 :             :                     {
     631                 :           0 :                         q++;
     632                 :           0 :                         (*pstr_len)--;
     633                 :             :                     }
     634                 :           0 :                     tm->tm_mon += 1;
     635                 :           0 :                     replace_type = PGTYPES_TYPE_NOTHING;
     636                 :           0 :                     break;
     637                 :             : 
     638                 :             :                     /*
     639                 :             :                      * The preferred date representation for the current
     640                 :             :                      * locale without the time.
     641                 :             :                      */
     642                 :           2 :                 case 'x':
     643                 :             :                     {
     644                 :           2 :                         const char *fmt = "%x"; /* Keep compiler quiet about
     645                 :             :                                                  * 2-digit year */
     646                 :             : 
     647                 :           2 :                         tm->tm_mon -= 1;
     648                 :           2 :                         i = strftime(q, *pstr_len, fmt, tm);
     649         [ -  + ]:           2 :                         if (i == 0)
     650                 :           0 :                             return -1;
     651         [ +  + ]:          18 :                         while (*q)
     652                 :             :                         {
     653                 :          16 :                             q++;
     654                 :          16 :                             (*pstr_len)--;
     655                 :             :                         }
     656                 :           2 :                         tm->tm_mon += 1;
     657                 :           2 :                         replace_type = PGTYPES_TYPE_NOTHING;
     658                 :             :                     }
     659                 :           2 :                     break;
     660                 :             : 
     661                 :             :                     /*
     662                 :             :                      * The preferred time representation for the current
     663                 :             :                      * locale without the date.
     664                 :             :                      */
     665                 :           2 :                 case 'X':
     666                 :           2 :                     tm->tm_mon -= 1;
     667                 :           2 :                     i = strftime(q, *pstr_len, "%X", tm);
     668         [ -  + ]:           2 :                     if (i == 0)
     669                 :           0 :                         return -1;
     670         [ +  + ]:          18 :                     while (*q)
     671                 :             :                     {
     672                 :          16 :                         q++;
     673                 :          16 :                         (*pstr_len)--;
     674                 :             :                     }
     675                 :           2 :                     tm->tm_mon += 1;
     676                 :           2 :                     replace_type = PGTYPES_TYPE_NOTHING;
     677                 :           2 :                     break;
     678                 :             :                     /* The year without the century (2 digits, leading zeroes) */
     679                 :           0 :                 case 'y':
     680                 :           0 :                     replace_val.uint_val = tm->tm_year % 100;
     681                 :           0 :                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
     682                 :           0 :                     break;
     683                 :             :                     /* The year with the century (4 digits) */
     684                 :           6 :                 case 'Y':
     685                 :           6 :                     replace_val.uint_val = tm->tm_year;
     686                 :           6 :                     replace_type = PGTYPES_TYPE_UINT;
     687                 :           6 :                     break;
     688                 :             :                     /* The time zone offset from GMT */
     689                 :           0 :                 case 'z':
     690                 :           0 :                     tm->tm_mon -= 1;
     691                 :           0 :                     i = strftime(q, *pstr_len, "%z", tm);
     692         [ #  # ]:           0 :                     if (i == 0)
     693                 :           0 :                         return -1;
     694         [ #  # ]:           0 :                     while (*q)
     695                 :             :                     {
     696                 :           0 :                         q++;
     697                 :           0 :                         (*pstr_len)--;
     698                 :             :                     }
     699                 :           0 :                     tm->tm_mon += 1;
     700                 :           0 :                     replace_type = PGTYPES_TYPE_NOTHING;
     701                 :           0 :                     break;
     702                 :             :                     /* The name or abbreviation of the time zone */
     703                 :           0 :                 case 'Z':
     704                 :           0 :                     tm->tm_mon -= 1;
     705                 :           0 :                     i = strftime(q, *pstr_len, "%Z", tm);
     706         [ #  # ]:           0 :                     if (i == 0)
     707                 :           0 :                         return -1;
     708         [ #  # ]:           0 :                     while (*q)
     709                 :             :                     {
     710                 :           0 :                         q++;
     711                 :           0 :                         (*pstr_len)--;
     712                 :             :                     }
     713                 :           0 :                     tm->tm_mon += 1;
     714                 :           0 :                     replace_type = PGTYPES_TYPE_NOTHING;
     715                 :           0 :                     break;
     716                 :             :                     /* A % sign */
     717                 :           2 :                 case '%':
     718                 :           2 :                     replace_val.char_val = '%';
     719                 :           2 :                     replace_type = PGTYPES_TYPE_CHAR;
     720                 :           2 :                     break;
     721                 :           0 :                 case '\0':
     722                 :             :                     /* fmtstr: foo%' - The string ends with a % sign */
     723                 :             : 
     724                 :             :                     /*
     725                 :             :                      * this is not compliant to the specification
     726                 :             :                      */
     727                 :           0 :                     return -1;
     728                 :           0 :                 default:
     729                 :             : 
     730                 :             :                     /*
     731                 :             :                      * if we don't know the pattern, we just copy it
     732                 :             :                      */
     733         [ #  # ]:           0 :                     if (*pstr_len > 1)
     734                 :             :                     {
     735                 :           0 :                         *q = '%';
     736                 :           0 :                         q++;
     737                 :           0 :                         (*pstr_len)--;
     738         [ #  # ]:           0 :                         if (*pstr_len > 1)
     739                 :             :                         {
     740                 :           0 :                             *q = *p;
     741                 :           0 :                             q++;
     742                 :           0 :                             (*pstr_len)--;
     743                 :             :                         }
     744                 :             :                         else
     745                 :             :                         {
     746                 :           0 :                             *q = '\0';
     747                 :           0 :                             return -1;
     748                 :             :                         }
     749                 :           0 :                         *q = '\0';
     750                 :             :                     }
     751                 :             :                     else
     752                 :           0 :                         return -1;
     753                 :           0 :                     break;
     754                 :             :             }
     755                 :          38 :             i = pgtypes_fmt_replace(replace_val, replace_type, &q, pstr_len);
     756         [ -  + ]:          38 :             if (i)
     757                 :           0 :                 return i;
     758                 :             :         }
     759                 :             :         else
     760                 :             :         {
     761         [ +  - ]:         100 :             if (*pstr_len > 1)
     762                 :             :             {
     763                 :         100 :                 *q = *p;
     764                 :         100 :                 (*pstr_len)--;
     765                 :         100 :                 q++;
     766                 :         100 :                 *q = '\0';
     767                 :             :             }
     768                 :             :             else
     769                 :           0 :                 return -1;
     770                 :             :         }
     771                 :         138 :         p++;
     772                 :             :     }
     773                 :           8 :     return 0;
     774                 :             : }
     775                 :             : 
     776                 :             : 
     777                 :             : int
     778                 :           8 : PGTYPEStimestamp_fmt_asc(timestamp * ts, char *output, int str_len, const char *fmtstr)
     779                 :             : {
     780                 :             :     struct tm   tm;
     781                 :             :     fsec_t      fsec;
     782                 :             :     date        dDate;
     783                 :             :     int         dow;
     784                 :             : 
     785                 :           8 :     dDate = PGTYPESdate_from_timestamp(*ts);
     786                 :           8 :     dow = PGTYPESdate_dayofweek(dDate);
     787                 :           8 :     timestamp2tm(*ts, NULL, &tm, &fsec, NULL);
     788                 :             : 
     789                 :           8 :     return dttofmtasc_replace(ts, dDate, dow, &tm, output, &str_len, fmtstr);
     790                 :             : }
     791                 :             : 
     792                 :             : int
     793                 :           0 : PGTYPEStimestamp_sub(timestamp * ts1, timestamp * ts2, interval * iv)
     794                 :             : {
     795   [ #  #  #  #  :           0 :     if (TIMESTAMP_NOT_FINITE(*ts1) || TIMESTAMP_NOT_FINITE(*ts2))
             #  #  #  # ]
     796                 :           0 :         return PGTYPES_TS_ERR_EINFTIME;
     797                 :             :     else
     798                 :           0 :         iv->time = (*ts1 - *ts2);
     799                 :             : 
     800                 :           0 :     iv->month = 0;
     801                 :             : 
     802                 :           0 :     return 0;
     803                 :             : }
     804                 :             : 
     805                 :             : int
     806                 :          50 : PGTYPEStimestamp_defmt_asc(const char *str, const char *fmt, timestamp * d)
     807                 :             : {
     808                 :             :     int         year,
     809                 :             :                 month,
     810                 :             :                 day;
     811                 :             :     int         hour,
     812                 :             :                 minute,
     813                 :             :                 second;
     814                 :             :     int         tz;
     815                 :             : 
     816                 :             :     int         i;
     817                 :             :     char       *mstr;
     818                 :             :     char       *mfmt;
     819                 :             : 
     820         [ +  + ]:          50 :     if (!fmt)
     821                 :           2 :         fmt = "%Y-%m-%d %H:%M:%S";
     822         [ +  + ]:          50 :     if (!fmt[0])
     823                 :           2 :         return 1;
     824                 :             : 
     825                 :          48 :     mstr = pgtypes_strdup(str);
     826                 :          48 :     mfmt = pgtypes_strdup(fmt);
     827                 :             : 
     828                 :             :     /*
     829                 :             :      * initialize with impossible values so that we can see if the fields
     830                 :             :      * where specified at all
     831                 :             :      */
     832                 :             :     /* XXX ambiguity with 1 BC for year? */
     833                 :          48 :     year = -1;
     834                 :          48 :     month = -1;
     835                 :          48 :     day = -1;
     836                 :          48 :     hour = 0;
     837                 :          48 :     minute = -1;
     838                 :          48 :     second = -1;
     839                 :          48 :     tz = 0;
     840                 :             : 
     841                 :          48 :     i = PGTYPEStimestamp_defmt_scan(&mstr, mfmt, d, &year, &month, &day, &hour, &minute, &second, &tz);
     842                 :          48 :     free(mstr);
     843                 :          48 :     free(mfmt);
     844                 :          48 :     return i;
     845                 :             : }
     846                 :             : 
     847                 :             : /*
     848                 :             :  * add an interval to a time stamp
     849                 :             :  *
     850                 :             :  *  *tout = tin + span
     851                 :             :  *
     852                 :             :  *   returns 0 if successful
     853                 :             :  *   returns -1 if it fails
     854                 :             :  *
     855                 :             :  */
     856                 :             : 
     857                 :             : int
     858                 :          14 : PGTYPEStimestamp_add_interval(timestamp * tin, interval * span, timestamp * tout)
     859                 :             : {
     860   [ +  -  -  + ]:          14 :     if (TIMESTAMP_NOT_FINITE(*tin))
     861                 :           0 :         *tout = *tin;
     862                 :             :     else
     863                 :             :     {
     864         [ +  + ]:          14 :         if (span->month != 0)
     865                 :             :         {
     866                 :             :             struct tm   tt,
     867                 :           2 :                        *tm = &tt;
     868                 :             :             fsec_t      fsec;
     869                 :             : 
     870         [ -  + ]:           2 :             if (timestamp2tm(*tin, NULL, tm, &fsec, NULL) != 0)
     871                 :           0 :                 return -1;
     872                 :           2 :             tm->tm_mon += span->month;
     873         [ +  - ]:           2 :             if (tm->tm_mon > MONTHS_PER_YEAR)
     874                 :             :             {
     875                 :           2 :                 tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
     876                 :           2 :                 tm->tm_mon = (tm->tm_mon - 1) % MONTHS_PER_YEAR + 1;
     877                 :             :             }
     878         [ #  # ]:           0 :             else if (tm->tm_mon < 1)
     879                 :             :             {
     880                 :           0 :                 tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1;
     881                 :           0 :                 tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR;
     882                 :             :             }
     883                 :             : 
     884                 :             : 
     885                 :             :             /* adjust for end of month boundary problems... */
     886   [ -  +  -  -  :           2 :             if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
             -  -  -  + ]
     887   [ #  #  #  #  :           0 :                 tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]);
                   #  # ]
     888                 :             : 
     889                 :             : 
     890         [ -  + ]:           2 :             if (tm2timestamp(tm, fsec, NULL, tin) != 0)
     891                 :           0 :                 return -1;
     892                 :             :         }
     893                 :             : 
     894                 :          14 :         *tin += span->time;
     895                 :          14 :         *tout = *tin;
     896                 :             :     }
     897                 :             : 
     898                 :          14 :     return 0;
     899                 :             : }
     900                 :             : 
     901                 :             : 
     902                 :             : /*
     903                 :             :  * subtract an interval from a time stamp
     904                 :             :  *
     905                 :             :  *  *tout = tin - span
     906                 :             :  *
     907                 :             :  *   returns 0 if successful
     908                 :             :  *   returns -1 if it fails
     909                 :             :  *
     910                 :             :  */
     911                 :             : 
     912                 :             : int
     913                 :           0 : PGTYPEStimestamp_sub_interval(timestamp * tin, interval * span, timestamp * tout)
     914                 :             : {
     915                 :             :     interval    tspan;
     916                 :             : 
     917                 :           0 :     tspan.month = -span->month;
     918                 :           0 :     tspan.time = -span->time;
     919                 :             : 
     920                 :           0 :     return PGTYPEStimestamp_add_interval(tin, &tspan, tout);
     921                 :             : }
        

Generated by: LCOV version 2.0-1