LCOV - code coverage report
Current view: top level - src/interfaces/ecpg/pgtypeslib - timestamp.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 171 452 37.8 %
Date: 2024-04-25 10:13:14 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         588 : time2t(const int hour, const int min, const int sec, const fsec_t fsec)
      21             : {
      22         588 :     return (((((hour * MINS_PER_HOUR) + min) * SECS_PER_MINUTE) + sec) * USECS_PER_SEC) + fsec;
      23             : }                               /* time2t() */
      24             : 
      25             : static timestamp
      26          84 : dt2local(timestamp dt, int tz)
      27             : {
      28          84 :     dt -= (tz * USECS_PER_SEC);
      29          84 :     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         588 : 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         588 :     if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
      47           0 :         return -1;
      48             : 
      49         588 :     dDate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
      50         588 :     time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
      51         588 :     *result = (dDate * USECS_PER_DAY) + time;
      52             :     /* check for major overflow */
      53         588 :     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         588 :     if ((*result < 0 && dDate > 0) ||
      58         588 :         (*result > 0 && dDate < -1))
      59           0 :         return -1;
      60         588 :     if (tzp != NULL)
      61          84 :         *result = dt2local(*result, -(*tzp));
      62             : 
      63             :     /* final range check catches just-out-of-range timestamps */
      64         588 :     if (!IS_VALID_TIMESTAMP(*result))
      65           0 :         return -1;
      66             : 
      67         588 :     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         652 : 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         652 :     date0 = date2j(2000, 1, 1);
     108             : 
     109         652 :     time = dt;
     110         652 :     TMODULO(time, dDate, USECS_PER_DAY);
     111             : 
     112         652 :     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         652 :     dDate += date0;
     120             : 
     121             :     /* Julian day routine does not work for negative Julian days */
     122         652 :     if (dDate < 0 || dDate > (timestamp) INT_MAX)
     123           0 :         return -1;
     124             : 
     125         652 :     j2date((int) dDate, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
     126         652 :     dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
     127             : 
     128         652 :     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 ||
     162             :                                  * HAVE_INT_TIMEZONE) */
     163             :             *tzp = 0;
     164             :             /* Mark this as *no* time zone available */
     165             :             tm->tm_isdst = -1;
     166             :             if (tzn != NULL)
     167             :                 *tzn = NULL;
     168             : #endif
     169             :         }
     170             :         else
     171             :         {
     172           0 :             *tzp = 0;
     173             :             /* Mark this as *no* time zone available */
     174           0 :             tm->tm_isdst = -1;
     175           0 :             if (tzn != NULL)
     176           0 :                 *tzn = NULL;
     177             :         }
     178             :     }
     179             :     else
     180             :     {
     181         652 :         tm->tm_isdst = -1;
     182         652 :         if (tzn != NULL)
     183           0 :             *tzn = NULL;
     184             :     }
     185             : 
     186         652 :     tm->tm_yday = dDate - date2j(tm->tm_year, 1, 1) + 1;
     187             : 
     188         652 :     return 0;
     189             : }                               /* timestamp2tm() */
     190             : 
     191             : /* EncodeSpecialTimestamp()
     192             :  *  * Convert reserved timestamp data type to string.
     193             :  *   */
     194             : static void
     195           0 : EncodeSpecialTimestamp(timestamp dt, char *str)
     196             : {
     197           0 :     if (TIMESTAMP_IS_NOBEGIN(dt))
     198           0 :         strcpy(str, EARLY);
     199           0 :     else if (TIMESTAMP_IS_NOEND(dt))
     200           0 :         strcpy(str, LATE);
     201             :     else
     202           0 :         abort();                /* shouldn't happen */
     203           0 : }
     204             : 
     205             : timestamp
     206         500 : PGTYPEStimestamp_from_asc(char *str, char **endptr)
     207             : {
     208             :     timestamp   result;
     209         500 :     int64       noresult = 0;
     210             :     fsec_t      fsec;
     211             :     struct tm   tt,
     212         500 :                *tm = &tt;
     213             :     int         dtype;
     214             :     int         nf;
     215             :     char       *field[MAXDATEFIELDS];
     216             :     int         ftype[MAXDATEFIELDS];
     217             :     char        lowstr[MAXDATELEN + MAXDATEFIELDS];
     218             :     char       *realptr;
     219         500 :     char      **ptr = (endptr != NULL) ? endptr : &realptr;
     220             : 
     221         500 :     if (strlen(str) > MAXDATELEN)
     222             :     {
     223           0 :         errno = PGTYPES_TS_BAD_TIMESTAMP;
     224           0 :         return noresult;
     225             :     }
     226             : 
     227        1000 :     if (ParseDateTime(str, lowstr, field, ftype, &nf, ptr) != 0 ||
     228         500 :         DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, 0) != 0)
     229             :     {
     230           4 :         errno = PGTYPES_TS_BAD_TIMESTAMP;
     231           4 :         return noresult;
     232             :     }
     233             : 
     234         496 :     switch (dtype)
     235             :     {
     236         496 :         case DTK_DATE:
     237         496 :             if (tm2timestamp(tm, fsec, NULL, &result) != 0)
     238             :             {
     239           0 :                 errno = PGTYPES_TS_BAD_TIMESTAMP;
     240           0 :                 return noresult;
     241             :             }
     242         496 :             break;
     243             : 
     244           0 :         case DTK_EPOCH:
     245           0 :             result = SetEpochTimestamp();
     246           0 :             break;
     247             : 
     248           0 :         case DTK_LATE:
     249           0 :             TIMESTAMP_NOEND(result);
     250           0 :             break;
     251             : 
     252           0 :         case DTK_EARLY:
     253           0 :             TIMESTAMP_NOBEGIN(result);
     254           0 :             break;
     255             : 
     256           0 :         default:
     257           0 :             errno = PGTYPES_TS_BAD_TIMESTAMP;
     258           0 :             return noresult;
     259             :     }
     260             : 
     261             :     /* AdjustTimestampForTypmod(&result, typmod); */
     262             : 
     263             :     /*
     264             :      * Since it's difficult to test for noresult, make sure errno is 0 if no
     265             :      * error occurred.
     266             :      */
     267         496 :     errno = 0;
     268         496 :     return result;
     269             : }
     270             : 
     271             : char *
     272         632 : PGTYPEStimestamp_to_asc(timestamp tstamp)
     273             : {
     274             :     struct tm   tt,
     275         632 :                *tm = &tt;
     276             :     char        buf[MAXDATELEN + 1];
     277             :     fsec_t      fsec;
     278         632 :     int         DateStyle = 1;  /* this defaults to USE_ISO_DATES, shall we
     279             :                                  * make it an option? */
     280             : 
     281         632 :     if (TIMESTAMP_NOT_FINITE(tstamp))
     282           0 :         EncodeSpecialTimestamp(tstamp, buf);
     283         632 :     else if (timestamp2tm(tstamp, NULL, tm, &fsec, NULL) == 0)
     284         632 :         EncodeDateTime(tm, fsec, false, 0, NULL, DateStyle, buf, 0);
     285             :     else
     286             :     {
     287           0 :         errno = PGTYPES_TS_BAD_TIMESTAMP;
     288           0 :         return NULL;
     289             :     }
     290         632 :     return pgtypes_strdup(buf);
     291             : }
     292             : 
     293             : void
     294           4 : PGTYPEStimestamp_current(timestamp * ts)
     295             : {
     296             :     struct tm   tm;
     297             : 
     298           4 :     GetCurrentDateTime(&tm);
     299           4 :     if (errno == 0)
     300           4 :         tm2timestamp(&tm, 0, NULL, ts);
     301           4 : }
     302             : 
     303             : static int
     304          16 : 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          16 :     const char *p = fmtstr;
     311          16 :     char       *q = output;
     312             : 
     313         292 :     while (*p)
     314             :     {
     315         276 :         if (*p == '%')
     316             :         {
     317          76 :             p++;
     318             :             /* fix compiler warning */
     319          76 :             replace_type = PGTYPES_TYPE_NOTHING;
     320          76 :             switch (*p)
     321             :             {
     322             :                     /* the abbreviated name of the day in the week */
     323             :                     /* XXX should be locale aware */
     324           8 :                 case 'a':
     325           8 :                     replace_val.str_val = pgtypes_date_weekdays_short[dow];
     326           8 :                     replace_type = PGTYPES_TYPE_STRING_CONSTANT;
     327           8 :                     break;
     328             :                     /* the full name of the day in the week */
     329             :                     /* XXX should be locale aware */
     330           0 :                 case 'A':
     331           0 :                     replace_val.str_val = days[dow];
     332           0 :                     replace_type = PGTYPES_TYPE_STRING_CONSTANT;
     333           0 :                     break;
     334             :                     /* the abbreviated name of the month */
     335             :                     /* XXX should be locale aware */
     336           8 :                 case 'b':
     337             :                 case 'h':
     338           8 :                     replace_val.str_val = months[tm->tm_mon - 1];
     339           8 :                     replace_type = PGTYPES_TYPE_STRING_CONSTANT;
     340           8 :                     break;
     341             :                     /* the full name of the month */
     342             :                     /* XXX should be locale aware */
     343           0 :                 case 'B':
     344           0 :                     replace_val.str_val = pgtypes_date_months[tm->tm_mon - 1];
     345           0 :                     replace_type = PGTYPES_TYPE_STRING_CONSTANT;
     346           0 :                     break;
     347             : 
     348             :                     /*
     349             :                      * The preferred date and time representation for the
     350             :                      * current locale.
     351             :                      */
     352           0 :                 case 'c':
     353             :                     /* XXX */
     354           0 :                     break;
     355             :                     /* the century number with leading zeroes */
     356           0 :                 case 'C':
     357           0 :                     replace_val.uint_val = tm->tm_year / 100;
     358           0 :                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
     359           0 :                     break;
     360             :                     /* day with leading zeroes (01 - 31) */
     361           8 :                 case 'd':
     362           8 :                     replace_val.uint_val = tm->tm_mday;
     363           8 :                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
     364           8 :                     break;
     365             :                     /* the date in the format mm/dd/yy */
     366           0 :                 case 'D':
     367             : 
     368             :                     /*
     369             :                      * ts, dDate, dow, tm is information about the timestamp
     370             :                      *
     371             :                      * q is the start of the current output buffer
     372             :                      *
     373             :                      * pstr_len is a pointer to the remaining size of output,
     374             :                      * i.e. the size of q
     375             :                      */
     376           0 :                     i = dttofmtasc_replace(ts, dDate, dow, tm,
     377             :                                            q, pstr_len,
     378             :                                            "%m/%d/%y");
     379           0 :                     if (i)
     380           0 :                         return i;
     381           0 :                     break;
     382             :                     /* day with leading spaces (01 - 31) */
     383           0 :                 case 'e':
     384           0 :                     replace_val.uint_val = tm->tm_mday;
     385           0 :                     replace_type = PGTYPES_TYPE_UINT_2_LS;
     386           0 :                     break;
     387             : 
     388             :                     /*
     389             :                      * alternative format modifier
     390             :                      */
     391           0 :                 case 'E':
     392             :                     {
     393           0 :                         char        tmp[4] = "%Ex";
     394             : 
     395           0 :                         p++;
     396           0 :                         if (*p == '\0')
     397           0 :                             return -1;
     398           0 :                         tmp[2] = *p;
     399             : 
     400             :                         /*
     401             :                          * strftime's month is 0 based, ours is 1 based
     402             :                          */
     403           0 :                         tm->tm_mon -= 1;
     404           0 :                         i = strftime(q, *pstr_len, tmp, tm);
     405           0 :                         if (i == 0)
     406           0 :                             return -1;
     407           0 :                         while (*q)
     408             :                         {
     409           0 :                             q++;
     410           0 :                             (*pstr_len)--;
     411             :                         }
     412           0 :                         tm->tm_mon += 1;
     413           0 :                         replace_type = PGTYPES_TYPE_NOTHING;
     414           0 :                         break;
     415             :                     }
     416             : 
     417             :                     /*
     418             :                      * The ISO 8601 year with century as a decimal number. The
     419             :                      * 4-digit year corresponding to the ISO week number.
     420             :                      */
     421           0 :                 case 'G':
     422             :                     {
     423             :                         /* Keep compiler quiet - Don't use a literal format */
     424           0 :                         const char *fmt = "%G";
     425             : 
     426           0 :                         tm->tm_mon -= 1;
     427           0 :                         i = strftime(q, *pstr_len, fmt, tm);
     428           0 :                         if (i == 0)
     429           0 :                             return -1;
     430           0 :                         while (*q)
     431             :                         {
     432           0 :                             q++;
     433           0 :                             (*pstr_len)--;
     434             :                         }
     435           0 :                         tm->tm_mon += 1;
     436           0 :                         replace_type = PGTYPES_TYPE_NOTHING;
     437             :                     }
     438           0 :                     break;
     439             : 
     440             :                     /*
     441             :                      * Like %G, but without century, i.e., with a 2-digit year
     442             :                      * (00-99).
     443             :                      */
     444           0 :                 case 'g':
     445             :                     {
     446           0 :                         const char *fmt = "%g"; /* Keep compiler quiet about
     447             :                                                  * 2-digit year */
     448             : 
     449           0 :                         tm->tm_mon -= 1;
     450           0 :                         i = strftime(q, *pstr_len, fmt, tm);
     451           0 :                         if (i == 0)
     452           0 :                             return -1;
     453           0 :                         while (*q)
     454             :                         {
     455           0 :                             q++;
     456           0 :                             (*pstr_len)--;
     457             :                         }
     458           0 :                         tm->tm_mon += 1;
     459           0 :                         replace_type = PGTYPES_TYPE_NOTHING;
     460             :                     }
     461           0 :                     break;
     462             :                     /* hour (24 hour clock) with leading zeroes */
     463           8 :                 case 'H':
     464           8 :                     replace_val.uint_val = tm->tm_hour;
     465           8 :                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
     466           8 :                     break;
     467             :                     /* hour (12 hour clock) with leading zeroes */
     468           0 :                 case 'I':
     469           0 :                     replace_val.uint_val = tm->tm_hour % 12;
     470           0 :                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
     471           0 :                     break;
     472             : 
     473             :                     /*
     474             :                      * The day of the year as a decimal number with leading
     475             :                      * zeroes. It ranges from 001 to 366.
     476             :                      */
     477           4 :                 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           0 :                 case 'k':
     487           0 :                     replace_val.uint_val = tm->tm_hour;
     488           0 :                     replace_type = PGTYPES_TYPE_UINT_2_LS;
     489           0 :                     break;
     490             : 
     491             :                     /*
     492             :                      * The hour (12 hour clock). Leading zeroes will be turned
     493             :                      * into spaces.
     494             :                      */
     495           0 :                 case 'l':
     496           0 :                     replace_val.uint_val = tm->tm_hour % 12;
     497           0 :                     replace_type = PGTYPES_TYPE_UINT_2_LS;
     498           0 :                     break;
     499             :                     /* The month as a decimal number with a leading zero */
     500           0 :                 case 'm':
     501           0 :                     replace_val.uint_val = tm->tm_mon;
     502           0 :                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
     503           0 :                     break;
     504             :                     /* The minute as a decimal number with a leading zero */
     505           8 :                 case 'M':
     506           8 :                     replace_val.uint_val = tm->tm_min;
     507           8 :                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
     508           8 :                     break;
     509             :                     /* A newline character */
     510           0 :                 case 'n':
     511           0 :                     replace_val.char_val = '\n';
     512           0 :                     replace_type = PGTYPES_TYPE_CHAR;
     513           0 :                     break;
     514             :                     /* the AM/PM specifier (uppercase) */
     515             :                     /* XXX should be locale aware */
     516           0 :                 case 'p':
     517           0 :                     if (tm->tm_hour < 12)
     518           0 :                         replace_val.str_val = "AM";
     519             :                     else
     520           0 :                         replace_val.str_val = "PM";
     521           0 :                     replace_type = PGTYPES_TYPE_STRING_CONSTANT;
     522           0 :                     break;
     523             :                     /* the AM/PM specifier (lowercase) */
     524             :                     /* XXX should be locale aware */
     525           0 :                 case 'P':
     526           0 :                     if (tm->tm_hour < 12)
     527           0 :                         replace_val.str_val = "am";
     528             :                     else
     529           0 :                         replace_val.str_val = "pm";
     530           0 :                     replace_type = PGTYPES_TYPE_STRING_CONSTANT;
     531           0 :                     break;
     532             :                     /* the time in the format %I:%M:%S %p */
     533             :                     /* XXX should be locale aware */
     534           0 :                 case 'r':
     535           0 :                     i = dttofmtasc_replace(ts, dDate, dow, tm,
     536             :                                            q, pstr_len,
     537             :                                            "%I:%M:%S %p");
     538           0 :                     if (i)
     539           0 :                         return i;
     540           0 :                     break;
     541             :                     /* The time in 24 hour notation (%H:%M) */
     542           0 :                 case 'R':
     543           0 :                     i = dttofmtasc_replace(ts, dDate, dow, tm,
     544             :                                            q, pstr_len,
     545             :                                            "%H:%M");
     546           0 :                     if (i)
     547           0 :                         return i;
     548           0 :                     break;
     549             :                     /* The number of seconds since the Epoch (1970-01-01) */
     550           0 :                 case 's':
     551           0 :                     replace_val.int64_val = (*ts - SetEpochTimestamp()) / 1000000.0;
     552           0 :                     replace_type = PGTYPES_TYPE_INT64;
     553           0 :                     break;
     554             :                     /* seconds as a decimal number with leading zeroes */
     555           8 :                 case 'S':
     556           8 :                     replace_val.uint_val = tm->tm_sec;
     557           8 :                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
     558           8 :                     break;
     559             :                     /* A tabulator */
     560           0 :                 case 't':
     561           0 :                     replace_val.char_val = '\t';
     562           0 :                     replace_type = PGTYPES_TYPE_CHAR;
     563           0 :                     break;
     564             :                     /* The time in 24 hour notation (%H:%M:%S) */
     565           0 :                 case 'T':
     566           0 :                     i = dttofmtasc_replace(ts, dDate, dow, tm,
     567             :                                            q, pstr_len,
     568             :                                            "%H:%M:%S");
     569           0 :                     if (i)
     570           0 :                         return i;
     571           0 :                     break;
     572             : 
     573             :                     /*
     574             :                      * The day of the week as a decimal, Monday = 1, Sunday =
     575             :                      * 7
     576             :                      */
     577           0 :                 case 'u':
     578           0 :                     replace_val.uint_val = dow;
     579           0 :                     if (replace_val.uint_val == 0)
     580           0 :                         replace_val.uint_val = 7;
     581           0 :                     replace_type = PGTYPES_TYPE_UINT;
     582           0 :                     break;
     583             :                     /* The week number of the year as a decimal number */
     584           0 :                 case 'U':
     585           0 :                     tm->tm_mon -= 1;
     586           0 :                     i = strftime(q, *pstr_len, "%U", tm);
     587           0 :                     if (i == 0)
     588           0 :                         return -1;
     589           0 :                     while (*q)
     590             :                     {
     591           0 :                         q++;
     592           0 :                         (*pstr_len)--;
     593             :                     }
     594           0 :                     tm->tm_mon += 1;
     595           0 :                     replace_type = PGTYPES_TYPE_NOTHING;
     596           0 :                     break;
     597             : 
     598             :                     /*
     599             :                      * The ISO 8601:1988 week number of the current year as a
     600             :                      * decimal number.
     601             :                      */
     602           0 :                 case 'V':
     603             :                     {
     604             :                         /* Keep compiler quiet - Don't use a literal format */
     605           0 :                         const char *fmt = "%V";
     606             : 
     607           0 :                         i = strftime(q, *pstr_len, fmt, tm);
     608           0 :                         if (i == 0)
     609           0 :                             return -1;
     610           0 :                         while (*q)
     611             :                         {
     612           0 :                             q++;
     613           0 :                             (*pstr_len)--;
     614             :                         }
     615           0 :                         replace_type = PGTYPES_TYPE_NOTHING;
     616             :                     }
     617           0 :                     break;
     618             : 
     619             :                     /*
     620             :                      * The day of the week as a decimal, Sunday being 0 and
     621             :                      * Monday 1.
     622             :                      */
     623           0 :                 case 'w':
     624           0 :                     replace_val.uint_val = dow;
     625           0 :                     replace_type = PGTYPES_TYPE_UINT;
     626           0 :                     break;
     627             :                     /* The week number of the year (another definition) */
     628           0 :                 case 'W':
     629           0 :                     tm->tm_mon -= 1;
     630           0 :                     i = strftime(q, *pstr_len, "%U", tm);
     631           0 :                     if (i == 0)
     632           0 :                         return -1;
     633           0 :                     while (*q)
     634             :                     {
     635           0 :                         q++;
     636           0 :                         (*pstr_len)--;
     637             :                     }
     638           0 :                     tm->tm_mon += 1;
     639           0 :                     replace_type = PGTYPES_TYPE_NOTHING;
     640           0 :                     break;
     641             : 
     642             :                     /*
     643             :                      * The preferred date representation for the current
     644             :                      * locale without the time.
     645             :                      */
     646           4 :                 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          36 :                         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           4 :                 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          36 :                     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           0 :                 case 'y':
     684           0 :                     replace_val.uint_val = tm->tm_year % 100;
     685           0 :                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
     686           0 :                     break;
     687             :                     /* The year with the century (4 digits) */
     688          12 :                 case 'Y':
     689          12 :                     replace_val.uint_val = tm->tm_year;
     690          12 :                     replace_type = PGTYPES_TYPE_UINT;
     691          12 :                     break;
     692             :                     /* The time zone offset from GMT */
     693           0 :                 case 'z':
     694           0 :                     tm->tm_mon -= 1;
     695           0 :                     i = strftime(q, *pstr_len, "%z", tm);
     696           0 :                     if (i == 0)
     697           0 :                         return -1;
     698           0 :                     while (*q)
     699             :                     {
     700           0 :                         q++;
     701           0 :                         (*pstr_len)--;
     702             :                     }
     703           0 :                     tm->tm_mon += 1;
     704           0 :                     replace_type = PGTYPES_TYPE_NOTHING;
     705           0 :                     break;
     706             :                     /* The name or abbreviation of the time zone */
     707           0 :                 case 'Z':
     708           0 :                     tm->tm_mon -= 1;
     709           0 :                     i = strftime(q, *pstr_len, "%Z", tm);
     710           0 :                     if (i == 0)
     711           0 :                         return -1;
     712           0 :                     while (*q)
     713             :                     {
     714           0 :                         q++;
     715           0 :                         (*pstr_len)--;
     716             :                     }
     717           0 :                     tm->tm_mon += 1;
     718           0 :                     replace_type = PGTYPES_TYPE_NOTHING;
     719           0 :                     break;
     720             :                     /* A % sign */
     721           4 :                 case '%':
     722           4 :                     replace_val.char_val = '%';
     723           4 :                     replace_type = PGTYPES_TYPE_CHAR;
     724           4 :                     break;
     725           0 :                 case '\0':
     726             :                     /* fmtstr: foo%' - The string ends with a % sign */
     727             : 
     728             :                     /*
     729             :                      * this is not compliant to the specification
     730             :                      */
     731           0 :                     return -1;
     732           0 :                 default:
     733             : 
     734             :                     /*
     735             :                      * if we don't know the pattern, we just copy it
     736             :                      */
     737           0 :                     if (*pstr_len > 1)
     738             :                     {
     739           0 :                         *q = '%';
     740           0 :                         q++;
     741           0 :                         (*pstr_len)--;
     742           0 :                         if (*pstr_len > 1)
     743             :                         {
     744           0 :                             *q = *p;
     745           0 :                             q++;
     746           0 :                             (*pstr_len)--;
     747             :                         }
     748             :                         else
     749             :                         {
     750           0 :                             *q = '\0';
     751           0 :                             return -1;
     752             :                         }
     753           0 :                         *q = '\0';
     754             :                     }
     755             :                     else
     756           0 :                         return -1;
     757           0 :                     break;
     758             :             }
     759          76 :             i = pgtypes_fmt_replace(replace_val, replace_type, &q, pstr_len);
     760          76 :             if (i)
     761           0 :                 return i;
     762             :         }
     763             :         else
     764             :         {
     765         200 :             if (*pstr_len > 1)
     766             :             {
     767         200 :                 *q = *p;
     768         200 :                 (*pstr_len)--;
     769         200 :                 q++;
     770         200 :                 *q = '\0';
     771             :             }
     772             :             else
     773           0 :                 return -1;
     774             :         }
     775         276 :         p++;
     776             :     }
     777          16 :     return 0;
     778             : }
     779             : 
     780             : 
     781             : int
     782          16 : 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          16 :     dDate = PGTYPESdate_from_timestamp(*ts);
     790          16 :     dow = PGTYPESdate_dayofweek(dDate);
     791          16 :     timestamp2tm(*ts, NULL, &tm, &fsec, NULL);
     792             : 
     793          16 :     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         100 : 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         100 :     if (!fmt)
     825           4 :         fmt = "%Y-%m-%d %H:%M:%S";
     826         100 :     if (!fmt[0])
     827           4 :         return 1;
     828             : 
     829          96 :     mstr = pgtypes_strdup(str);
     830          96 :     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          96 :     year = -1;
     838          96 :     month = -1;
     839          96 :     day = -1;
     840          96 :     hour = 0;
     841          96 :     minute = -1;
     842          96 :     second = -1;
     843          96 :     tz = 0;
     844             : 
     845          96 :     i = PGTYPEStimestamp_defmt_scan(&mstr, mfmt, d, &year, &month, &day, &hour, &minute, &second, &tz);
     846          96 :     free(mstr);
     847          96 :     free(mfmt);
     848          96 :     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             :     else
     867             :     {
     868          28 :         if (span->month != 0)
     869             :         {
     870             :             struct tm   tt,
     871           4 :                        *tm = &tt;
     872             :             fsec_t      fsec;
     873             : 
     874           4 :             if (timestamp2tm(*tin, NULL, tm, &fsec, NULL) != 0)
     875           0 :                 return -1;
     876           4 :             tm->tm_mon += span->month;
     877           4 :             if (tm->tm_mon > MONTHS_PER_YEAR)
     878             :             {
     879           4 :                 tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
     880           4 :                 tm->tm_mon = (tm->tm_mon - 1) % MONTHS_PER_YEAR + 1;
     881             :             }
     882           0 :             else if (tm->tm_mon < 1)
     883             :             {
     884           0 :                 tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1;
     885           0 :                 tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR;
     886             :             }
     887             : 
     888             : 
     889             :             /* adjust for end of month boundary problems... */
     890           4 :             if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
     891           0 :                 tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]);
     892             : 
     893             : 
     894           4 :             if (tm2timestamp(tm, fsec, NULL, tin) != 0)
     895           0 :                 return -1;
     896             :         }
     897             : 
     898          28 :         *tin += span->time;
     899          28 :         *tout = *tin;
     900             :     }
     901             : 
     902          28 :     return 0;
     903             : }
     904             : 
     905             : 
     906             : /*
     907             : * subtract an interval from a time stamp
     908             : *
     909             : *   *tout = tin - span
     910             : *
     911             : *    returns 0 if successful
     912             : *    returns -1 if it fails
     913             : *
     914             : */
     915             : 
     916             : int
     917           0 : PGTYPEStimestamp_sub_interval(timestamp * tin, interval * span, timestamp * tout)
     918             : {
     919             :     interval    tspan;
     920             : 
     921           0 :     tspan.month = -span->month;
     922           0 :     tspan.time = -span->time;
     923             : 
     924           0 :     return PGTYPEStimestamp_add_interval(tin, &tspan, tout);
     925             : }

Generated by: LCOV version 1.14