LCOV - code coverage report
Current view: top level - src/interfaces/ecpg/pgtypeslib - timestamp.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 37.4 % 449 168
Test Date: 2026-03-23 22:16:10 Functions: 73.3 % 15 11
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.0-1