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

Generated by: LCOV version 1.14