diff options
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() */ |