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

Generated by: LCOV version 1.13