diff options
author | Thomas G. Lockhart <lockhart@fourpalms.org> | 2002-04-21 19:52:18 +0000 |
---|---|---|
committer | Thomas G. Lockhart <lockhart@fourpalms.org> | 2002-04-21 19:52:18 +0000 |
commit | 547df0cc853c3fe70dbf33e241cbe46f5e441b3e (patch) | |
tree | 1cc84e876fb28cae976d53ad64e6fab4a5c22e66 /src/backend/utils/adt/timestamp.c | |
parent | 3fab49325de7924b051ea25ee8d1436d3aff6b28 (diff) |
Support alternate storage scheme of 64-bit integer for date/time types.
Use "--enable-integer-datetimes" in configuration to use this rather
than the original float8 storage. I would recommend the integer-based
storage for any platform on which it is available. We perhaps should
make this the default for the production release.
Change timezone(timestamptz) results to return timestamp rather than
a character string. Formerly, we didn't have a way to represent
timestamps with an explicit time zone other than freezing the info into
a string. Now, we can reasonably omit the explicit time zone from the
result and return a timestamp with values appropriate for the specified
time zone. Much cleaner, and if you need the time zone in the result
you can put it into a character string pretty easily anyway.
Allow fractional seconds in date/time types even for dates prior to 1BC.
Limit timestamp data types to 6 decimal places of precision. Just right
for a micro-second storage of int8 date/time types, and reduces the
number of places ad-hoc rounding was occuring for the float8-based types.
Use lookup tables for precision/rounding calculations for timestamp and
interval types. Formerly used pow() to calculate the desired value but
with a more limited range there is no reason to not type in a lookup
table. Should be *much* better performance, though formerly there were
some optimizations to help minimize the number of times pow() was called.
Define a HAVE_INT64_TIMESTAMP variable. Based on the configure option
"--enable-integer-datetimes" and the existing internal INT64_IS_BUSTED.
Add explicit date/interval operators and functions for addition and
subtraction. Formerly relied on implicit type promotion from date to
timestamp with time zone.
Change timezone conversion functions for the timetz type from "timetz()"
to "timezone()". This is consistant with other time zone coersion
functions for other types.
Bump the catalog version to 200204201.
Fix up regression tests to reflect changes in fractional seconds
representation for date/times in BC eras.
All regression tests pass on my Linux box.
Diffstat (limited to 'src/backend/utils/adt/timestamp.c')
-rw-r--r-- | src/backend/utils/adt/timestamp.c | 716 |
1 files changed, 545 insertions, 171 deletions
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 5fbdc5b8d89..7637ccf150b 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.65 2002/03/09 17:35:36 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.66 2002/04/21 19:48:13 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -29,7 +29,11 @@ #include "utils/builtins.h" -static double time2t(const int hour, const int min, const double sec); +#ifdef HAVE_INT64_TIMESTAMP +static int64 time2t(const int hour, const int min, const int sec, const fsec_t fsec); +#else +static double time2t(const int hour, const int min, const int sec, const fsec_t fsec); +#endif static int EncodeSpecialTimestamp(Timestamp dt, char *str); static Timestamp dt2local(Timestamp dt, int timezone); static void AdjustTimestampForTypmod(Timestamp *time, int32 typmod); @@ -53,7 +57,7 @@ timestamp_in(PG_FUNCTION_ARGS) #endif int32 typmod = PG_GETARG_INT32(2); Timestamp result; - double fsec; + fsec_t fsec; struct tm tt, *tm = &tt; int tz; @@ -111,7 +115,7 @@ timestamp_out(PG_FUNCTION_ARGS) char *result; struct tm tt, *tm = &tt; - double fsec; + fsec_t fsec; char *tzn = NULL; char buf[MAXDATELEN + 1]; @@ -147,19 +151,81 @@ timestamp_scale(PG_FUNCTION_ARGS) static void AdjustTimestampForTypmod(Timestamp *time, int32 typmod) { - if (!TIMESTAMP_NOT_FINITE(*time) && - (typmod >= 0) && (typmod <= 13)) +#ifdef HAVE_INT64_TIMESTAMP + static const int64 TimestampScales[MAX_TIMESTAMP_PRECISION+1] = { + INT64CONST(1000000), + INT64CONST(100000), + INT64CONST(10000), + INT64CONST(1000), + INT64CONST(100), + INT64CONST(10), + INT64CONST(1) + }; + + static const int64 TimestampOffsets[MAX_TIMESTAMP_PRECISION+1] = { + INT64CONST(-500000), + INT64CONST(-50000), + INT64CONST(-5000), + INT64CONST(-500), + INT64CONST(-50), + INT64CONST(-5), + INT64CONST(0) + }; +#else + static const double TimestampScales[MAX_TIMESTAMP_PRECISION+1] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000 + }; + + static const double TimestampOffsets[MAX_TIMESTAMP_PRECISION+1] = { + 0.5, + 0.05, + 0.005, + 0.0005, + 0.00005, + 0.000005, + 0.0000005 + }; +#endif + + if (!TIMESTAMP_NOT_FINITE(*time) + && (typmod != -1) && (typmod != MAX_TIMESTAMP_PRECISION)) { - static double TimestampScale = 1; - static int32 TimestampTypmod = 0; + if ((typmod < 0) || (typmod > MAX_TIMESTAMP_PRECISION)) + elog(ERROR, "TIMESTAMP(%d) precision must be between %d and %d", + typmod, 0, MAX_TIMESTAMP_PRECISION); - if (typmod != TimestampTypmod) +#ifdef HAVE_INT64_TIMESTAMP + /* we have different truncation behavior depending on sign */ + if (*time >= INT64CONST(0)) { - TimestampScale = pow(10.0, typmod); - TimestampTypmod = typmod; + *time = ((*time / TimestampScales[typmod]) + * TimestampScales[typmod]); } - - *time = (rint(((double) *time) * TimestampScale) / TimestampScale); + else + { + *time = (((*time + TimestampOffsets[typmod]) / TimestampScales[typmod]) + * TimestampScales[typmod]); + } +#else + /* we have different truncation behavior depending on sign */ + if (*time >= 0) + { + *time = (rint(((double) *time) * TimestampScales[typmod]) + / TimestampScales[typmod]); + } + else + { + /* Scale and truncate first, then add to help the rounding behavior */ + *time = (rint((((double) *time) * TimestampScales[typmod]) + TimestampOffsets[typmod]) + / TimestampScales[typmod]); + } +#endif } } @@ -177,7 +243,7 @@ timestamptz_in(PG_FUNCTION_ARGS) #endif int32 typmod = PG_GETARG_INT32(2); TimestampTz result; - double fsec; + fsec_t fsec; struct tm tt, *tm = &tt; int tz; @@ -236,7 +302,7 @@ timestamptz_out(PG_FUNCTION_ARGS) int tz; struct tm tt, *tm = &tt; - double fsec; + fsec_t fsec; char *tzn; char buf[MAXDATELEN + 1]; @@ -286,7 +352,7 @@ interval_in(PG_FUNCTION_ARGS) #endif int32 typmod = PG_GETARG_INT32(2); Interval *result; - double fsec; + fsec_t fsec; struct tm tt, *tm = &tt; int dtype; @@ -304,7 +370,7 @@ interval_in(PG_FUNCTION_ARGS) fsec = 0; if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0) - || (DecodeDateDelta(field, ftype, nf, &dtype, tm, &fsec) != 0)) + || (DecodeInterval(field, ftype, nf, &dtype, tm, &fsec) != 0)) elog(ERROR, "Bad interval external representation '%s'", str); result = (Interval *) palloc(sizeof(Interval)); @@ -338,13 +404,13 @@ interval_out(PG_FUNCTION_ARGS) char *result; struct tm tt, *tm = &tt; - double fsec; + fsec_t fsec; char buf[MAXDATELEN + 1]; if (interval2tm(*span, tm, &fsec) != 0) elog(ERROR, "Unable to encode interval; internal coding error"); - if (EncodeTimeSpan(tm, fsec, DateStyle, buf) != 0) + if (EncodeInterval(tm, fsec, DateStyle, buf) != 0) elog(ERROR, "Unable to format interval; internal coding error"); result = pstrdup(buf); @@ -375,6 +441,48 @@ interval_scale(PG_FUNCTION_ARGS) static void AdjustIntervalForTypmod(Interval *interval, int32 typmod) { +#ifdef HAVE_INT64_TIMESTAMP + static const int64 IntervalScales[MAX_INTERVAL_PRECISION+1] = { + INT64CONST(1000000), + INT64CONST(100000), + INT64CONST(10000), + INT64CONST(1000), + INT64CONST(100), + INT64CONST(10), + INT64CONST(1) + }; + + static const int64 IntervalOffsets[MAX_INTERVAL_PRECISION+1] = { + INT64CONST(-500000), + INT64CONST(-50000), + INT64CONST(-5000), + INT64CONST(-500), + INT64CONST(-50), + INT64CONST(-5), + INT64CONST(0) + }; +#else + static const double IntervalScales[MAX_INTERVAL_PRECISION+1] = { + 1000000, + 100000, + 10000, + 1000, + 100, + 10, + 1 + }; + + static const double IntervalOffsets[MAX_INTERVAL_PRECISION+1] = { + -500000, + -50000, + -5000, + -500, + -50, + -5, + 0 + }; +#endif + if (typmod != -1) { int range = ((typmod >> 16) & 0x7FFF); @@ -396,102 +504,190 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod) } /* YEAR TO MONTH */ else if (range == (MASK(YEAR) | MASK(MONTH))) + { interval->time = 0; + } else if (range == MASK(DAY)) { interval->month = 0; +#ifdef HAVE_INT64_TIMESTAMP + interval->time = (((int) (interval->time / INT64CONST(86400000000))) + * INT64CONST(86400000000)); +#else interval->time = (((int) (interval->time / 86400)) * 86400); +#endif } else if (range == MASK(HOUR)) { +#ifdef HAVE_INT64_TIMESTAMP + int64 day; +#else double day; +#endif interval->month = 0; +#ifdef HAVE_INT64_TIMESTAMP + day = (interval->time / INT64CONST(86400000000)); + interval->time -= (day * INT64CONST(86400000000)); + interval->time = ((interval->time / INT64CONST(3600000000)) + * INT64CONST(3600000000)); +#else TMODULO(interval->time, day, 86400.0); interval->time = (((int) (interval->time / 3600)) * 3600.0); +#endif } else if (range == MASK(MINUTE)) { +#ifdef HAVE_INT64_TIMESTAMP + int64 hour; +#else double hour; +#endif interval->month = 0; +#ifdef HAVE_INT64_TIMESTAMP + hour = (interval->time / INT64CONST(3600000000)); + interval->time -= (hour * INT64CONST(3600000000)); + interval->time = ((interval->time / INT64CONST(60000000)) + * INT64CONST(60000000)); +#else TMODULO(interval->time, hour, 3600.0); interval->time = (((int) (interval->time / 60)) * 60); +#endif } else if (range == MASK(SECOND)) { - double hour; +#ifdef HAVE_INT64_TIMESTAMP + int64 minute; +#else + double minute; +#endif interval->month = 0; - TMODULO(interval->time, hour, 60.0); +#ifdef HAVE_INT64_TIMESTAMP + minute = (interval->time / INT64CONST(60000000)); + interval->time -= (minute * INT64CONST(60000000)); +#else + TMODULO(interval->time, minute, 60.0); /* interval->time = (int)(interval->time); */ +#endif } /* DAY TO HOUR */ else if (range == (MASK(DAY) | MASK(HOUR))) { interval->month = 0; +#ifdef HAVE_INT64_TIMESTAMP + interval->time = ((interval->time / INT64CONST(3600000000)) + * INT64CONST(3600000000)); +#else interval->time = (((int) (interval->time / 3600)) * 3600); +#endif } /* DAY TO MINUTE */ else if (range == (MASK(DAY) | MASK(HOUR) | MASK(MINUTE))) { interval->month = 0; +#ifdef HAVE_INT64_TIMESTAMP + interval->time = ((interval->time / INT64CONST(60000000)) + * INT64CONST(60000000)); +#else interval->time = (((int) (interval->time / 60)) * 60); +#endif } /* DAY TO SECOND */ else if (range == (MASK(DAY) | MASK(HOUR) | MASK(MINUTE) | MASK(SECOND))) + { interval->month = 0; + } /* HOUR TO MINUTE */ else if (range == (MASK(HOUR) | MASK(MINUTE))) { +#ifdef HAVE_INT64_TIMESTAMP + int64 day; +#else double day; +#endif interval->month = 0; +#ifdef HAVE_INT64_TIMESTAMP + day = (interval->time / INT64CONST(86400000000)); + interval->time -= (day * INT64CONST(86400000000)); + interval->time = ((interval->time / INT64CONST(60000000)) + * INT64CONST(60000000)); +#else TMODULO(interval->time, day, 86400.0); interval->time = (((int) (interval->time / 60)) * 60); +#endif } /* HOUR TO SECOND */ else if (range == (MASK(HOUR) | MASK(MINUTE) | MASK(SECOND))) { +#ifdef HAVE_INT64_TIMESTAMP + int64 day; +#else double day; +#endif interval->month = 0; +#ifdef HAVE_INT64_TIMESTAMP + day = (interval->time / INT64CONST(86400000000)); + interval->time -= (day * INT64CONST(86400000000)); +#else TMODULO(interval->time, day, 86400.0); +#endif } /* MINUTE TO SECOND */ else if (range == (MASK(MINUTE) | MASK(SECOND))) { +#ifdef HAVE_INT64_TIMESTAMP + int64 hour; +#else double hour; +#endif interval->month = 0; +#ifdef HAVE_INT64_TIMESTAMP + hour = (interval->time / INT64CONST(3600000000)); + interval->time -= (hour * INT64CONST(3600000000)); +#else TMODULO(interval->time, hour, 3600.0); +#endif } else elog(ERROR, "AdjustIntervalForTypmod(): internal coding error"); + /* Need to adjust precision? If not, don't even try! */ if (precision != 0xFFFF) { - static double IntervalScale = 1; - static int IntervalTypmod = 0; + if ((precision < 0) || (precision > MAX_INTERVAL_PRECISION)) + elog(ERROR, "INTERVAL(%d) precision must be between %d and %d", + precision, 0, MAX_INTERVAL_PRECISION); - if (precision != IntervalTypmod) +#ifdef HAVE_INT64_TIMESTAMP + /* we have different truncation behavior depending on sign */ + if (interval->time >= INT64CONST(0)) { - IntervalTypmod = precision; - IntervalScale = pow(10.0, IntervalTypmod); + interval->time = ((interval->time / IntervalScales[precision]) + * IntervalScales[precision]); } - - /* - * Hmm. For the time field, we can get to a large value since - * we store everything related to an absolute interval (e.g. - * years worth of days) in this one field. So we have - * precision problems doing rint() on this field if the field - * is too large. This resulted in an annoying "...0001" - * appended to the printed result on my Linux box. I hate - * doing an expensive math operation like log10() to avoid - * this, but what else can we do?? - thomas 2001-10-19 - */ - if ((log10(interval->time) + IntervalTypmod) <= 13) - interval->time = (rint(interval->time * IntervalScale) / IntervalScale); + else + { + interval->time = (((interval->time + IntervalOffsets[precision]) / IntervalScales[precision]) + * IntervalScales[precision]); + } +#else + /* we have different truncation behavior depending on sign */ + if (interval->time >= 0) + { + interval->time = (rint(((double) interval->time) * IntervalScales[precision]) + / IntervalScales[precision]); + } + else + { + interval->time = (rint((((double) interval->time) + IntervalOffsets[precision]) + * IntervalScales[precision]) / IntervalScales[precision]); + } +#endif } } @@ -524,23 +720,42 @@ now(PG_FUNCTION_ARGS) sec = GetCurrentTransactionStartTimeUsec(&usec); +#ifdef HAVE_INT64_TIMESTAMP + result = (((sec - ((date2j(2000, 1, 1) - date2j(1970, 1, 1)) * 86400)) + * INT64CONST(1000000)) + usec); +#else result = (sec + (usec * 1.0e-6) - ((date2j(2000, 1, 1) - date2j(1970, 1, 1)) * 86400)); +#endif PG_RETURN_TIMESTAMPTZ(result); } void -dt2time(Timestamp jd, int *hour, int *min, double *sec) +dt2time(Timestamp jd, int *hour, int *min, int *sec, fsec_t *fsec) { +#ifdef HAVE_INT64_TIMESTAMP + int64 time; +#else double time; +#endif time = jd; +#ifdef HAVE_INT64_TIMESTAMP + *hour = (time / INT64CONST(3600000000)); + time -= ((*hour) * INT64CONST(3600000000)); + *min = (time / INT64CONST(60000000)); + time -= ((*min) * INT64CONST(60000000)); + *sec = (time / INT64CONST(1000000)); + *fsec = (time - (*sec * INT64CONST(1000000))); +#else *hour = (time / 3600); time -= ((*hour) * 3600); *min = (time / 60); time -= ((*min) * 60); - *sec = JROUND(time); + *sec = time; + *fsec = JROUND(time - *sec); +#endif return; } /* dt2time() */ @@ -558,13 +773,18 @@ dt2time(Timestamp jd, int *hour, int *min, double *sec) * local time zone. If out of this range, leave as GMT. - tgl 97/05/27 */ int -timestamp2tm(Timestamp dt, int *tzp, struct tm * tm, double *fsec, char **tzn) +timestamp2tm(Timestamp dt, int *tzp, struct tm *tm, fsec_t *fsec, char **tzn) { - double date, - date0, - time, - sec; - time_t utime; +#ifdef HAVE_INT64_TIMESTAMP + int date, + date0; + int64 time; +#else + double date, + date0; + double time; +#endif + time_t utime; #if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE) struct tm *tx; @@ -578,9 +798,22 @@ timestamp2tm(Timestamp dt, int *tzp, struct tm * tm, double *fsec, char **tzn) * later bypass any calls which adjust the tm fields. */ if (HasCTZSet && (tzp != NULL)) +#ifdef HAVE_INT64_TIMESTAMP + dt -= (CTimeZone * INT64CONST(1000000)); +#else dt -= CTimeZone; +#endif time = dt; +#ifdef HAVE_INT64_TIMESTAMP + TMODULO(time, date, INT64CONST(86400000000)); + + if (time < INT64CONST(0)) + { + time += INT64CONST(86400000000); + date -= 1; + } +#else TMODULO(time, date, 86400e0); if (time < 0) @@ -588,6 +821,7 @@ timestamp2tm(Timestamp dt, int *tzp, struct tm * tm, double *fsec, char **tzn) time += 86400; date -= 1; } +#endif /* Julian day routine does not work for negative Julian days */ if (date < -date0) @@ -597,10 +831,7 @@ timestamp2tm(Timestamp dt, int *tzp, struct tm * tm, double *fsec, char **tzn) date += date0; j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); - dt2time(time, &tm->tm_hour, &tm->tm_min, &sec); - - *fsec = JROUND(sec); - TMODULO(*fsec, tm->tm_sec, 1e0); + dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec); if (tzp != NULL) { @@ -626,7 +857,12 @@ timestamp2tm(Timestamp dt, int *tzp, struct tm * tm, double *fsec, char **tzn) */ else if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday)) { - utime = (dt + (date0 - date2j(1970, 1, 1)) * 86400); +#ifdef HAVE_INT64_TIMESTAMP + utime = ((dt / INT64CONST(1000000)) + + ((date0 - date2j(1970, 1, 1)) * INT64CONST(86400))); +#else + utime = (dt + ((date0 - date2j(1970, 1, 1)) * 86400)); +#endif #if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE) tx = localtime(&utime); @@ -703,19 +939,27 @@ timestamp2tm(Timestamp dt, int *tzp, struct tm * tm, double *fsec, char **tzn) * Also, month is one-based, _not_ zero-based. */ int -tm2timestamp(struct tm * tm, double fsec, int *tzp, Timestamp *result) +tm2timestamp(struct tm * tm, fsec_t fsec, int *tzp, Timestamp *result) { - +#ifdef HAVE_INT64_TIMESTAMP + int date; + int64 time; +#else double date, time; +#endif /* Julian day routines are not correct for negative Julian days */ if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday)) return -1; date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1); - time = time2t(tm->tm_hour, tm->tm_min, (tm->tm_sec + fsec)); - *result = (date * 86400 + time); + time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec); +#ifdef HAVE_INT64_TIMESTAMP + *result = ((date * INT64CONST(86400000000)) + time); +#else + *result = ((date * 86400) + time); +#endif if (tzp != NULL) *result = dt2local(*result, -(*tzp)); @@ -727,9 +971,13 @@ tm2timestamp(struct tm * tm, double fsec, int *tzp, Timestamp *result) * Convert a interval data type to a tm structure. */ int -interval2tm(Interval span, struct tm * tm, float8 *fsec) +interval2tm(Interval span, struct tm * tm, fsec_t *fsec) { +#ifdef HAVE_INT64_TIMESTAMP + int64 time; +#else double time; +#endif if (span.month != 0) { @@ -743,45 +991,71 @@ interval2tm(Interval span, struct tm * tm, float8 *fsec) tm->tm_mon = 0; } -#ifdef ROUND_ALL - time = JROUND(span.time); -#else time = span.time; -#endif +#ifdef HAVE_INT64_TIMESTAMP + tm->tm_mday = (time / INT64CONST(86400000000)); + time -= (tm->tm_mday * INT64CONST(86400000000)); + tm->tm_hour = (time / INT64CONST(3600000000)); + time -= (tm->tm_hour * INT64CONST(3600000000)); + tm->tm_min = (time / INT64CONST(60000000)); + time -= (tm->tm_min * INT64CONST(60000000)); + tm->tm_sec = (time / INT64CONST(1000000)); + *fsec = (time - (tm->tm_sec * INT64CONST(1000000))); +#else TMODULO(time, tm->tm_mday, 86400e0); TMODULO(time, tm->tm_hour, 3600e0); TMODULO(time, tm->tm_min, 60e0); TMODULO(time, tm->tm_sec, 1e0); *fsec = time; +#endif return 0; } /* interval2tm() */ int -tm2interval(struct tm * tm, double fsec, Interval *span) +tm2interval(struct tm * tm, fsec_t fsec, Interval *span) { span->month = ((tm->tm_year * 12) + tm->tm_mon); +#ifdef HAVE_INT64_TIMESTAMP + span->time = ((((((((tm->tm_mday * INT64CONST(24)) + + tm->tm_hour) * INT64CONST(60)) + + tm->tm_min) * INT64CONST(60)) + + tm->tm_sec) * INT64CONST(1000000)) + fsec); +#else span->time = ((((((tm->tm_mday * 24.0) + tm->tm_hour) * 60.0) + tm->tm_min) * 60.0) + tm->tm_sec); span->time = JROUND(span->time + fsec); +#endif return 0; } /* tm2interval() */ +#ifdef HAVE_INT64_TIMESTAMP +static int64 +time2t(const int hour, const int min, const int sec, const fsec_t fsec) +{ + return ((((((hour * 60) + min) * 60) + sec) * INT64CONST(1000000)) + fsec); +} /* time2t() */ +#else static double -time2t(const int hour, const int min, const double sec) +time2t(const int hour, const int min, const int sec, const fsec_t fsec) { - return (((hour * 60) + min) * 60) + sec; + return ((((hour * 60) + min) * 60) + sec + fsec); } /* time2t() */ +#endif static Timestamp dt2local(Timestamp dt, int tz) { +#ifdef HAVE_INT64_TIMESTAMP + dt -= (tz * INT64CONST(1000000)); +#else dt -= tz; dt = JROUND(dt); +#endif return dt; } /* dt2local() */ @@ -928,15 +1202,28 @@ timestamp_cmp(PG_FUNCTION_ARGS) static int interval_cmp_internal(Interval *interval1, Interval *interval2) { +#ifdef HAVE_INT64_TIMESTAMP + int64 span1, + span2; +#else double span1, span2; +#endif span1 = interval1->time; + span2 = interval2->time; + +#ifdef HAVE_INT64_TIMESTAMP + if (interval1->month != 0) + span1 += ((interval1->month * INT64CONST(30) * INT64CONST(86400000000))); + if (interval2->month != 0) + span2 += ((interval2->month * INT64CONST(30) * INT64CONST(86400000000))); +#else if (interval1->month != 0) span1 += (interval1->month * (30.0 * 86400)); - span2 = interval2->time; if (interval2->month != 0) span2 += (interval2->month * (30.0 * 86400)); +#endif return ((span1 < span2) ? -1 : (span1 > span2) ? 1 : 0); } @@ -1017,7 +1304,7 @@ interval_hash(PG_FUNCTION_ARGS) * sizeof(Interval), so that any garbage pad bytes in the structure * won't be included in the hash! */ - return hash_any((unsigned char *) key, sizeof(double) + sizeof(int4)); + return hash_any((unsigned char *) key, sizeof(key->time) + sizeof(key->month)); } /* overlaps_timestamp() --- implements the SQL92 OVERLAPS operator. @@ -1195,7 +1482,11 @@ timestamp_mi(PG_FUNCTION_ARGS) result->time = 0; } else +#ifdef HAVE_INT64_TIMESTAMP + result->time = (dt1 - dt2); +#else result->time = JROUND(dt1 - dt2); +#endif result->month = 0; @@ -1220,14 +1511,16 @@ timestamp_pl_span(PG_FUNCTION_ARGS) Timestamp result; if (TIMESTAMP_NOT_FINITE(timestamp)) + { result = timestamp; + } else { if (span->month != 0) { struct tm tt, *tm = &tt; - double fsec; + fsec_t fsec; if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) == 0) { @@ -1262,12 +1555,7 @@ timestamp_pl_span(PG_FUNCTION_ARGS) } } -#ifdef ROUND_ALL - timestamp = JROUND(timestamp + span->time); -#else timestamp += span->time; -#endif - result = timestamp; } @@ -1316,7 +1604,7 @@ timestamptz_pl_span(PG_FUNCTION_ARGS) { struct tm tt, *tm = &tt; - double fsec; + fsec_t fsec; if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) == 0) { @@ -1349,12 +1637,7 @@ timestamptz_pl_span(PG_FUNCTION_ARGS) } } -#ifdef ROUND_ALL - timestamp = JROUND(timestamp + span->time); -#else timestamp += span->time; -#endif - result = timestamp; } @@ -1398,17 +1681,29 @@ interval_smaller(PG_FUNCTION_ARGS) Interval *interval1 = PG_GETARG_INTERVAL_P(0); Interval *interval2 = PG_GETARG_INTERVAL_P(1); Interval *result; +#ifdef HAVE_INT64_TIMESTAMP + int64 span1, + span2; +#else double span1, span2; +#endif result = (Interval *) palloc(sizeof(Interval)); span1 = interval1->time; + span2 = interval2->time; +#ifdef HAVE_INT64_TIMESTAMP + if (interval1->month != 0) + span1 += ((interval1->month * INT64CONST(30) * INT64CONST(86400000000))); + if (interval2->month != 0) + span2 += ((interval2->month * INT64CONST(30) * INT64CONST(86400000000))); +#else if (interval1->month != 0) span1 += (interval1->month * (30.0 * 86400)); - span2 = interval2->time; if (interval2->month != 0) span2 += (interval2->month * (30.0 * 86400)); +#endif if (span2 < span1) { @@ -1430,17 +1725,29 @@ interval_larger(PG_FUNCTION_ARGS) Interval *interval1 = PG_GETARG_INTERVAL_P(0); Interval *interval2 = PG_GETARG_INTERVAL_P(1); Interval *result; +#ifdef HAVE_INT64_TIMESTAMP + int64 span1, + span2; +#else double span1, span2; +#endif result = (Interval *) palloc(sizeof(Interval)); span1 = interval1->time; + span2 = interval2->time; +#ifdef HAVE_INT64_TIMESTAMP + if (interval1->month != 0) + span1 += ((interval1->month * INT64CONST(30) * INT64CONST(86400000000))); + if (interval2->month != 0) + span2 += ((interval2->month * INT64CONST(30) * INT64CONST(86400000000))); +#else if (interval1->month != 0) span1 += (interval1->month * (30.0 * 86400)); - span2 = interval2->time; if (interval2->month != 0) span2 += (interval2->month * (30.0 * 86400)); +#endif if (span2 > span1) { @@ -1466,7 +1773,11 @@ interval_pl(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); result->month = (span1->month + span2->month); +#ifdef HAVE_INT64_TIMESTAMP + result->time = (span1->time + span2->time); +#else result->time = JROUND(span1->time + span2->time); +#endif PG_RETURN_INTERVAL_P(result); } @@ -1481,7 +1792,11 @@ interval_mi(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); result->month = (span1->month - span2->month); +#ifdef HAVE_INT64_TIMESTAMP + result->time = (span1->time - span2->time); +#else result->time = JROUND(span1->time - span2->time); +#endif PG_RETURN_INTERVAL_P(result); } @@ -1492,15 +1807,26 @@ interval_mul(PG_FUNCTION_ARGS) Interval *span1 = PG_GETARG_INTERVAL_P(0); float8 factor = PG_GETARG_FLOAT8(1); Interval *result; +#ifdef HAVE_INT64_TIMESTAMP + int64 months; +#else double months; +#endif result = (Interval *) palloc(sizeof(Interval)); months = (span1->month * factor); +#ifdef HAVE_INT64_TIMESTAMP + result->month = months; + result->time = (span1->time * factor); + result->time += ((months - result->month) * INT64CONST(30) + * INT64CONST(86400000000)); +#else result->month = rint(months); result->time = JROUND(span1->time * factor); /* evaluate fractional months as 30 days */ result->time += JROUND((months - result->month) * 30 * 86400); +#endif PG_RETURN_INTERVAL_P(result); } @@ -1518,21 +1844,31 @@ mul_d_interval(PG_FUNCTION_ARGS) Datum interval_div(PG_FUNCTION_ARGS) { - Interval *span1 = PG_GETARG_INTERVAL_P(0); + Interval *span = PG_GETARG_INTERVAL_P(0); float8 factor = PG_GETARG_FLOAT8(1); Interval *result; +#ifndef HAVE_INT64_TIMESTAMP double months; +#endif result = (Interval *) palloc(sizeof(Interval)); if (factor == 0.0) elog(ERROR, "interval_div: divide by 0.0 error"); - months = (span1->month / factor); +#ifdef HAVE_INT64_TIMESTAMP + result->month = (span->month / factor); + result->time = (span->time / factor); + /* evaluate fractional months as 30 days */ + result->time += (((span->month - (result->month * factor)) + * INT64CONST(30) * INT64CONST(86400000000)) / factor); +#else + months = (span->month / factor); result->month = rint(months); - result->time = JROUND(span1->time / factor); + result->time = JROUND(span->time / factor); /* evaluate fractional months as 30 days */ result->time += JROUND((months - result->month) * 30 * 86400); +#endif PG_RETURN_INTERVAL_P(result); } @@ -1641,7 +1977,7 @@ timestamp_age(PG_FUNCTION_ARGS) Timestamp dt1 = PG_GETARG_TIMESTAMP(0); Timestamp dt2 = PG_GETARG_TIMESTAMP(1); Interval *result; - double fsec, + fsec_t fsec, fsec1, fsec2; struct tm tt, @@ -1750,7 +2086,7 @@ timestamptz_age(PG_FUNCTION_ARGS) TimestampTz dt1 = PG_GETARG_TIMESTAMP(0); TimestampTz dt2 = PG_GETARG_TIMESTAMP(1); Interval *result; - double fsec, + fsec_t fsec, fsec1, fsec2; struct tm tt, @@ -2033,7 +2369,7 @@ timestamp_trunc(PG_FUNCTION_ARGS) char *up, *lp, lowunits[MAXDATELEN + 1]; - double fsec; + fsec_t fsec; struct tm tt, *tm = &tt; @@ -2079,11 +2415,17 @@ timestamp_trunc(PG_FUNCTION_ARGS) break; case DTK_MILLISEC: +#ifdef HAVE_INT64_TIMESTAMP + fsec = ((fsec / 1000) * 1000); +#else fsec = rint(fsec * 1000) / 1000; +#endif break; case DTK_MICROSEC: +#ifndef HAVE_INT64_TIMESTAMP fsec = rint(fsec * 1000000) / 1000000; +#endif break; default: @@ -2119,7 +2461,7 @@ timestamptz_trunc(PG_FUNCTION_ARGS) char *up, *lp, lowunits[MAXDATELEN + 1]; - double fsec; + fsec_t fsec; char *tzn; struct tm tt, *tm = &tt; @@ -2166,10 +2508,16 @@ timestamptz_trunc(PG_FUNCTION_ARGS) break; case DTK_MILLISEC: +#ifdef HAVE_INT64_TIMESTAMP + fsec = ((fsec / 1000) * 1000); +#else fsec = rint(fsec * 1000) / 1000; +#endif break; case DTK_MICROSEC: +#ifndef HAVE_INT64_TIMESTAMP fsec = rint(fsec * 1000000) / 1000000; +#endif break; default: @@ -2206,7 +2554,7 @@ interval_trunc(PG_FUNCTION_ARGS) char *up, *lp, lowunits[MAXDATELEN + 1]; - double fsec; + fsec_t fsec; struct tm tt, *tm = &tt; @@ -2253,11 +2601,16 @@ interval_trunc(PG_FUNCTION_ARGS) break; case DTK_MILLISEC: +#ifdef HAVE_INT64_TIMESTAMP + fsec = ((fsec / 1000) * 1000); +#else fsec = rint(fsec * 1000) / 1000; +#endif break; - case DTK_MICROSEC: +#ifndef HAVE_INT64_TIMESTAMP fsec = rint(fsec * 1000000) / 1000000; +#endif break; default: @@ -2380,7 +2733,7 @@ timestamp_part(PG_FUNCTION_ARGS) char *up, *lp, lowunits[MAXDATELEN + 1]; - double fsec; + fsec_t fsec; struct tm tt, *tm = &tt; @@ -2410,15 +2763,27 @@ timestamp_part(PG_FUNCTION_ARGS) switch (val) { case DTK_MICROSEC: +#ifdef HAVE_INT64_TIMESTAMP + result = ((tm->tm_sec * 1000000e0) + fsec); +#else result = (tm->tm_sec + fsec) * 1000000; +#endif break; case DTK_MILLISEC: +#ifdef HAVE_INT64_TIMESTAMP + result = ((tm->tm_sec * 1000e0) + (fsec / 1000e0)); +#else result = (tm->tm_sec + fsec) * 1000; +#endif break; case DTK_SECOND: +#ifdef HAVE_INT64_TIMESTAMP + result = (tm->tm_sec + (fsec / 1000000e0)); +#else result = (tm->tm_sec + fsec); +#endif break; case DTK_MINUTE: @@ -2463,7 +2828,13 @@ timestamp_part(PG_FUNCTION_ARGS) case DTK_JULIAN: result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday); - result += (((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec) / 86400e0); +#ifdef HAVE_INT64_TIMESTAMP + result += (((((tm->tm_hour * 60) + tm->tm_min) * 60) + + tm->tm_sec + (fsec / 1000000e0)) / 86400e0); +#else + result += (((((tm->tm_hour * 60) + tm->tm_min) * 60) + + tm->tm_sec + fsec) / 86400e0); +#endif break; case DTK_TZ: @@ -2479,7 +2850,7 @@ timestamp_part(PG_FUNCTION_ARGS) switch (val) { case DTK_EPOCH: - result = timestamp - SetEpochTimestamp(); + result = ((timestamp - SetEpochTimestamp()) / 1000000e0); break; case DTK_DOW: @@ -2529,7 +2900,7 @@ timestamptz_part(PG_FUNCTION_ARGS) *lp, lowunits[MAXDATELEN + 1]; double dummy; - double fsec; + fsec_t fsec; char *tzn; struct tm tt, *tm = &tt; @@ -2574,15 +2945,27 @@ timestamptz_part(PG_FUNCTION_ARGS) break; case DTK_MICROSEC: +#ifdef HAVE_INT64_TIMESTAMP + result = ((tm->tm_sec * 1000000e0) + fsec); +#else result = (tm->tm_sec + fsec) * 1000000; +#endif break; case DTK_MILLISEC: +#ifdef HAVE_INT64_TIMESTAMP + result = ((tm->tm_sec * 1000e0) + (fsec / 1000e0)); +#else result = (tm->tm_sec + fsec) * 1000; +#endif break; case DTK_SECOND: +#ifdef HAVE_INT64_TIMESTAMP + result = (tm->tm_sec + (fsec / 1000000e0)); +#else result = (tm->tm_sec + fsec); +#endif break; case DTK_MINUTE: @@ -2627,7 +3010,13 @@ timestamptz_part(PG_FUNCTION_ARGS) case DTK_JULIAN: result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday); - result += (((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec) / 86400e0); +#ifdef HAVE_INT64_TIMESTAMP + result += (((((tm->tm_hour * 60) + tm->tm_min) * 60) + + tm->tm_sec + (fsec / 1000000e0)) / 86400e0); +#else + result += (((((tm->tm_hour * 60) + tm->tm_min) * 60) + + tm->tm_sec + fsec) / 86400e0); +#endif break; default: @@ -2641,7 +3030,11 @@ timestamptz_part(PG_FUNCTION_ARGS) switch (val) { case DTK_EPOCH: +#ifdef HAVE_INT64_TIMESTAMP + result = ((timestamp - SetEpochTimestamp()) / 100000e0); +#else result = timestamp - SetEpochTimestamp(); +#endif break; case DTK_DOW: @@ -2689,7 +3082,7 @@ interval_part(PG_FUNCTION_ARGS) char *up, *lp, lowunits[MAXDATELEN + 1]; - double fsec; + fsec_t fsec; struct tm tt, *tm = &tt; @@ -2713,17 +3106,29 @@ interval_part(PG_FUNCTION_ARGS) { switch (val) { - case DTK_MICROSEC: - result = ((tm->tm_sec + fsec) * 1000000); - break; + case DTK_MICROSEC: +#ifdef HAVE_INT64_TIMESTAMP + result = ((tm->tm_sec * 1000000e0) + fsec); +#else + result = (tm->tm_sec + fsec) * 1000000; +#endif + break; - case DTK_MILLISEC: - result = ((tm->tm_sec + fsec) * 1000); - break; + case DTK_MILLISEC: +#ifdef HAVE_INT64_TIMESTAMP + result = ((tm->tm_sec * 1000e0) + (fsec / 1000e0)); +#else + result = (tm->tm_sec + fsec) * 1000; +#endif + break; - case DTK_SECOND: - result = (tm->tm_sec + fsec); - break; + case DTK_SECOND: +#ifdef HAVE_INT64_TIMESTAMP + result = (tm->tm_sec + (fsec / 1000000e0)); +#else + result = (tm->tm_sec + fsec); +#endif + break; case DTK_MINUTE: result = tm->tm_min; @@ -2778,11 +3183,15 @@ interval_part(PG_FUNCTION_ARGS) } else if ((type == RESERV) && (val == DTK_EPOCH)) { +#ifdef HAVE_INT64_TIMESTAMP + result = (interval->time / 1000000e0); +#else result = interval->time; +#endif if (interval->month != 0) { result += ((365.25 * 86400) * (interval->month / 12)); - result += ((30 * 86400) * (interval->month % 12)); + result += ((30.0 * 86400) * (interval->month % 12)); } } else @@ -2799,6 +3208,8 @@ interval_part(PG_FUNCTION_ARGS) /* timestamp_zone() * Encode timestamp type with specified time zone. + * Returns timestamp with time zone, with the input + * rotated from local time to the specified zone. */ Datum timestamp_zone(PG_FUNCTION_ARGS) @@ -2832,8 +3243,9 @@ timestamp_zone(PG_FUNCTION_ARGS) if ((type == TZ) || (type == DTZ)) { - tz = val * 60; - result = timestamp - tz; + tz = -(val * 60); + + result = dt2local(timestamp, tz); } else { @@ -2846,7 +3258,6 @@ timestamp_zone(PG_FUNCTION_ARGS) /* timestamp_izone() * Encode timestamp type with specified time interval as time zone. - * Require ISO-formatted result, since character-string time zone is not available. */ Datum timestamp_izone(PG_FUNCTION_ARGS) @@ -2864,8 +3275,13 @@ timestamp_izone(PG_FUNCTION_ARGS) DatumGetCString(DirectFunctionCall1(interval_out, PointerGetDatum(zone)))); - tz = -(zone->time); - result = timestamp - tz; +#ifdef HAVE_INT64_TIMESTAMP + tz = (zone->time / INT64CONST(1000000)); +#else + tz = (zone->time); +#endif + + result = dt2local(timestamp, tz); PG_RETURN_TIMESTAMPTZ(result); } /* timestamp_izone() */ @@ -2880,7 +3296,7 @@ timestamp_timestamptz(PG_FUNCTION_ARGS) TimestampTz result; struct tm tt, *tm = &tt; - double fsec; + fsec_t fsec; int tz; if (TIMESTAMP_NOT_FINITE(timestamp)) @@ -2909,7 +3325,7 @@ timestamptz_timestamp(PG_FUNCTION_ARGS) Timestamp result; struct tm tt, *tm = &tt; - double fsec; + fsec_t fsec; char *tzn; int tz; @@ -2928,15 +3344,16 @@ timestamptz_timestamp(PG_FUNCTION_ARGS) } /* timestamptz_zone() - * Encode timestamp with time zone type with specified time zone. + * Evaluate timestamp with time zone type at the specified time zone. + * Returns a timestamp without time zone. */ Datum timestamptz_zone(PG_FUNCTION_ARGS) { text *zone = PG_GETARG_TEXT_P(0); TimestampTz timestamp = PG_GETARG_TIMESTAMP(1); - text *result; - TimestampTz dt; + Timestamp result; + int tz; int type, val; @@ -2944,13 +3361,6 @@ timestamptz_zone(PG_FUNCTION_ARGS) char *up, *lp, lowzone[MAXDATELEN + 1]; - char *tzn, - upzone[MAXDATELEN + 1]; - double fsec; - struct tm tt, - *tm = &tt; - char buf[MAXDATELEN + 1]; - int len; if (VARSIZE(zone) - VARHDRSZ > MAXDATELEN) elog(ERROR, "Time zone '%s' not recognized", @@ -2965,86 +3375,50 @@ timestamptz_zone(PG_FUNCTION_ARGS) type = DecodeSpecial(0, lowzone, &val); if (TIMESTAMP_NOT_FINITE(timestamp)) - PG_RETURN_TEXT_P(pstrdup("")); + PG_RETURN_NULL(); if ((type == TZ) || (type == DTZ)) { - tm->tm_isdst = ((type == DTZ) ? 1 : 0); tz = val * 60; - dt = dt2local(timestamp, tz); - - if (timestamp2tm(dt, NULL, tm, &fsec, NULL) != 0) - elog(ERROR, "Unable to decode TIMESTAMP WITH TIME ZONE" - "\n\ttimestamptz_zone() internal coding error"); - - up = upzone; - lp = lowzone; - for (i = 0; *lp != '\0'; i++) - *up++ = toupper((unsigned char) *lp++); - *up = '\0'; - - tzn = upzone; - EncodeDateTime(tm, fsec, &tz, &tzn, DateStyle, buf); - - len = (strlen(buf) + VARHDRSZ); - - result = palloc(len); - - VARATT_SIZEP(result) = len; - memmove(VARDATA(result), buf, (len - VARHDRSZ)); + result = dt2local(timestamp, tz); } else { elog(ERROR, "Time zone '%s' not recognized", lowzone); - PG_RETURN_TEXT_P(pstrdup("")); + PG_RETURN_NULL(); } - PG_RETURN_TEXT_P(result); + PG_RETURN_TIMESTAMP(result); } /* timestamptz_zone() */ /* timestamptz_izone() * Encode timestamp with time zone type with specified time interval as time zone. - * Require ISO-formatted result, since character-string time zone is not available. + * Returns a timestamp without time zone. */ Datum timestamptz_izone(PG_FUNCTION_ARGS) { Interval *zone = PG_GETARG_INTERVAL_P(0); TimestampTz timestamp = PG_GETARG_TIMESTAMP(1); - text *result; - TimestampTz dt; + Timestamp result; int tz; - char *tzn = ""; - double fsec; - struct tm tt, - *tm = &tt; - char buf[MAXDATELEN + 1]; - int len; if (TIMESTAMP_NOT_FINITE(timestamp)) - PG_RETURN_TEXT_P(pstrdup("")); + PG_RETURN_NULL(); if (zone->month != 0) elog(ERROR, "INTERVAL time zone '%s' not legal (month specified)", DatumGetCString(DirectFunctionCall1(interval_out, PointerGetDatum(zone)))); - tm->tm_isdst = -1; +#ifdef HAVE_INT64_TIMESTAMP + tz = -(zone->time / INT64CONST(1000000)); +#else tz = -(zone->time); +#endif - dt = dt2local(timestamp, tz); - - if (timestamp2tm(dt, NULL, tm, &fsec, NULL) != 0) - elog(ERROR, "Unable to decode TIMESTAMP WITH TIME ZONE" - "\n\ttimestamptz_izone() internal coding error"); - - EncodeDateTime(tm, fsec, &tz, &tzn, USE_ISO_DATES, buf); - len = (strlen(buf) + VARHDRSZ); + result = dt2local(timestamp, tz); - result = palloc(len); - VARATT_SIZEP(result) = len; - memmove(VARDATA(result), buf, (len - VARHDRSZ)); - - PG_RETURN_TEXT_P(result); + PG_RETURN_TIMESTAMP(result); } /* timestamptz_izone() */ |