diff options
author | Thomas G. Lockhart <lockhart@fourpalms.org> | 2001-09-28 08:09:14 +0000 |
---|---|---|
committer | Thomas G. Lockhart <lockhart@fourpalms.org> | 2001-09-28 08:09:14 +0000 |
commit | 6f58115dddfa8ca63004c4784f57ef660422861d (patch) | |
tree | 71816e03286e53113ec4b6de337f0b345028a314 /src/backend/utils/adt/timestamp.c | |
parent | 1f075a32ee28004251f508f50a4325944801da10 (diff) | |
download | postgresql-6f58115dddfa8ca63004c4784f57ef660422861d.tar.gz postgresql-6f58115dddfa8ca63004c4784f57ef660422861d.zip |
Measure the current transaction time to milliseconds.
Define a new function, GetCurrentTransactionStartTimeUsec() to get the time
to this precision.
Allow now() and timestamp 'now' to use this higher precision result so
we now have fractional seconds in this "constant".
Add timestamp without time zone type.
Move previous timestamp type to timestamp with time zone.
Accept another ISO variant for date/time values: yyyy-mm-ddThh:mm:ss
(note the "T" separating the day from hours information).
Remove 'current' from date/time types; convert to 'now' in input.
Separate time and timetz regression tests.
Separate timestamp and timestamptz regression test.
Diffstat (limited to 'src/backend/utils/adt/timestamp.c')
-rw-r--r-- | src/backend/utils/adt/timestamp.c | 1370 |
1 files changed, 972 insertions, 398 deletions
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 824faae152b..1bd42689659 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.50 2001/09/06 03:22:42 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.51 2001/09/28 08:09:11 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -32,7 +32,7 @@ static double time2t(const int hour, const int min, const double sec); static int EncodeSpecialTimestamp(Timestamp dt, char *str); static Timestamp dt2local(Timestamp dt, int timezone); -static void dt2time(Timestamp dt, int *hour, int *min, double *sec); + /***************************************************************************** * USER I/O ROUTINES * @@ -63,16 +63,12 @@ timestamp_in(PG_FUNCTION_ARGS) switch (dtype) { case DTK_DATE: - if (tm2timestamp(tm, fsec, &tz, &result) != 0) + if (tm2timestamp(tm, fsec, NULL, &result) != 0) elog(ERROR, "Timestamp out of range '%s'", str); break; case DTK_EPOCH: - TIMESTAMP_EPOCH(result); - break; - - case DTK_CURRENT: - TIMESTAMP_CURRENT(result); + result = SetEpochTimestamp(); break; case DTK_LATE: @@ -84,12 +80,13 @@ timestamp_in(PG_FUNCTION_ARGS) break; case DTK_INVALID: - TIMESTAMP_INVALID(result); + elog(ERROR, "Timestamp '%s' no longer supported", str); + TIMESTAMP_NOEND(result); break; default: - elog(ERROR, "Internal coding error, can't input timestamp '%s'", str); - TIMESTAMP_INVALID(result); /* keep compiler quiet */ + elog(ERROR, "Timestamp '%s' not parsed; internal coding error", str); + TIMESTAMP_NOEND(result); } PG_RETURN_TIMESTAMP(result); @@ -103,6 +100,86 @@ timestamp_out(PG_FUNCTION_ARGS) { Timestamp dt = PG_GETARG_TIMESTAMP(0); char *result; + struct tm tt, + *tm = &tt; + double fsec; + char *tzn = NULL; + char buf[MAXDATELEN + 1]; + + if (TIMESTAMP_NOT_FINITE(dt)) + EncodeSpecialTimestamp(dt, buf); + else if (timestamp2tm(dt, NULL, tm, &fsec, NULL) == 0) + EncodeDateTime(tm, fsec, NULL, &tzn, DateStyle, buf); + else + elog(ERROR, "Unable to format timestamp; internal coding error"); + + result = pstrdup(buf); + PG_RETURN_CSTRING(result); +} + + +/* timestamptz_in() + * Convert a string to internal form. + */ +Datum +timestamptz_in(PG_FUNCTION_ARGS) +{ + char *str = PG_GETARG_CSTRING(0); + TimestampTz result; + double fsec; + struct tm tt, + *tm = &tt; + int tz; + int dtype; + int nf; + char *field[MAXDATEFIELDS]; + int ftype[MAXDATEFIELDS]; + char lowstr[MAXDATELEN + 1]; + + if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0) + || (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz) != 0)) + elog(ERROR, "Bad timestamp external representation '%s'", str); + + switch (dtype) + { + case DTK_DATE: + if (tm2timestamp(tm, fsec, &tz, &result) != 0) + elog(ERROR, "Timestamp out of range '%s'", str); + break; + + case DTK_EPOCH: + result = SetEpochTimestamp(); + break; + + case DTK_LATE: + TIMESTAMP_NOEND(result); + break; + + case DTK_EARLY: + TIMESTAMP_NOBEGIN(result); + break; + + case DTK_INVALID: + elog(ERROR, "Timestamp with time zone '%s' no longer supported", str); + TIMESTAMP_NOEND(result); + break; + + default: + elog(ERROR, "Timestamp with time zone '%s' not parsed; internal coding error", str); + TIMESTAMP_NOEND(result); + } + + PG_RETURN_TIMESTAMPTZ(result); +} + +/* timestamptz_out() + * Convert a timestamp to external form. + */ +Datum +timestamptz_out(PG_FUNCTION_ARGS) +{ + TimestampTz dt = PG_GETARG_TIMESTAMP(0); + char *result; int tz; struct tm tt, *tm = &tt; @@ -110,12 +187,12 @@ timestamp_out(PG_FUNCTION_ARGS) char *tzn; char buf[MAXDATELEN + 1]; - if (TIMESTAMP_IS_RESERVED(dt)) + if (TIMESTAMP_NOT_FINITE(dt)) EncodeSpecialTimestamp(dt, buf); else if (timestamp2tm(dt, &tz, tm, &fsec, &tzn) == 0) EncodeDateTime(tm, fsec, &tz, &tzn, DateStyle, buf); else - EncodeSpecialTimestamp(DT_INVALID, buf); + elog(ERROR, "Unable to format timestamp with time zone; internal coding error"); result = pstrdup(buf); PG_RETURN_CSTRING(result); @@ -132,7 +209,7 @@ Datum interval_in(PG_FUNCTION_ARGS) { char *str = PG_GETARG_CSTRING(0); - Interval *span; + Interval *result; double fsec; struct tm tt, *tm = &tt; @@ -154,25 +231,24 @@ interval_in(PG_FUNCTION_ARGS) || (DecodeDateDelta(field, ftype, nf, &dtype, tm, &fsec) != 0)) elog(ERROR, "Bad interval external representation '%s'", str); - span = (Interval *) palloc(sizeof(Interval)); + result = (Interval *) palloc(sizeof(Interval)); switch (dtype) { case DTK_DELTA: - if (tm2interval(tm, fsec, span) != 0) - { -#if NOT_USED - INTERVAL_INVALID(span); -#endif + if (tm2interval(tm, fsec, result) != 0) elog(ERROR, "Bad interval external representation '%s'", str); - } + break; + + case DTK_INVALID: + elog(ERROR, "Interval '%s' no longer supported", str); break; default: - elog(ERROR, "Internal coding error, can't input interval '%s'", str); + elog(ERROR, "Interval '%s' not parsed; internal coding error", str); } - PG_RETURN_INTERVAL_P(span); + PG_RETURN_INTERVAL_P(result); } /* interval_out() @@ -189,10 +265,10 @@ interval_out(PG_FUNCTION_ARGS) char buf[MAXDATELEN + 1]; if (interval2tm(*span, tm, &fsec) != 0) - PG_RETURN_NULL(); + elog(ERROR, "Unable to encode interval; internal coding error"); if (EncodeTimeSpan(tm, fsec, DateStyle, buf) != 0) - elog(ERROR, "Unable to format interval"); + elog(ERROR, "Unable to format interval; internal coding error"); result = pstrdup(buf); PG_RETURN_CSTRING(result); @@ -205,40 +281,31 @@ interval_out(PG_FUNCTION_ARGS) static int EncodeSpecialTimestamp(Timestamp dt, char *str) { - if (TIMESTAMP_IS_RESERVED(dt)) - { - if (TIMESTAMP_IS_INVALID(dt)) - strcpy(str, INVALID); - else if (TIMESTAMP_IS_NOBEGIN(dt)) - strcpy(str, EARLY); - else if (TIMESTAMP_IS_NOEND(dt)) - strcpy(str, LATE); - else if (TIMESTAMP_IS_CURRENT(dt)) - strcpy(str, DCURRENT); - else if (TIMESTAMP_IS_EPOCH(dt)) - strcpy(str, EPOCH); - else - strcpy(str, INVALID); - return TRUE; - } + if (TIMESTAMP_IS_NOBEGIN(dt)) + strcpy(str, EARLY); + else if (TIMESTAMP_IS_NOEND(dt)) + strcpy(str, LATE); + else + return FALSE; - return FALSE; + return TRUE; } /* EncodeSpecialTimestamp() */ Datum now(PG_FUNCTION_ARGS) { - Timestamp result; + TimestampTz result; AbsoluteTime sec; + int usec; - sec = GetCurrentTransactionStartTime(); + sec = GetCurrentTransactionStartTimeUsec(&usec); - result = (sec - ((date2j(2000, 1, 1) - date2j(1970, 1, 1)) * 86400)); + result = (sec + (usec * 1.0e-6) - ((date2j(2000, 1, 1) - date2j(1970, 1, 1)) * 86400)); - PG_RETURN_TIMESTAMP(result); + PG_RETURN_TIMESTAMPTZ(result); } -static void +void dt2time(Timestamp jd, int *hour, int *min, double *sec) { double time; @@ -485,9 +552,7 @@ timestamp_finite(PG_FUNCTION_ARGS) Datum interval_finite(PG_FUNCTION_ARGS) { - Interval *interval = PG_GETARG_INTERVAL_P(0); - - PG_RETURN_BOOL(!INTERVAL_NOT_FINITE(*interval)); + PG_RETURN_BOOL(true); } @@ -495,7 +560,7 @@ interval_finite(PG_FUNCTION_ARGS) * Relational operators for timestamp. *---------------------------------------------------------*/ -static void +void GetEpochTime(struct tm * tm) { struct tm *t0; @@ -518,24 +583,17 @@ GetEpochTime(struct tm * tm) } /* GetEpochTime() */ Timestamp -SetTimestamp(Timestamp dt) +SetEpochTimestamp(void) { - struct tm tt; + Timestamp dt; + struct tm tt, + *tm = &tt; - if (TIMESTAMP_IS_CURRENT(dt)) - { - GetCurrentTime(&tt); - tm2timestamp(&tt, 0, NULL, &dt); - dt = dt2local(dt, -CTimeZone); - } - else - { /* if (TIMESTAMP_IS_EPOCH(dt1)) */ - GetEpochTime(&tt); - tm2timestamp(&tt, 0, NULL, &dt); - } + GetEpochTime(tm); + tm2timestamp(tm, 0, NULL, &dt); return dt; -} /* SetTimestamp() */ +} /* SetEpochTimestamp() */ /* * timestamp_relop - is timestamp1 relop timestamp2 @@ -545,19 +603,7 @@ SetTimestamp(Timestamp dt) static int timestamp_cmp_internal(Timestamp dt1, Timestamp dt2) { - if (TIMESTAMP_IS_INVALID(dt1)) - return (TIMESTAMP_IS_INVALID(dt2) ? 0 : 1); - else if (TIMESTAMP_IS_INVALID(dt2)) - return -1; - else - { - if (TIMESTAMP_IS_RELATIVE(dt1)) - dt1 = SetTimestamp(dt1); - if (TIMESTAMP_IS_RELATIVE(dt2)) - dt2 = SetTimestamp(dt2); - - return ((dt1 < dt2) ? -1 : ((dt1 > dt2) ? 1 : 0)); - } + return ((dt1 < dt2) ? -1 : ((dt1 > dt2) ? 1 : 0)); } Datum @@ -632,24 +678,17 @@ timestamp_cmp(PG_FUNCTION_ARGS) static int interval_cmp_internal(Interval *interval1, Interval *interval2) { - if (INTERVAL_IS_INVALID(*interval1)) - return (INTERVAL_IS_INVALID(*interval2) ? 0 : 1); - else if (INTERVAL_IS_INVALID(*interval2)) - return -1; - else - { - double span1, - span2; + double span1, + span2; - span1 = interval1->time; - if (interval1->month != 0) - span1 += (interval1->month * (30.0 * 86400)); - span2 = interval2->time; - if (interval2->month != 0) - span2 += (interval2->month * (30.0 * 86400)); + span1 = interval1->time; + if (interval1->month != 0) + span1 += (interval1->month * (30.0 * 86400)); + span2 = interval2->time; + if (interval2->month != 0) + span2 += (interval2->month * (30.0 * 86400)); - return ((span1 < span2) ? -1 : (span1 > span2) ? 1 : 0); - } + return ((span1 < span2) ? -1 : (span1 > span2) ? 1 : 0); } Datum @@ -866,6 +905,9 @@ overlaps_timestamp(PG_FUNCTION_ARGS) * "Arithmetic" operators on date/times. *---------------------------------------------------------*/ +/* We are currently sharing some code between timestamp and timestamptz. + * The comparison functions are among them. - thomas 2001-09-25 + */ Datum timestamp_smaller(PG_FUNCTION_ARGS) { @@ -873,17 +915,7 @@ timestamp_smaller(PG_FUNCTION_ARGS) Timestamp dt2 = PG_GETARG_TIMESTAMP(1); Timestamp result; - if (TIMESTAMP_IS_RELATIVE(dt1)) - dt1 = SetTimestamp(dt1); - if (TIMESTAMP_IS_RELATIVE(dt2)) - dt2 = SetTimestamp(dt2); - - if (TIMESTAMP_IS_INVALID(dt1)) - result = dt2; - else if (TIMESTAMP_IS_INVALID(dt2)) - result = dt1; - else - result = ((dt2 < dt1) ? dt2 : dt1); + result = ((dt2 < dt1) ? dt2 : dt1); PG_RETURN_TIMESTAMP(result); } @@ -895,17 +927,7 @@ timestamp_larger(PG_FUNCTION_ARGS) Timestamp dt2 = PG_GETARG_TIMESTAMP(1); Timestamp result; - if (TIMESTAMP_IS_RELATIVE(dt1)) - dt1 = SetTimestamp(dt1); - if (TIMESTAMP_IS_RELATIVE(dt2)) - dt2 = SetTimestamp(dt2); - - if (TIMESTAMP_IS_INVALID(dt1)) - result = dt2; - else if (TIMESTAMP_IS_INVALID(dt2)) - result = dt1; - else - result = ((dt2 > dt1) ? dt2 : dt1); + result = ((dt2 > dt1) ? dt2 : dt1); PG_RETURN_TIMESTAMP(result); } @@ -920,16 +942,14 @@ timestamp_mi(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); - if (TIMESTAMP_IS_RELATIVE(dt1)) - dt1 = SetTimestamp(dt1); - if (TIMESTAMP_IS_RELATIVE(dt2)) - dt2 = SetTimestamp(dt2); - - if (TIMESTAMP_IS_INVALID(dt1) - || TIMESTAMP_IS_INVALID(dt2)) - TIMESTAMP_INVALID(result->time); + if (TIMESTAMP_NOT_FINITE(dt1) || TIMESTAMP_NOT_FINITE(dt2)) + { + elog(ERROR, "Unable to subtract non-finite timestamps"); + result->time = 0; + } else result->time = JROUND(dt1 - dt2); + result->month = 0; PG_RETURN_INTERVAL_P(result); @@ -951,25 +971,111 @@ timestamp_pl_span(PG_FUNCTION_ARGS) Timestamp timestamp = PG_GETARG_TIMESTAMP(0); Interval *span = PG_GETARG_INTERVAL_P(1); Timestamp result; - Timestamp dt; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + { + result = timestamp; + } + else + { + if (span->month != 0) + { + struct tm tt, + *tm = &tt; + double fsec; + + if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) == 0) + { + tm->tm_mon += span->month; + if (tm->tm_mon > 12) + { + tm->tm_year += ((tm->tm_mon - 1) / 12); + tm->tm_mon = (((tm->tm_mon - 1) % 12) + 1); + } + else if (tm->tm_mon < 1) + { + tm->tm_year += ((tm->tm_mon / 12) - 1); + tm->tm_mon = ((tm->tm_mon % 12) + 12); + } + + /* adjust for end of month boundary problems... */ + if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]) + tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]); + + if (tm2timestamp(tm, fsec, NULL, ×tamp) != 0) + { + elog(ERROR, "Unable to add timestamp and interval" + "\n\ttimestamp_pl_span() internal error encoding timestamp"); + PG_RETURN_NULL(); + } + } + else + { + elog(ERROR, "Unable to add timestamp and interval" + "\n\ttimestamp_pl_span() internal error decoding timestamp"); + PG_RETURN_NULL(); + } + } + +#ifdef ROUND_ALL + timestamp = JROUND(timestamp + span->time); +#else + timestamp += span->time; +#endif + + result = timestamp; + } + + PG_RETURN_TIMESTAMP(result); +} + +Datum +timestamp_mi_span(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + Interval *span = PG_GETARG_INTERVAL_P(1); + Interval tspan; + + tspan.month = -span->month; + tspan.time = -span->time; + + return DirectFunctionCall2(timestamp_pl_span, + TimestampGetDatum(timestamp), + PointerGetDatum(&tspan)); +} + + +/* timestamp_pl_span() + * Add a interval to a timestamp with time zone data type. + * Note that interval has provisions for qualitative year/month + * units, so try to do the right thing with them. + * To add a month, increment the month, and use the same day of month. + * Then, if the next month has fewer days, set the day of month + * to the last day of month. + * Lastly, add in the "quantitative time". + */ +Datum +timestamptz_pl_span(PG_FUNCTION_ARGS) +{ + TimestampTz timestamp = PG_GETARG_TIMESTAMP(0); + Interval *span = PG_GETARG_INTERVAL_P(1); + TimestampTz result; int tz; char *tzn; if (TIMESTAMP_NOT_FINITE(timestamp)) + { result = timestamp; - else if (INTERVAL_IS_INVALID(*span)) - TIMESTAMP_INVALID(result); + } else { - dt = (TIMESTAMP_IS_RELATIVE(timestamp) ? SetTimestamp(timestamp) : timestamp); - if (span->month != 0) { struct tm tt, *tm = &tt; double fsec; - if (timestamp2tm(dt, &tz, tm, &fsec, &tzn) == 0) + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) == 0) { tm->tm_mon += span->month; if (tm->tm_mon > 12) @@ -989,30 +1095,33 @@ timestamp_pl_span(PG_FUNCTION_ARGS) tz = DetermineLocalTimeZone(tm); - if (tm2timestamp(tm, fsec, &tz, &dt) != 0) - elog(ERROR, "Unable to add timestamp and interval"); - + if (tm2timestamp(tm, fsec, &tz, ×tamp) != 0) + elog(ERROR, "Unable to add timestamp and interval" + "\n\ttimestamptz_pl_span() internal error encoding timestamp"); } else - TIMESTAMP_INVALID(dt); + { + elog(ERROR, "Unable to add timestamp and interval" + "\n\ttimestamptz_pl_span() internal error decoding timestamp"); + } } #ifdef ROUND_ALL - dt = JROUND(dt + span->time); + timestamp = JROUND(timestamp + span->time); #else - dt += span->time; + timestamp += span->time; #endif - result = dt; + result = timestamp; } PG_RETURN_TIMESTAMP(result); } Datum -timestamp_mi_span(PG_FUNCTION_ARGS) +timestamptz_mi_span(PG_FUNCTION_ARGS) { - Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + TimestampTz timestamp = PG_GETARG_TIMESTAMP(0); Interval *span = PG_GETARG_INTERVAL_P(1); Interval tspan; @@ -1051,36 +1160,23 @@ interval_smaller(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); - if (INTERVAL_IS_INVALID(*interval1)) + span1 = interval1->time; + if (interval1->month != 0) + span1 += (interval1->month * (30.0 * 86400)); + span2 = interval2->time; + if (interval2->month != 0) + span2 += (interval2->month * (30.0 * 86400)); + + if (span2 < span1) { result->time = interval2->time; result->month = interval2->month; } - else if (INTERVAL_IS_INVALID(*interval2)) + else { result->time = interval1->time; result->month = interval1->month; } - else - { - span1 = interval1->time; - if (interval1->month != 0) - span1 += (interval1->month * (30.0 * 86400)); - span2 = interval2->time; - if (interval2->month != 0) - span2 += (interval2->month * (30.0 * 86400)); - - if (span2 < span1) - { - result->time = interval2->time; - result->month = interval2->month; - } - else - { - result->time = interval1->time; - result->month = interval1->month; - } - } PG_RETURN_INTERVAL_P(result); } @@ -1096,36 +1192,23 @@ interval_larger(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); - if (INTERVAL_IS_INVALID(*interval1)) + span1 = interval1->time; + if (interval1->month != 0) + span1 += (interval1->month * (30.0 * 86400)); + span2 = interval2->time; + if (interval2->month != 0) + span2 += (interval2->month * (30.0 * 86400)); + + if (span2 > span1) { result->time = interval2->time; result->month = interval2->month; } - else if (INTERVAL_IS_INVALID(*interval2)) + else { result->time = interval1->time; result->month = interval1->month; } - else - { - span1 = interval1->time; - if (interval1->month != 0) - span1 += (interval1->month * (30.0 * 86400)); - span2 = interval2->time; - if (interval2->month != 0) - span2 += (interval2->month * (30.0 * 86400)); - - if (span2 > span1) - { - result->time = interval2->time; - result->month = interval2->month; - } - else - { - result->time = interval1->time; - result->month = interval1->month; - } - } PG_RETURN_INTERVAL_P(result); } @@ -1200,7 +1283,7 @@ interval_div(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); if (factor == 0.0) - elog(ERROR, "interval_div: divide by 0.0 error"); + elog(ERROR, "interval_div: divide by 0.0 error"); months = (span1->month / factor); result->month = rint(months); @@ -1321,16 +1404,117 @@ timestamp_age(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); - if (TIMESTAMP_IS_RELATIVE(dt1)) - dt1 = SetTimestamp(dt1); - if (TIMESTAMP_IS_RELATIVE(dt2)) - dt2 = SetTimestamp(dt2); + if ((timestamp2tm(dt1, NULL, tm1, &fsec1, NULL) == 0) + && (timestamp2tm(dt2, NULL, tm2, &fsec2, NULL) == 0)) + { + fsec = (fsec1 - fsec2); + tm->tm_sec = (tm1->tm_sec - tm2->tm_sec); + tm->tm_min = (tm1->tm_min - tm2->tm_min); + tm->tm_hour = (tm1->tm_hour - tm2->tm_hour); + tm->tm_mday = (tm1->tm_mday - tm2->tm_mday); + tm->tm_mon = (tm1->tm_mon - tm2->tm_mon); + tm->tm_year = (tm1->tm_year - tm2->tm_year); + + /* flip sign if necessary... */ + if (dt1 < dt2) + { + fsec = -fsec; + tm->tm_sec = -tm->tm_sec; + tm->tm_min = -tm->tm_min; + tm->tm_hour = -tm->tm_hour; + tm->tm_mday = -tm->tm_mday; + tm->tm_mon = -tm->tm_mon; + tm->tm_year = -tm->tm_year; + } + + if (tm->tm_sec < 0) + { + tm->tm_sec += 60; + tm->tm_min--; + } + + if (tm->tm_min < 0) + { + tm->tm_min += 60; + tm->tm_hour--; + } + + if (tm->tm_hour < 0) + { + tm->tm_hour += 24; + tm->tm_mday--; + } + + if (tm->tm_mday < 0) + { + if (dt1 < dt2) + { + tm->tm_mday += day_tab[isleap(tm1->tm_year)][tm1->tm_mon - 1]; + tm->tm_mon--; + } + else + { + tm->tm_mday += day_tab[isleap(tm2->tm_year)][tm2->tm_mon - 1]; + tm->tm_mon--; + } + } + + if (tm->tm_mon < 0) + { + tm->tm_mon += 12; + tm->tm_year--; + } - if (TIMESTAMP_IS_INVALID(dt1) - || TIMESTAMP_IS_INVALID(dt2)) - TIMESTAMP_INVALID(result->time); - else if ((timestamp2tm(dt1, NULL, tm1, &fsec1, NULL) == 0) - && (timestamp2tm(dt2, NULL, tm2, &fsec2, NULL) == 0)) + /* recover sign if necessary... */ + if (dt1 < dt2) + { + fsec = -fsec; + tm->tm_sec = -tm->tm_sec; + tm->tm_min = -tm->tm_min; + tm->tm_hour = -tm->tm_hour; + tm->tm_mday = -tm->tm_mday; + tm->tm_mon = -tm->tm_mon; + tm->tm_year = -tm->tm_year; + } + + if (tm2interval(tm, fsec, result) != 0) + elog(ERROR, "Unable to encode interval" + "\n\ttimestamp_age() internal coding error"); + } + else + elog(ERROR, "Unable to decode timestamp" + "\n\ttimestamp_age() internal coding error"); + + PG_RETURN_INTERVAL_P(result); +} + + +/* timestamptz_age() + * Calculate time difference while retaining year/month fields. + * Note that this does not result in an accurate absolute time span + * since year and month are out of context once the arithmetic + * is done. + */ +Datum +timestamptz_age(PG_FUNCTION_ARGS) +{ + TimestampTz dt1 = PG_GETARG_TIMESTAMP(0); + TimestampTz dt2 = PG_GETARG_TIMESTAMP(1); + Interval *result; + double fsec, + fsec1, + fsec2; + struct tm tt, + *tm = &tt; + struct tm tt1, + *tm1 = &tt1; + struct tm tt2, + *tm2 = &tt2; + + result = (Interval *) palloc(sizeof(Interval)); + + if ((timestamp2tm(dt1, NULL, tm1, &fsec1, NULL) == 0) + && (timestamp2tm(dt2, NULL, tm2, &fsec2, NULL) == 0)) { fsec = (fsec1 - fsec2); tm->tm_sec = (tm1->tm_sec - tm2->tm_sec); @@ -1472,6 +1656,60 @@ text_timestamp(PG_FUNCTION_ARGS) } +/* timestamptz_text() + * Convert timestamp with time zone to text data type. + */ +Datum +timestamptz_text(PG_FUNCTION_ARGS) +{ + /* Input is a Timestamp, but may as well leave it in Datum form */ + Datum timestamp = PG_GETARG_DATUM(0); + text *result; + char *str; + int len; + + str = DatumGetCString(DirectFunctionCall1(timestamptz_out, timestamp)); + + len = (strlen(str) + VARHDRSZ); + + result = palloc(len); + + VARATT_SIZEP(result) = len; + memmove(VARDATA(result), str, (len - VARHDRSZ)); + + pfree(str); + + PG_RETURN_TEXT_P(result); +} + +/* text_timestamptz() + * Convert text string to timestamp with time zone. + * Text type is not null terminated, so use temporary string + * then call the standard input routine. + */ +Datum +text_timestamptz(PG_FUNCTION_ARGS) +{ + text *str = PG_GETARG_TEXT_P(0); + int i; + char *sp, + *dp, + dstr[MAXDATELEN + 1]; + + if (VARSIZE(str) - VARHDRSZ > MAXDATELEN) + elog(ERROR, "Bad timestamp with time zone external representation (too long)"); + + sp = VARDATA(str); + dp = dstr; + for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++) + *dp++ = *sp++; + *dp = '\0'; + + return DirectFunctionCall1(timestamptz_in, + CStringGetDatum(dstr)); +} + + /* interval_text() * Convert interval to text data type. */ @@ -1484,7 +1722,7 @@ interval_text(PG_FUNCTION_ARGS) int len; str = DatumGetCString(DirectFunctionCall1(interval_out, - IntervalPGetDatum(interval))); + IntervalPGetDatum(interval))); len = (strlen(str) + VARHDRSZ); @@ -1525,7 +1763,7 @@ text_interval(PG_FUNCTION_ARGS) } /* timestamp_trunc() - * Extract specified field from timestamp. + * Truncate timestamp to specified units. */ Datum timestamp_trunc(PG_FUNCTION_ARGS) @@ -1533,8 +1771,6 @@ timestamp_trunc(PG_FUNCTION_ARGS) text *units = PG_GETARG_TEXT_P(0); Timestamp timestamp = PG_GETARG_TIMESTAMP(1); Timestamp result; - Timestamp dt; - int tz; int type, val; int i; @@ -1542,7 +1778,6 @@ timestamp_trunc(PG_FUNCTION_ARGS) *lp, lowunits[MAXDATELEN + 1]; double fsec; - char *tzn; struct tm tt, *tm = &tt; @@ -1559,70 +1794,146 @@ timestamp_trunc(PG_FUNCTION_ARGS) type = DecodeUnits(0, lowunits, &val); if (TIMESTAMP_NOT_FINITE(timestamp)) - PG_RETURN_NULL(); + PG_RETURN_TIMESTAMP(timestamp); + + if ((type == UNITS) && (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) == 0)) + { + switch (val) + { + case DTK_MILLENNIUM: + tm->tm_year = (tm->tm_year / 1000) * 1000; + case DTK_CENTURY: + tm->tm_year = (tm->tm_year / 100) * 100; + case DTK_DECADE: + tm->tm_year = (tm->tm_year / 10) * 10; + case DTK_YEAR: + tm->tm_mon = 1; + case DTK_QUARTER: + tm->tm_mon = (3 * (tm->tm_mon / 4)) + 1; + case DTK_MONTH: + tm->tm_mday = 1; + case DTK_DAY: + tm->tm_hour = 0; + case DTK_HOUR: + tm->tm_min = 0; + case DTK_MINUTE: + tm->tm_sec = 0; + case DTK_SECOND: + fsec = 0; + break; + + case DTK_MILLISEC: + fsec = rint(fsec * 1000) / 1000; + break; + + case DTK_MICROSEC: + fsec = rint(fsec * 1000000) / 1000000; + break; + + default: + elog(ERROR, "Timestamp units '%s' not supported", lowunits); + result = 0; + } + + if (tm2timestamp(tm, fsec, NULL, &result) != 0) + elog(ERROR, "Unable to truncate timestamp to '%s'", lowunits); + } else { - dt = (TIMESTAMP_IS_RELATIVE(timestamp) ? SetTimestamp(timestamp) : timestamp); + elog(ERROR, "Timestamp units '%s' not recognized", lowunits); + result = 0; + } - if ((type == UNITS) && (timestamp2tm(dt, &tz, tm, &fsec, &tzn) == 0)) - { - switch (val) - { - case DTK_MILLENNIUM: - tm->tm_year = (tm->tm_year / 1000) * 1000; - case DTK_CENTURY: - tm->tm_year = (tm->tm_year / 100) * 100; - case DTK_DECADE: - tm->tm_year = (tm->tm_year / 10) * 10; - case DTK_YEAR: - tm->tm_mon = 1; - case DTK_QUARTER: - tm->tm_mon = (3 * (tm->tm_mon / 4)) + 1; - case DTK_MONTH: - tm->tm_mday = 1; - case DTK_DAY: - tm->tm_hour = 0; - case DTK_HOUR: - tm->tm_min = 0; - case DTK_MINUTE: - tm->tm_sec = 0; - case DTK_SECOND: - fsec = 0; - break; + PG_RETURN_TIMESTAMP(result); +} - case DTK_MILLISEC: - fsec = rint(fsec * 1000) / 1000; - break; +/* timestamptz_trunc() + * Truncate timestamp to specified units. + */ +Datum +timestamptz_trunc(PG_FUNCTION_ARGS) +{ + text *units = PG_GETARG_TEXT_P(0); + TimestampTz timestamp = PG_GETARG_TIMESTAMP(1); + TimestampTz result; + int tz; + int type, + val; + int i; + char *up, + *lp, + lowunits[MAXDATELEN + 1]; + double fsec; + char *tzn; + struct tm tt, + *tm = &tt; - case DTK_MICROSEC: - fsec = rint(fsec * 1000000) / 1000000; - break; + if (VARSIZE(units) - VARHDRSZ > MAXDATELEN) + elog(ERROR, "Interval units '%s' not recognized", + DatumGetCString(DirectFunctionCall1(textout, + PointerGetDatum(units)))); + up = VARDATA(units); + lp = lowunits; + for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++) + *lp++ = tolower((unsigned char) *up++); + *lp = '\0'; - default: - elog(ERROR, "Timestamp units '%s' not supported", lowunits); - result = 0; - } + type = DecodeUnits(0, lowunits, &val); - tz = DetermineLocalTimeZone(tm); + if (TIMESTAMP_NOT_FINITE(timestamp)) + PG_RETURN_TIMESTAMPTZ(timestamp); - if (tm2timestamp(tm, fsec, &tz, &result) != 0) - elog(ERROR, "Unable to truncate timestamp to '%s'", lowunits); - } -#if NOT_USED - else if ((type == RESERV) && (val == DTK_EPOCH)) - { - TIMESTAMP_EPOCH(result); - result = dt - SetTimestamp(result); - } -#endif - else + if ((type == UNITS) && (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) == 0)) + { + switch (val) { - elog(ERROR, "Timestamp units '%s' not recognized", lowunits); - result = 0; + case DTK_MILLENNIUM: + tm->tm_year = (tm->tm_year / 1000) * 1000; + case DTK_CENTURY: + tm->tm_year = (tm->tm_year / 100) * 100; + case DTK_DECADE: + tm->tm_year = (tm->tm_year / 10) * 10; + case DTK_YEAR: + tm->tm_mon = 1; + case DTK_QUARTER: + tm->tm_mon = (3 * (tm->tm_mon / 4)) + 1; + case DTK_MONTH: + tm->tm_mday = 1; + case DTK_DAY: + tm->tm_hour = 0; + case DTK_HOUR: + tm->tm_min = 0; + case DTK_MINUTE: + tm->tm_sec = 0; + case DTK_SECOND: + fsec = 0; + break; + + case DTK_MILLISEC: + fsec = rint(fsec * 1000) / 1000; + break; + + case DTK_MICROSEC: + fsec = rint(fsec * 1000000) / 1000000; + break; + + default: + elog(ERROR, "Timestamp units '%s' not supported", lowunits); + result = 0; } + + tz = DetermineLocalTimeZone(tm); + + if (tm2timestamp(tm, fsec, &tz, &result) != 0) + elog(ERROR, "Unable to truncate timestamp to '%s'", lowunits); + } + else + { + elog(ERROR, "Timestamp units '%s' not recognized", lowunits); + PG_RETURN_NULL(); } - PG_RETURN_TIMESTAMP(result); + PG_RETURN_TIMESTAMPTZ(result); } /* interval_trunc() @@ -1658,14 +1969,7 @@ interval_trunc(PG_FUNCTION_ARGS) type = DecodeUnits(0, lowunits, &val); - if (INTERVAL_IS_INVALID(*interval)) - { -#if NOT_USED - elog(ERROR, "Interval is not finite"); -#endif - PG_RETURN_NULL(); - } - else if (type == UNITS) + if (type == UNITS) { if (interval2tm(*interval, tm, &fsec) == 0) { @@ -1703,7 +2007,6 @@ interval_trunc(PG_FUNCTION_ARGS) default: elog(ERROR, "Interval units '%s' not supported", lowunits); - PG_RETURN_NULL(); } if (tm2interval(tm, fsec, result) != 0) @@ -1712,28 +2015,16 @@ interval_trunc(PG_FUNCTION_ARGS) } else { - elog(NOTICE, "Interval out of range"); - PG_RETURN_NULL(); + elog(NOTICE, "Unable to decode interval; internal coding error"); + *result = *interval; } - } -#if NOT_USED - else if ((type == RESERV) && (val == DTK_EPOCH)) - { - *result = interval->time; - if (interval->month != 0) - { - *result += ((365.25 * 86400) * (interval->month / 12)); - *result += ((30 * 86400) * (interval->month % 12)); - } - } -#endif else { elog(ERROR, "Interval units '%s' not recognized", DatumGetCString(DirectFunctionCall1(textout, - PointerGetDatum(units)))); - PG_RETURN_NULL(); + PointerGetDatum(units)))); + *result = *interval; } PG_RETURN_INTERVAL_P(result); @@ -1828,7 +2119,6 @@ timestamp_part(PG_FUNCTION_ARGS) text *units = PG_GETARG_TEXT_P(0); Timestamp timestamp = PG_GETARG_TIMESTAMP(1); float8 result; - Timestamp dt; int tz; int type, val; @@ -1845,7 +2135,7 @@ timestamp_part(PG_FUNCTION_ARGS) if (VARSIZE(units) - VARHDRSZ > MAXDATELEN) elog(ERROR, "Interval units '%s' not recognized", DatumGetCString(DirectFunctionCall1(textout, - PointerGetDatum(units)))); + PointerGetDatum(units)))); up = VARDATA(units); lp = lowunits; for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++) @@ -1857,123 +2147,276 @@ timestamp_part(PG_FUNCTION_ARGS) type = DecodeSpecial(0, lowunits, &val); if (TIMESTAMP_NOT_FINITE(timestamp)) - PG_RETURN_NULL(); - else { - dt = (TIMESTAMP_IS_RELATIVE(timestamp) ? SetTimestamp(timestamp) : timestamp); + result = 0; + PG_RETURN_FLOAT8(result); + } - if ((type == UNITS) && (timestamp2tm(dt, &tz, tm, &fsec, &tzn) == 0)) + if ((type == UNITS) && (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) == 0)) + { + switch (val) { - switch (val) - { - case DTK_TZ: - result = tz; - break; - - case DTK_TZ_MINUTE: - result = tz / 60; - TMODULO(result, dummy, 60e0); - break; - - case DTK_TZ_HOUR: - dummy = tz; - TMODULO(dummy, result, 3600e0); - break; - - case DTK_MICROSEC: - result = (fsec * 1000000); - break; - - case DTK_MILLISEC: - result = (fsec * 1000); - break; + case DTK_TZ: + result = tz; + break; + + case DTK_TZ_MINUTE: + result = tz / 60; + TMODULO(result, dummy, 60e0); + break; + + case DTK_TZ_HOUR: + dummy = tz; + TMODULO(dummy, result, 3600e0); + break; + + case DTK_MICROSEC: + result = (fsec * 1000000); + break; + + case DTK_MILLISEC: + result = (fsec * 1000); + break; + + case DTK_SECOND: + result = (tm->tm_sec + fsec); + break; + + case DTK_MINUTE: + result = tm->tm_min; + break; + + case DTK_HOUR: + result = tm->tm_hour; + break; + + case DTK_DAY: + result = tm->tm_mday; + break; + + case DTK_MONTH: + result = tm->tm_mon; + break; + + case DTK_QUARTER: + result = ((tm->tm_mon - 1) / 3) + 1; + break; + + case DTK_WEEK: + result = (float8) date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday); + break; + + case DTK_YEAR: + result = tm->tm_year; + break; + + case DTK_DECADE: + result = (tm->tm_year / 10); + break; + + case DTK_CENTURY: + result = (tm->tm_year / 100); + break; + + case DTK_MILLENNIUM: + result = (tm->tm_year / 1000); + break; + + default: + elog(ERROR, "Timestamp units '%s' not supported", lowunits); + result = 0; + } - case DTK_SECOND: - result = (tm->tm_sec + fsec); - break; + } + else if (type == RESERV) + { + switch (val) + { + case DTK_EPOCH: + result = timestamp - SetEpochTimestamp(); + break; - case DTK_MINUTE: - result = tm->tm_min; - break; + case DTK_DOW: + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0) + elog(ERROR, "Unable to encode timestamp"); - case DTK_HOUR: - result = tm->tm_hour; - break; + result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)); + break; - case DTK_DAY: - result = tm->tm_mday; - break; + case DTK_DOY: + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0) + elog(ERROR, "Unable to encode timestamp"); - case DTK_MONTH: - result = tm->tm_mon; - break; + result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + - date2j(tm->tm_year, 1, 1) + 1); + break; - case DTK_QUARTER: - result = ((tm->tm_mon - 1) / 3) + 1; - break; + default: + elog(ERROR, "Timestamp units '%s' not supported", lowunits); + result = 0; + } - case DTK_WEEK: - result = (float8) date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday); - break; + } + else + { + elog(ERROR, "Timestamp units '%s' not recognized", lowunits); + result = 0; + } - case DTK_YEAR: - result = tm->tm_year; - break; + PG_RETURN_FLOAT8(result); +} - case DTK_DECADE: - result = (tm->tm_year / 10); - break; +/* timestamptz_part() + * Extract specified field from timestamp with time zone. + */ +Datum +timestamptz_part(PG_FUNCTION_ARGS) +{ + text *units = PG_GETARG_TEXT_P(0); + TimestampTz timestamp = PG_GETARG_TIMESTAMP(1); + float8 result; + int tz; + int type, + val; + int i; + char *up, + *lp, + lowunits[MAXDATELEN + 1]; + double dummy; + double fsec; + char *tzn; + struct tm tt, + *tm = &tt; - case DTK_CENTURY: - result = (tm->tm_year / 100); - break; + if (VARSIZE(units) - VARHDRSZ > MAXDATELEN) + elog(ERROR, "Interval units '%s' not recognized", + DatumGetCString(DirectFunctionCall1(textout, + PointerGetDatum(units)))); + up = VARDATA(units); + lp = lowunits; + for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++) + *lp++ = tolower((unsigned char) *up++); + *lp = '\0'; - case DTK_MILLENNIUM: - result = (tm->tm_year / 1000); - break; + type = DecodeUnits(0, lowunits, &val); + if (type == IGNORE) + type = DecodeSpecial(0, lowunits, &val); - default: - elog(ERROR, "Timestamp units '%s' not supported", lowunits); - result = 0; - } + if (TIMESTAMP_NOT_FINITE(timestamp)) + { + result = 0; + PG_RETURN_FLOAT8(result); + } - } - else if (type == RESERV) + if ((type == UNITS) && (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) == 0)) + { + switch (val) { - switch (val) - { - case DTK_EPOCH: - TIMESTAMP_EPOCH(result); - result = dt - SetTimestamp(result); - break; + case DTK_TZ: + result = tz; + break; + + case DTK_TZ_MINUTE: + result = tz / 60; + TMODULO(result, dummy, 60e0); + break; + + case DTK_TZ_HOUR: + dummy = tz; + TMODULO(dummy, result, 3600e0); + break; + + case DTK_MICROSEC: + result = (fsec * 1000000); + break; + + case DTK_MILLISEC: + result = (fsec * 1000); + break; + + case DTK_SECOND: + result = (tm->tm_sec + fsec); + break; + + case DTK_MINUTE: + result = tm->tm_min; + break; + + case DTK_HOUR: + result = tm->tm_hour; + break; + + case DTK_DAY: + result = tm->tm_mday; + break; + + case DTK_MONTH: + result = tm->tm_mon; + break; + + case DTK_QUARTER: + result = ((tm->tm_mon - 1) / 3) + 1; + break; + + case DTK_WEEK: + result = (float8) date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday); + break; + + case DTK_YEAR: + result = tm->tm_year; + break; + + case DTK_DECADE: + result = (tm->tm_year / 10); + break; + + case DTK_CENTURY: + result = (tm->tm_year / 100); + break; + + case DTK_MILLENNIUM: + result = (tm->tm_year / 1000); + break; + + default: + elog(ERROR, "Timestamp with time zone units '%s' not supported", lowunits); + result = 0; + } - case DTK_DOW: - if (timestamp2tm(dt, &tz, tm, &fsec, &tzn) != 0) - elog(ERROR, "Unable to encode timestamp"); + } + else if (type == RESERV) + { + switch (val) + { + case DTK_EPOCH: + result = timestamp - SetEpochTimestamp(); + break; - result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)); - break; + case DTK_DOW: + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0) + elog(ERROR, "Unable to encode timestamp with time zone"); - case DTK_DOY: - if (timestamp2tm(dt, &tz, tm, &fsec, &tzn) != 0) - elog(ERROR, "Unable to encode timestamp"); + result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)); + break; - result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - - date2j(tm->tm_year, 1, 1) + 1); - break; + case DTK_DOY: + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0) + elog(ERROR, "Unable to encode timestamp with time zone"); - default: - elog(ERROR, "Timestamp units '%s' not supported", lowunits); - result = 0; - } + result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + - date2j(tm->tm_year, 1, 1) + 1); + break; - } - else - { - elog(ERROR, "Timestamp units '%s' not recognized", lowunits); - result = 0; + default: + elog(ERROR, "Timestamp with time zone units '%s' not supported", lowunits); + result = 0; } } + else + { + elog(ERROR, "Timestamp with time zone units '%s' not recognized", lowunits); + result = 0; + } PG_RETURN_FLOAT8(result); } @@ -2012,14 +2455,7 @@ interval_part(PG_FUNCTION_ARGS) if (type == IGNORE) type = DecodeSpecial(0, lowunits, &val); - if (INTERVAL_IS_INVALID(*interval)) - { -#if NOT_USED - elog(ERROR, "Interval is not finite"); -#endif - result = 0; - } - else if (type == UNITS) + if (type == UNITS) { if (interval2tm(*interval, tm, &fsec) == 0) { @@ -2074,7 +2510,7 @@ interval_part(PG_FUNCTION_ARGS) break; default: - elog(ERROR, "Interval units '%s' not yet supported", + elog(ERROR, "Interval units '%s' not supported", DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(units)))); result = 0; @@ -2083,7 +2519,8 @@ interval_part(PG_FUNCTION_ARGS) } else { - elog(NOTICE, "Interval out of range"); + elog(NOTICE, "Unable to decode interval" + "\n\tinterval_part() internal coding error"); result = 0; } } @@ -2116,8 +2553,142 @@ timestamp_zone(PG_FUNCTION_ARGS) { text *zone = PG_GETARG_TEXT_P(0); Timestamp timestamp = PG_GETARG_TIMESTAMP(1); + TimestampTz result; + int tz; + int type, + val; + int i; + char *up, + *lp, + lowzone[MAXDATELEN + 1]; + + if (VARSIZE(zone) - VARHDRSZ > MAXDATELEN) + elog(ERROR, "Time zone '%s' not recognized", + DatumGetCString(DirectFunctionCall1(textout, + PointerGetDatum(zone)))); + + if (TIMESTAMP_NOT_FINITE(timestamp)) + PG_RETURN_TIMESTAMPTZ(timestamp); + + up = VARDATA(zone); + lp = lowzone; + for (i = 0; i < (VARSIZE(zone) - VARHDRSZ); i++) + *lp++ = tolower((unsigned char) *up++); + *lp = '\0'; + + type = DecodeSpecial(0, lowzone, &val); + + if ((type == TZ) || (type == DTZ)) + { + tz = val * 60; + result = timestamp - tz; + } + else + { + elog(ERROR, "Time zone '%s' not recognized", lowzone); + PG_RETURN_NULL(); + } + + PG_RETURN_TIMESTAMPTZ(result); +} /* timestamp_zone() */ + +/* 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) +{ + Interval *zone = PG_GETARG_INTERVAL_P(0); + Timestamp timestamp = PG_GETARG_TIMESTAMP(1); + TimestampTz result; + int tz; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + PG_RETURN_TIMESTAMPTZ(timestamp); + + if (zone->month != 0) + elog(ERROR, "INTERVAL time zone '%s' not legal (month specified)", + DatumGetCString(DirectFunctionCall1(interval_out, + PointerGetDatum(zone)))); + + tz = -(zone->time); + result = timestamp - tz; + + PG_RETURN_TIMESTAMPTZ(result); +} /* timestamp_izone() */ + +/* timestamp_timestamptz() + * Convert local timestamp to timestamp at GMT + */ +Datum +timestamp_timestamptz(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + TimestampTz result; + struct tm tt, + *tm = &tt; + double fsec; + int tz; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + { + result = timestamp; + } + else + { + if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) != 0) + elog(ERROR, "Unable to convert timestamp to timestamp with time zone (tm)"); + + tz = DetermineLocalTimeZone(tm); + + if (tm2timestamp(tm, fsec, &tz, &result) != 0) + elog(ERROR, "Unable to convert timestamp to timestamp with time zone"); + } + + PG_RETURN_TIMESTAMPTZ(result); +} + +/* timestamptz_timestamp() + * Convert timestamp at GMT to local timestamp + */ +Datum +timestamptz_timestamp(PG_FUNCTION_ARGS) +{ + TimestampTz timestamp = PG_GETARG_TIMESTAMP(0); + Timestamp result; + struct tm tt, + *tm = &tt; + double fsec; + char *tzn; + int tz; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + { + result = timestamp; + } + else + { + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0) + elog(ERROR, "Unable to convert timestamp with time zone to timestamp (tm)"); + + if (tm2timestamp(tm, fsec, NULL, &result) != 0) + elog(ERROR, "Unable to convert timestamp with time zone to timestamp"); + } + + PG_RETURN_TIMESTAMP(result); +} + +/* timestamptz_zone() + * Encode timestamp with time zone type with specified time zone. + */ +Datum +timestamptz_zone(PG_FUNCTION_ARGS) +{ + text *zone = PG_GETARG_TEXT_P(0); + TimestampTz timestamp = PG_GETARG_TIMESTAMP(1); text *result; - Timestamp dt; + TimestampTz dt; int tz; int type, val; @@ -2146,17 +2717,18 @@ timestamp_zone(PG_FUNCTION_ARGS) type = DecodeSpecial(0, lowzone, &val); if (TIMESTAMP_NOT_FINITE(timestamp)) - PG_RETURN_NULL(); - else if ((type == TZ) || (type == DTZ)) + PG_RETURN_TEXT_P(pstrdup("")); + + if ((type == TZ) || (type == DTZ)) { tm->tm_isdst = ((type == DTZ) ? 1 : 0); tz = val * 60; - dt = (TIMESTAMP_IS_RELATIVE(timestamp) ? SetTimestamp(timestamp) : timestamp); - dt = dt2local(dt, tz); + dt = dt2local(timestamp, tz); if (timestamp2tm(dt, NULL, tm, &fsec, NULL) != 0) - elog(ERROR, "Timestamp not legal"); + elog(ERROR, "Unable to decode timestamp" + "\n\ttimestamp_zone() internal coding error"); up = upzone; lp = lowzone; @@ -2177,23 +2749,23 @@ timestamp_zone(PG_FUNCTION_ARGS) else { elog(ERROR, "Time zone '%s' not recognized", lowzone); - result = NULL; + PG_RETURN_TEXT_P(pstrdup("")); } PG_RETURN_TEXT_P(result); -} /* timestamp_zone() */ +} /* timestamptz_zone() */ -/* timestamp_izone() - * Encode timestamp type with specified time interval as time 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. */ Datum -timestamp_izone(PG_FUNCTION_ARGS) +timestamptz_izone(PG_FUNCTION_ARGS) { Interval *zone = PG_GETARG_INTERVAL_P(0); - Timestamp timestamp = PG_GETARG_TIMESTAMP(1); + TimestampTz timestamp = PG_GETARG_TIMESTAMP(1); text *result; - Timestamp dt; + TimestampTz dt; int tz; char *tzn = ""; double fsec; @@ -2203,19 +2775,21 @@ timestamp_izone(PG_FUNCTION_ARGS) int len; if (TIMESTAMP_NOT_FINITE(timestamp)) - PG_RETURN_NULL(); + PG_RETURN_TEXT_P(pstrdup("")); if (zone->month != 0) - elog(ERROR, "INTERVAL time zone not legal (month specified)"); + elog(ERROR, "INTERVAL time zone '%s' not legal (month specified)", + DatumGetCString(DirectFunctionCall1(interval_out, + PointerGetDatum(zone)))); tm->tm_isdst = -1; tz = -(zone->time); - dt = (TIMESTAMP_IS_RELATIVE(timestamp) ? SetTimestamp(timestamp) : timestamp); - dt = dt2local(dt, tz); + dt = dt2local(timestamp, tz); if (timestamp2tm(dt, NULL, tm, &fsec, NULL) != 0) - elog(ERROR, "Timestamp not legal"); + elog(ERROR, "Unable to decode timestamp" + "\n\ttimestamp_izone() internal coding error"); EncodeDateTime(tm, fsec, &tz, &tzn, USE_ISO_DATES, buf); len = (strlen(buf) + VARHDRSZ); @@ -2225,4 +2799,4 @@ timestamp_izone(PG_FUNCTION_ARGS) memmove(VARDATA(result), buf, (len - VARHDRSZ)); PG_RETURN_TEXT_P(result); -} /* timestamp_izone() */ +} /* timestamptz_izone() */ |