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

Generated by: LCOV version 2.0-1