From e511d878f3bbc205cd260a79740e646eea3c1cd3 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 29 Mar 2016 17:09:21 -0400 Subject: Allow to_timestamp(float8) to convert float infinity to timestamp infinity. With the original SQL-function implementation, such cases failed because we don't support infinite intervals. Converting the function to C lets us bypass the interval representation, which should be a bit faster as well as more flexible. Vitaly Burovoy, reviewed by Anastasia Lubennikova --- src/backend/utils/adt/timestamp.c | 58 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) (limited to 'src/backend/utils/adt/timestamp.c') diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index c9e5270a9eb..b9c26b68544 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -737,6 +737,64 @@ make_timestamptz_at_timezone(PG_FUNCTION_ARGS) PG_RETURN_TIMESTAMPTZ(result); } +/* + * to_timestamp(double precision) + * Convert UNIX epoch to timestamptz. + */ +Datum +float8_timestamptz(PG_FUNCTION_ARGS) +{ + float8 seconds = PG_GETARG_FLOAT8(0); + TimestampTz result; + + /* Deal with NaN and infinite inputs ... */ + if (isnan(seconds)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp cannot be NaN"))); + + if (isinf(seconds)) + { + if (seconds < 0) + TIMESTAMP_NOBEGIN(result); + else + TIMESTAMP_NOEND(result); + } + else + { + /* Out of range? */ + if (seconds < + (float8) SECS_PER_DAY * (DATETIME_MIN_JULIAN - UNIX_EPOCH_JDATE)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range: \"%g\"", seconds))); + + if (seconds >= + (float8) SECS_PER_DAY * (TIMESTAMP_END_JULIAN - UNIX_EPOCH_JDATE)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range: \"%g\"", seconds))); + + /* Convert UNIX epoch to Postgres epoch */ + seconds -= ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY); + +#ifdef HAVE_INT64_TIMESTAMP + result = seconds * USECS_PER_SEC; +#else + result = seconds; +#endif + + /* Recheck in case roundoff produces something just out of range */ + if (!IS_VALID_TIMESTAMP(result)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range: \"%g\"", + PG_GETARG_FLOAT8(0)))); + } + + PG_RETURN_TIMESTAMP(result); +} + /* timestamptz_out() * Convert a timestamp to external form. */ -- cgit v1.2.3