Header And Logo

PostgreSQL
| The world's most advanced open source database.

timestamp.c

Go to the documentation of this file.
00001 /*
00002  * src/interfaces/ecpg/pgtypeslib/timestamp.c
00003  */
00004 #include "postgres_fe.h"
00005 
00006 #include <time.h>
00007 #include <float.h>
00008 #include <limits.h>
00009 #include <math.h>
00010 
00011 #ifdef __FAST_MATH__
00012 #error -ffast-math is known to break this code
00013 #endif
00014 
00015 #include "extern.h"
00016 #include "dt.h"
00017 #include "pgtypes_timestamp.h"
00018 #include "pgtypes_date.h"
00019 
00020 
00021 int PGTYPEStimestamp_defmt_scan(char **, const char *, timestamp *, int *, int *, int *,
00022                             int *, int *, int *, int *);
00023 
00024 #ifdef HAVE_INT64_TIMESTAMP
00025 static int64
00026 time2t(const int hour, const int min, const int sec, const fsec_t fsec)
00027 {
00028     return (((((hour * MINS_PER_HOUR) + min) * SECS_PER_MINUTE) + sec) * USECS_PER_SEC) + fsec;
00029 }   /* time2t() */
00030 #else
00031 static double
00032 time2t(const int hour, const int min, const int sec, const fsec_t fsec)
00033 {
00034     return (((hour * MINS_PER_HOUR) + min) * SECS_PER_MINUTE) + sec + fsec;
00035 }   /* time2t() */
00036 #endif
00037 
00038 static timestamp
00039 dt2local(timestamp dt, int tz)
00040 {
00041 #ifdef HAVE_INT64_TIMESTAMP
00042     dt -= (tz * USECS_PER_SEC);
00043 #else
00044     dt -= tz;
00045 #endif
00046     return dt;
00047 }   /* dt2local() */
00048 
00049 /* tm2timestamp()
00050  * Convert a tm structure to a timestamp data type.
00051  * Note that year is _not_ 1900-based, but is an explicit full value.
00052  * Also, month is one-based, _not_ zero-based.
00053  *
00054  * Returns -1 on failure (overflow).
00055  */
00056 int
00057 tm2timestamp(struct tm * tm, fsec_t fsec, int *tzp, timestamp * result)
00058 {
00059 #ifdef HAVE_INT64_TIMESTAMP
00060     int         dDate;
00061     int64       time;
00062 #else
00063     double      dDate,
00064                 time;
00065 #endif
00066 
00067     /* Julian day routines are not correct for negative Julian days */
00068     if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
00069         return -1;
00070 
00071     dDate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
00072     time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
00073 #ifdef HAVE_INT64_TIMESTAMP
00074     *result = (dDate * USECS_PER_DAY) + time;
00075     /* check for major overflow */
00076     if ((*result - time) / USECS_PER_DAY != dDate)
00077         return -1;
00078     /* check for just-barely overflow (okay except time-of-day wraps) */
00079     /* caution: we want to allow 1999-12-31 24:00:00 */
00080     if ((*result < 0 && dDate > 0) ||
00081         (*result > 0 && dDate < -1))
00082         return -1;
00083 #else
00084     *result = dDate * SECS_PER_DAY + time;
00085 #endif
00086     if (tzp != NULL)
00087         *result = dt2local(*result, -(*tzp));
00088 
00089     return 0;
00090 }   /* tm2timestamp() */
00091 
00092 static timestamp
00093 SetEpochTimestamp(void)
00094 {
00095 #ifdef HAVE_INT64_TIMESTAMP
00096     int64       noresult = 0;
00097 #else
00098     double      noresult = 0.0;
00099 #endif
00100     timestamp   dt;
00101     struct tm   tt,
00102                *tm = &tt;
00103 
00104     if (GetEpochTime(tm) < 0)
00105         return noresult;
00106 
00107     tm2timestamp(tm, 0, NULL, &dt);
00108     return dt;
00109 }   /* SetEpochTimestamp() */
00110 
00111 /* timestamp2tm()
00112  * Convert timestamp data type to POSIX time structure.
00113  * Note that year is _not_ 1900-based, but is an explicit full value.
00114  * Also, month is one-based, _not_ zero-based.
00115  * Returns:
00116  *   0 on success
00117  *  -1 on out of range
00118  *
00119  * For dates within the system-supported time_t range, convert to the
00120  *  local time zone. If out of this range, leave as GMT. - tgl 97/05/27
00121  */
00122 static int
00123 timestamp2tm(timestamp dt, int *tzp, struct tm * tm, fsec_t *fsec, const char **tzn)
00124 {
00125 #ifdef HAVE_INT64_TIMESTAMP
00126     int64       dDate,
00127                 date0;
00128     int64       time;
00129 #else
00130     double      dDate,
00131                 date0;
00132     double      time;
00133 #endif
00134 #if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
00135     time_t      utime;
00136     struct tm  *tx;
00137 #endif
00138 
00139     date0 = date2j(2000, 1, 1);
00140 
00141 #ifdef HAVE_INT64_TIMESTAMP
00142     time = dt;
00143     TMODULO(time, dDate, USECS_PER_DAY);
00144 
00145     if (time < INT64CONST(0))
00146     {
00147         time += USECS_PER_DAY;
00148         dDate -= 1;
00149     }
00150 
00151     /* add offset to go from J2000 back to standard Julian date */
00152     dDate += date0;
00153 
00154     /* Julian day routine does not work for negative Julian days */
00155     if (dDate < 0 || dDate > (timestamp) INT_MAX)
00156         return -1;
00157 
00158     j2date((int) dDate, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
00159     dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
00160 #else
00161     time = dt;
00162     TMODULO(time, dDate, (double) SECS_PER_DAY);
00163 
00164     if (time < 0)
00165     {
00166         time += SECS_PER_DAY;
00167         dDate -= 1;
00168     }
00169 
00170     /* add offset to go from J2000 back to standard Julian date */
00171     dDate += date0;
00172 
00173 recalc_d:
00174     /* Julian day routine does not work for negative Julian days */
00175     if (dDate < 0 || dDate > (timestamp) INT_MAX)
00176         return -1;
00177 
00178     j2date((int) dDate, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
00179 recalc_t:
00180     dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
00181 
00182     *fsec = TSROUND(*fsec);
00183     /* roundoff may need to propagate to higher-order fields */
00184     if (*fsec >= 1.0)
00185     {
00186         time = ceil(time);
00187         if (time >= (double) SECS_PER_DAY)
00188         {
00189             time = 0;
00190             dDate += 1;
00191             goto recalc_d;
00192         }
00193         goto recalc_t;
00194     }
00195 #endif
00196 
00197     if (tzp != NULL)
00198     {
00199         /*
00200          * Does this fall within the capabilities of the localtime()
00201          * interface? Then use this to rotate to the local time zone.
00202          */
00203         if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
00204         {
00205 #if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
00206 
00207 #ifdef HAVE_INT64_TIMESTAMP
00208             utime = dt / USECS_PER_SEC +
00209                 ((date0 - date2j(1970, 1, 1)) * INT64CONST(86400));
00210 #else
00211             utime = dt + (date0 - date2j(1970, 1, 1)) * SECS_PER_DAY;
00212 #endif
00213 
00214             tx = localtime(&utime);
00215             tm->tm_year = tx->tm_year + 1900;
00216             tm->tm_mon = tx->tm_mon + 1;
00217             tm->tm_mday = tx->tm_mday;
00218             tm->tm_hour = tx->tm_hour;
00219             tm->tm_min = tx->tm_min;
00220             tm->tm_isdst = tx->tm_isdst;
00221 
00222 #if defined(HAVE_TM_ZONE)
00223             tm->tm_gmtoff = tx->tm_gmtoff;
00224             tm->tm_zone = tx->tm_zone;
00225 
00226             *tzp = -tm->tm_gmtoff;      /* tm_gmtoff is Sun/DEC-ism */
00227             if (tzn != NULL)
00228                 *tzn = tm->tm_zone;
00229 #elif defined(HAVE_INT_TIMEZONE)
00230             *tzp = (tm->tm_isdst > 0) ? TIMEZONE_GLOBAL - SECS_PER_HOUR : TIMEZONE_GLOBAL;
00231             if (tzn != NULL)
00232                 *tzn = TZNAME_GLOBAL[(tm->tm_isdst > 0)];
00233 #endif
00234 #else                           /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */
00235             *tzp = 0;
00236             /* Mark this as *no* time zone available */
00237             tm->tm_isdst = -1;
00238             if (tzn != NULL)
00239                 *tzn = NULL;
00240 #endif
00241         }
00242         else
00243         {
00244             *tzp = 0;
00245             /* Mark this as *no* time zone available */
00246             tm->tm_isdst = -1;
00247             if (tzn != NULL)
00248                 *tzn = NULL;
00249         }
00250     }
00251     else
00252     {
00253         tm->tm_isdst = -1;
00254         if (tzn != NULL)
00255             *tzn = NULL;
00256     }
00257 
00258     return 0;
00259 }   /* timestamp2tm() */
00260 
00261 /* EncodeSpecialTimestamp()
00262  *  * Convert reserved timestamp data type to string.
00263  *   */
00264 static int
00265 EncodeSpecialTimestamp(timestamp dt, char *str)
00266 {
00267     if (TIMESTAMP_IS_NOBEGIN(dt))
00268         strcpy(str, EARLY);
00269     else if (TIMESTAMP_IS_NOEND(dt))
00270         strcpy(str, LATE);
00271     else
00272         return FALSE;
00273 
00274     return TRUE;
00275 }   /* EncodeSpecialTimestamp() */
00276 
00277 timestamp
00278 PGTYPEStimestamp_from_asc(char *str, char **endptr)
00279 {
00280     timestamp   result;
00281 
00282 #ifdef HAVE_INT64_TIMESTAMP
00283     int64       noresult = 0;
00284 #else
00285     double      noresult = 0.0;
00286 #endif
00287     fsec_t      fsec;
00288     struct tm   tt,
00289                *tm = &tt;
00290     int         dtype;
00291     int         nf;
00292     char       *field[MAXDATEFIELDS];
00293     int         ftype[MAXDATEFIELDS];
00294     char        lowstr[MAXDATELEN + MAXDATEFIELDS];
00295     char       *realptr;
00296     char      **ptr = (endptr != NULL) ? endptr : &realptr;
00297 
00298     if (strlen(str) >= sizeof(lowstr))
00299     {
00300         errno = PGTYPES_TS_BAD_TIMESTAMP;
00301         return (noresult);
00302     }
00303 
00304     if (ParseDateTime(str, lowstr, field, ftype, &nf, ptr) != 0 ||
00305         DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, 0) != 0)
00306     {
00307         errno = PGTYPES_TS_BAD_TIMESTAMP;
00308         return (noresult);
00309     }
00310 
00311     switch (dtype)
00312     {
00313         case DTK_DATE:
00314             if (tm2timestamp(tm, fsec, NULL, &result) != 0)
00315             {
00316                 errno = PGTYPES_TS_BAD_TIMESTAMP;
00317                 return (noresult);
00318             }
00319             break;
00320 
00321         case DTK_EPOCH:
00322             result = SetEpochTimestamp();
00323             break;
00324 
00325         case DTK_LATE:
00326             TIMESTAMP_NOEND(result);
00327             break;
00328 
00329         case DTK_EARLY:
00330             TIMESTAMP_NOBEGIN(result);
00331             break;
00332 
00333         case DTK_INVALID:
00334             errno = PGTYPES_TS_BAD_TIMESTAMP;
00335             return (noresult);
00336 
00337         default:
00338             errno = PGTYPES_TS_BAD_TIMESTAMP;
00339             return (noresult);
00340     }
00341 
00342     /* AdjustTimestampForTypmod(&result, typmod); */
00343 
00344     /*
00345      * Since it's difficult to test for noresult, make sure errno is 0 if no
00346      * error occurred.
00347      */
00348     errno = 0;
00349     return result;
00350 }
00351 
00352 char *
00353 PGTYPEStimestamp_to_asc(timestamp tstamp)
00354 {
00355     struct tm   tt,
00356                *tm = &tt;
00357     char        buf[MAXDATELEN + 1];
00358     fsec_t      fsec;
00359     int         DateStyle = 1;  /* this defaults to ISO_DATES, shall we make
00360                                  * it an option? */
00361 
00362     if (TIMESTAMP_NOT_FINITE(tstamp))
00363         EncodeSpecialTimestamp(tstamp, buf);
00364     else if (timestamp2tm(tstamp, NULL, tm, &fsec, NULL) == 0)
00365         EncodeDateTime(tm, fsec, false, 0, NULL, DateStyle, buf, 0);
00366     else
00367     {
00368         errno = PGTYPES_TS_BAD_TIMESTAMP;
00369         return NULL;
00370     }
00371     return pgtypes_strdup(buf);
00372 }
00373 
00374 void
00375 PGTYPEStimestamp_current(timestamp * ts)
00376 {
00377     struct tm   tm;
00378 
00379     GetCurrentDateTime(&tm);
00380     if (errno == 0)
00381         tm2timestamp(&tm, 0, NULL, ts);
00382     return;
00383 }
00384 
00385 static int
00386 dttofmtasc_replace(timestamp * ts, date dDate, int dow, struct tm * tm,
00387                    char *output, int *pstr_len, const char *fmtstr)
00388 {
00389     union un_fmt_comb replace_val;
00390     int         replace_type;
00391     int         i;
00392     const char *p = fmtstr;
00393     char       *q = output;
00394 
00395     while (*p)
00396     {
00397         if (*p == '%')
00398         {
00399             p++;
00400             /* fix compiler warning */
00401             replace_type = PGTYPES_TYPE_NOTHING;
00402             switch (*p)
00403             {
00404                     /* the abbreviated name of the day in the week */
00405                     /* XXX should be locale aware */
00406                 case 'a':
00407                     replace_val.str_val = pgtypes_date_weekdays_short[dow];
00408                     replace_type = PGTYPES_TYPE_STRING_CONSTANT;
00409                     break;
00410                     /* the full name of the day in the week */
00411                     /* XXX should be locale aware */
00412                 case 'A':
00413                     replace_val.str_val = days[dow];
00414                     replace_type = PGTYPES_TYPE_STRING_CONSTANT;
00415                     break;
00416                     /* the abbreviated name of the month */
00417                     /* XXX should be locale aware */
00418                 case 'b':
00419                 case 'h':
00420                     replace_val.str_val = months[tm->tm_mon];
00421                     replace_type = PGTYPES_TYPE_STRING_CONSTANT;
00422                     break;
00423                     /* the full name name of the month */
00424                     /* XXX should be locale aware */
00425                 case 'B':
00426                     replace_val.str_val = pgtypes_date_months[tm->tm_mon];
00427                     replace_type = PGTYPES_TYPE_STRING_CONSTANT;
00428                     break;
00429 
00430                     /*
00431                      * The  preferred  date  and  time  representation  for
00432                      * the current locale.
00433                      */
00434                 case 'c':
00435                     /* XXX */
00436                     break;
00437                     /* the century number with leading zeroes */
00438                 case 'C':
00439                     replace_val.uint_val = tm->tm_year / 100;
00440                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
00441                     break;
00442                     /* day with leading zeroes (01 - 31) */
00443                 case 'd':
00444                     replace_val.uint_val = tm->tm_mday;
00445                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
00446                     break;
00447                     /* the date in the format mm/dd/yy */
00448                 case 'D':
00449 
00450                     /*
00451                      * ts, dDate, dow, tm is information about the timestamp
00452                      *
00453                      * q is the start of the current output buffer
00454                      *
00455                      * pstr_len is a pointer to the remaining size of output,
00456                      * i.e. the size of q
00457                      */
00458                     i = dttofmtasc_replace(ts, dDate, dow, tm,
00459                                            q, pstr_len,
00460                                            "%m/%d/%y");
00461                     if (i)
00462                         return i;
00463                     break;
00464                     /* day with leading spaces (01 - 31) */
00465                 case 'e':
00466                     replace_val.uint_val = tm->tm_mday;
00467                     replace_type = PGTYPES_TYPE_UINT_2_LS;
00468                     break;
00469 
00470                     /*
00471                      * alternative format modifier
00472                      */
00473                 case 'E':
00474                     {
00475                         char        tmp[4] = "%Ex";
00476 
00477                         p++;
00478                         if (*p == '\0')
00479                             return -1;
00480                         tmp[2] = *p;
00481 
00482                         /*
00483                          * strftime's month is 0 based, ours is 1 based
00484                          */
00485                         tm->tm_mon -= 1;
00486                         i = strftime(q, *pstr_len, tmp, tm);
00487                         if (i == 0)
00488                             return -1;
00489                         while (*q)
00490                         {
00491                             q++;
00492                             (*pstr_len)--;
00493                         }
00494                         tm->tm_mon += 1;
00495                         replace_type = PGTYPES_TYPE_NOTHING;
00496                         break;
00497                     }
00498 
00499                     /*
00500                      * The ISO 8601 year with century as a decimal number. The
00501                      * 4-digit year corresponding to the ISO week number.
00502                      */
00503                 case 'G':
00504                     {
00505                         /* Keep compiler quiet - Don't use a literal format */
00506                         const char *fmt = "%G";
00507 
00508                         tm->tm_mon -= 1;
00509                         i = strftime(q, *pstr_len, fmt, tm);
00510                         if (i == 0)
00511                             return -1;
00512                         while (*q)
00513                         {
00514                             q++;
00515                             (*pstr_len)--;
00516                         }
00517                         tm->tm_mon += 1;
00518                         replace_type = PGTYPES_TYPE_NOTHING;
00519                     }
00520                     break;
00521 
00522                     /*
00523                      * Like %G, but without century, i.e., with a 2-digit year
00524                      * (00-99).
00525                      */
00526                 case 'g':
00527                     {
00528                         const char *fmt = "%g"; /* Keep compiler quiet about
00529                                                  * 2-digit year */
00530 
00531                         tm->tm_mon -= 1;
00532                         i = strftime(q, *pstr_len, fmt, tm);
00533                         if (i == 0)
00534                             return -1;
00535                         while (*q)
00536                         {
00537                             q++;
00538                             (*pstr_len)--;
00539                         }
00540                         tm->tm_mon += 1;
00541                         replace_type = PGTYPES_TYPE_NOTHING;
00542                     }
00543                     break;
00544                     /* hour (24 hour clock) with leading zeroes */
00545                 case 'H':
00546                     replace_val.uint_val = tm->tm_hour;
00547                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
00548                     break;
00549                     /* hour (12 hour clock) with leading zeroes */
00550                 case 'I':
00551                     replace_val.uint_val = tm->tm_hour % 12;
00552                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
00553                     break;
00554 
00555                     /*
00556                      * The day of the year as a decimal number with leading
00557                      * zeroes. It ranges from 001 to 366.
00558                      */
00559                 case 'j':
00560                     replace_val.uint_val = tm->tm_yday;
00561                     replace_type = PGTYPES_TYPE_UINT_3_LZ;
00562                     break;
00563 
00564                     /*
00565                      * The hour (24 hour clock). Leading zeroes will be turned
00566                      * into spaces.
00567                      */
00568                 case 'k':
00569                     replace_val.uint_val = tm->tm_hour;
00570                     replace_type = PGTYPES_TYPE_UINT_2_LS;
00571                     break;
00572 
00573                     /*
00574                      * The hour (12 hour clock). Leading zeroes will be turned
00575                      * into spaces.
00576                      */
00577                 case 'l':
00578                     replace_val.uint_val = tm->tm_hour % 12;
00579                     replace_type = PGTYPES_TYPE_UINT_2_LS;
00580                     break;
00581                     /* The month as a decimal number with a leading zero */
00582                 case 'm':
00583                     replace_val.uint_val = tm->tm_mon;
00584                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
00585                     break;
00586                     /* The minute as a decimal number with a leading zero */
00587                 case 'M':
00588                     replace_val.uint_val = tm->tm_min;
00589                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
00590                     break;
00591                     /* A newline character */
00592                 case 'n':
00593                     replace_val.char_val = '\n';
00594                     replace_type = PGTYPES_TYPE_CHAR;
00595                     break;
00596                     /* the AM/PM specifier (uppercase) */
00597                     /* XXX should be locale aware */
00598                 case 'p':
00599                     if (tm->tm_hour < 12)
00600                         replace_val.str_val = "AM";
00601                     else
00602                         replace_val.str_val = "PM";
00603                     replace_type = PGTYPES_TYPE_STRING_CONSTANT;
00604                     break;
00605                     /* the AM/PM specifier (lowercase) */
00606                     /* XXX should be locale aware */
00607                 case 'P':
00608                     if (tm->tm_hour < 12)
00609                         replace_val.str_val = "am";
00610                     else
00611                         replace_val.str_val = "pm";
00612                     replace_type = PGTYPES_TYPE_STRING_CONSTANT;
00613                     break;
00614                     /* the time in the format %I:%M:%S %p */
00615                     /* XXX should be locale aware */
00616                 case 'r':
00617                     i = dttofmtasc_replace(ts, dDate, dow, tm,
00618                                            q, pstr_len,
00619                                            "%I:%M:%S %p");
00620                     if (i)
00621                         return i;
00622                     break;
00623                     /* The time in 24 hour notation (%H:%M) */
00624                 case 'R':
00625                     i = dttofmtasc_replace(ts, dDate, dow, tm,
00626                                            q, pstr_len,
00627                                            "%H:%M");
00628                     if (i)
00629                         return i;
00630                     break;
00631                     /* The number of seconds since the Epoch (1970-01-01) */
00632                 case 's':
00633 #ifdef HAVE_INT64_TIMESTAMP
00634                     replace_val.int64_val = (*ts - SetEpochTimestamp()) / 1000000.0;
00635                     replace_type = PGTYPES_TYPE_INT64;
00636 #else
00637                     replace_val.double_val = *ts - SetEpochTimestamp();
00638                     replace_type = PGTYPES_TYPE_DOUBLE_NF;
00639 #endif
00640                     break;
00641                     /* seconds as a decimal number with leading zeroes */
00642                 case 'S':
00643                     replace_val.uint_val = tm->tm_sec;
00644                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
00645                     break;
00646                     /* A tabulator */
00647                 case 't':
00648                     replace_val.char_val = '\t';
00649                     replace_type = PGTYPES_TYPE_CHAR;
00650                     break;
00651                     /* The time in 24 hour notation (%H:%M:%S) */
00652                 case 'T':
00653                     i = dttofmtasc_replace(ts, dDate, dow, tm,
00654                                            q, pstr_len,
00655                                            "%H:%M:%S");
00656                     if (i)
00657                         return i;
00658                     break;
00659 
00660                     /*
00661                      * The day of the week as a decimal, Monday = 1, Sunday =
00662                      * 7
00663                      */
00664                 case 'u':
00665                     replace_val.uint_val = dow;
00666                     if (replace_val.uint_val == 0)
00667                         replace_val.uint_val = 7;
00668                     replace_type = PGTYPES_TYPE_UINT;
00669                     break;
00670                     /* The week number of the year as a decimal number */
00671                 case 'U':
00672                     tm->tm_mon -= 1;
00673                     i = strftime(q, *pstr_len, "%U", tm);
00674                     if (i == 0)
00675                         return -1;
00676                     while (*q)
00677                     {
00678                         q++;
00679                         (*pstr_len)--;
00680                     }
00681                     tm->tm_mon += 1;
00682                     replace_type = PGTYPES_TYPE_NOTHING;
00683                     break;
00684 
00685                     /*
00686                      * The ISO 8601:1988 week number of the current year as a
00687                      * decimal number.
00688                      */
00689                 case 'V':
00690                     {
00691                         /* Keep compiler quiet - Don't use a literal format */
00692                         const char *fmt = "%V";
00693 
00694                         i = strftime(q, *pstr_len, fmt, tm);
00695                         if (i == 0)
00696                             return -1;
00697                         while (*q)
00698                         {
00699                             q++;
00700                             (*pstr_len)--;
00701                         }
00702                         replace_type = PGTYPES_TYPE_NOTHING;
00703                     }
00704                     break;
00705 
00706                     /*
00707                      * The day of the week as a decimal, Sunday being 0 and
00708                      * Monday 1.
00709                      */
00710                 case 'w':
00711                     replace_val.uint_val = dow;
00712                     replace_type = PGTYPES_TYPE_UINT;
00713                     break;
00714                     /* The week number of the year (another definition) */
00715                 case 'W':
00716                     tm->tm_mon -= 1;
00717                     i = strftime(q, *pstr_len, "%U", tm);
00718                     if (i == 0)
00719                         return -1;
00720                     while (*q)
00721                     {
00722                         q++;
00723                         (*pstr_len)--;
00724                     }
00725                     tm->tm_mon += 1;
00726                     replace_type = PGTYPES_TYPE_NOTHING;
00727                     break;
00728 
00729                     /*
00730                      * The preferred date representation for the current
00731                      * locale without the time.
00732                      */
00733                 case 'x':
00734                     {
00735                         const char *fmt = "%x"; /* Keep compiler quiet about
00736                                                  * 2-digit year */
00737 
00738                         tm->tm_mon -= 1;
00739                         i = strftime(q, *pstr_len, fmt, tm);
00740                         if (i == 0)
00741                             return -1;
00742                         while (*q)
00743                         {
00744                             q++;
00745                             (*pstr_len)--;
00746                         }
00747                         tm->tm_mon += 1;
00748                         replace_type = PGTYPES_TYPE_NOTHING;
00749                     }
00750                     break;
00751 
00752                     /*
00753                      * The preferred time representation for the current
00754                      * locale without the date.
00755                      */
00756                 case 'X':
00757                     tm->tm_mon -= 1;
00758                     i = strftime(q, *pstr_len, "%X", tm);
00759                     if (i == 0)
00760                         return -1;
00761                     while (*q)
00762                     {
00763                         q++;
00764                         (*pstr_len)--;
00765                     }
00766                     tm->tm_mon += 1;
00767                     replace_type = PGTYPES_TYPE_NOTHING;
00768                     break;
00769                     /* The year without the century (2 digits, leading zeroes) */
00770                 case 'y':
00771                     replace_val.uint_val = tm->tm_year % 100;
00772                     replace_type = PGTYPES_TYPE_UINT_2_LZ;
00773                     break;
00774                     /* The year with the century (4 digits) */
00775                 case 'Y':
00776                     replace_val.uint_val = tm->tm_year;
00777                     replace_type = PGTYPES_TYPE_UINT;
00778                     break;
00779                     /* The time zone offset from GMT */
00780                 case 'z':
00781                     tm->tm_mon -= 1;
00782                     i = strftime(q, *pstr_len, "%z", tm);
00783                     if (i == 0)
00784                         return -1;
00785                     while (*q)
00786                     {
00787                         q++;
00788                         (*pstr_len)--;
00789                     }
00790                     tm->tm_mon += 1;
00791                     replace_type = PGTYPES_TYPE_NOTHING;
00792                     break;
00793                     /* The name or abbreviation of the time zone */
00794                 case 'Z':
00795                     tm->tm_mon -= 1;
00796                     i = strftime(q, *pstr_len, "%Z", tm);
00797                     if (i == 0)
00798                         return -1;
00799                     while (*q)
00800                     {
00801                         q++;
00802                         (*pstr_len)--;
00803                     }
00804                     tm->tm_mon += 1;
00805                     replace_type = PGTYPES_TYPE_NOTHING;
00806                     break;
00807                     /* A % sign */
00808                 case '%':
00809                     replace_val.char_val = '%';
00810                     replace_type = PGTYPES_TYPE_CHAR;
00811                     break;
00812                 case '\0':
00813                     /* fmtstr: foo%' - The string ends with a % sign */
00814 
00815                     /*
00816                      * this is not compliant to the specification
00817                      */
00818                     return -1;
00819                 default:
00820 
00821                     /*
00822                      * if we don't know the pattern, we just copy it
00823                      */
00824                     if (*pstr_len > 1)
00825                     {
00826                         *q = '%';
00827                         q++;
00828                         (*pstr_len)--;
00829                         if (*pstr_len > 1)
00830                         {
00831                             *q = *p;
00832                             q++;
00833                             (*pstr_len)--;
00834                         }
00835                         else
00836                         {
00837                             *q = '\0';
00838                             return -1;
00839                         }
00840                         *q = '\0';
00841                     }
00842                     else
00843                         return -1;
00844                     break;
00845             }
00846             i = pgtypes_fmt_replace(replace_val, replace_type, &q, pstr_len);
00847             if (i)
00848                 return i;
00849         }
00850         else
00851         {
00852             if (*pstr_len > 1)
00853             {
00854                 *q = *p;
00855                 (*pstr_len)--;
00856                 q++;
00857                 *q = '\0';
00858             }
00859             else
00860                 return -1;
00861         }
00862         p++;
00863     }
00864     return 0;
00865 }
00866 
00867 
00868 int
00869 PGTYPEStimestamp_fmt_asc(timestamp * ts, char *output, int str_len, const char *fmtstr)
00870 {
00871     struct tm   tm;
00872     fsec_t      fsec;
00873     date        dDate;
00874     int         dow;
00875 
00876     dDate = PGTYPESdate_from_timestamp(*ts);
00877     dow = PGTYPESdate_dayofweek(dDate);
00878     timestamp2tm(*ts, NULL, &tm, &fsec, NULL);
00879 
00880     return dttofmtasc_replace(ts, dDate, dow, &tm, output, &str_len, fmtstr);
00881 }
00882 
00883 int
00884 PGTYPEStimestamp_sub(timestamp * ts1, timestamp * ts2, interval * iv)
00885 {
00886     if (TIMESTAMP_NOT_FINITE(*ts1) || TIMESTAMP_NOT_FINITE(*ts2))
00887         return PGTYPES_TS_ERR_EINFTIME;
00888     else
00889         iv->time = (*ts1 - *ts2);
00890 
00891     iv->month = 0;
00892 
00893     return 0;
00894 }
00895 
00896 int
00897 PGTYPEStimestamp_defmt_asc(char *str, const char *fmt, timestamp * d)
00898 {
00899     int         year,
00900                 month,
00901                 day;
00902     int         hour,
00903                 minute,
00904                 second;
00905     int         tz;
00906 
00907     int         i;
00908     char       *mstr;
00909     char       *mfmt;
00910 
00911     if (!fmt)
00912         fmt = "%Y-%m-%d %H:%M:%S";
00913     if (!fmt[0])
00914         return 1;
00915 
00916     mstr = pgtypes_strdup(str);
00917     mfmt = pgtypes_strdup(fmt);
00918 
00919     /*
00920      * initialize with impossible values so that we can see if the fields
00921      * where specified at all
00922      */
00923     /* XXX ambiguity with 1 BC for year? */
00924     year = -1;
00925     month = -1;
00926     day = -1;
00927     hour = 0;
00928     minute = -1;
00929     second = -1;
00930     tz = 0;
00931 
00932     i = PGTYPEStimestamp_defmt_scan(&mstr, mfmt, d, &year, &month, &day, &hour, &minute, &second, &tz);
00933     free(mstr);
00934     free(mfmt);
00935     return i;
00936 }
00937 
00938 /*
00939 * add an interval to a time stamp
00940 *
00941 *   *tout = tin + span
00942 *
00943 *    returns 0 if successful
00944 *    returns -1 if it fails
00945 *
00946 */
00947 
00948 int
00949 PGTYPEStimestamp_add_interval(timestamp * tin, interval * span, timestamp * tout)
00950 {
00951 
00952 
00953     if (TIMESTAMP_NOT_FINITE(*tin))
00954         *tout = *tin;
00955 
00956 
00957     else
00958     {
00959         if (span->month != 0)
00960         {
00961             struct tm   tt,
00962                        *tm = &tt;
00963             fsec_t      fsec;
00964 
00965 
00966             if (timestamp2tm(*tin, NULL, tm, &fsec, NULL) != 0)
00967                 return -1;
00968             tm->tm_mon += span->month;
00969             if (tm->tm_mon > MONTHS_PER_YEAR)
00970             {
00971                 tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
00972                 tm->tm_mon = (tm->tm_mon - 1) % MONTHS_PER_YEAR + 1;
00973             }
00974             else if (tm->tm_mon < 1)
00975             {
00976                 tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1;
00977                 tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR;
00978             }
00979 
00980 
00981             /* adjust for end of month boundary problems... */
00982             if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
00983                 tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]);
00984 
00985 
00986             if (tm2timestamp(tm, fsec, NULL, tin) != 0)
00987                 return -1;
00988         }
00989 
00990 
00991         *tin += span->time;
00992         *tout = *tin;
00993     }
00994     return 0;
00995 
00996 }
00997 
00998 
00999 /*
01000 * subtract an interval from a time stamp
01001 *
01002 *   *tout = tin - span
01003 *
01004 *    returns 0 if successful
01005 *    returns -1 if it fails
01006 *
01007 */
01008 
01009 int
01010 PGTYPEStimestamp_sub_interval(timestamp * tin, interval * span, timestamp * tout)
01011 {
01012     interval    tspan;
01013 
01014     tspan.month = -span->month;
01015     tspan.time = -span->time;
01016 
01017 
01018     return PGTYPEStimestamp_add_interval(tin, &tspan, tout);
01019 }