LCOV - differential code coverage report
Current view: top level - src/interfaces/ecpg/pgtypeslib - timestamp.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage HEAD vs 15 Lines: 37.8 % 452 171 281 171
Current Date: 2023-04-08 15:15:32 Functions: 73.3 % 15 11 4 11
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a