mirror of
https://github.com/postgres/postgres.git
synced 2025-06-10 09:21:54 +03:00
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
This commit is contained in:
@ -5579,15 +5579,6 @@ SELECT regexp_matches('abc01234xyz', '(?:(.*?)(\d+)(.*)){1,1}');
|
|||||||
argument is the value to be formatted and the second argument is a
|
argument is the value to be formatted and the second argument is a
|
||||||
template that defines the output or input format.
|
template that defines the output or input format.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
|
||||||
A single-argument <function>to_timestamp</function> function is also
|
|
||||||
available; it accepts a
|
|
||||||
<type>double precision</type> argument and converts from Unix epoch
|
|
||||||
(seconds since 1970-01-01 00:00:00+00) to
|
|
||||||
<type>timestamp with time zone</type>.
|
|
||||||
(<type>Integer</type> Unix epochs are implicitly cast to
|
|
||||||
<type>double precision</type>.)
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<table id="functions-formatting-table">
|
<table id="functions-formatting-table">
|
||||||
<title>Formatting Functions</title>
|
<title>Formatting Functions</title>
|
||||||
@ -5670,16 +5661,17 @@ SELECT regexp_matches('abc01234xyz', '(?:(.*?)(\d+)(.*)){1,1}');
|
|||||||
<entry>convert string to time stamp</entry>
|
<entry>convert string to time stamp</entry>
|
||||||
<entry><literal>to_timestamp('05 Dec 2000', 'DD Mon YYYY')</literal></entry>
|
<entry><literal>to_timestamp('05 Dec 2000', 'DD Mon YYYY')</literal></entry>
|
||||||
</row>
|
</row>
|
||||||
<row>
|
|
||||||
<entry><literal><function>to_timestamp(<type>double precision</type>)</function></literal></entry>
|
|
||||||
<entry><type>timestamp with time zone</type></entry>
|
|
||||||
<entry>convert Unix epoch to time stamp</entry>
|
|
||||||
<entry><literal>to_timestamp(1284352323)</literal></entry>
|
|
||||||
</row>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</tgroup>
|
</tgroup>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<note>
|
||||||
|
<para>
|
||||||
|
There is also a single-argument <function>to_timestamp</function>
|
||||||
|
function; see <xref linkend="functions-datetime-table">.
|
||||||
|
</para>
|
||||||
|
</note>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
In a <function>to_char</> output template string, there are certain
|
In a <function>to_char</> output template string, there are certain
|
||||||
patterns that are recognized and replaced with appropriately-formatted
|
patterns that are recognized and replaced with appropriately-formatted
|
||||||
@ -7060,8 +7052,8 @@ SELECT regexp_matches('abc01234xyz', '(?:(.*?)(\d+)(.*)){1,1}');
|
|||||||
<entry><type>timestamp with time zone</type></entry>
|
<entry><type>timestamp with time zone</type></entry>
|
||||||
<entry>
|
<entry>
|
||||||
Create timestamp with time zone from year, month, day, hour, minute
|
Create timestamp with time zone from year, month, day, hour, minute
|
||||||
and seconds fields. When <parameter>timezone</parameter> is not specified,
|
and seconds fields; if <parameter>timezone</parameter> is not
|
||||||
then current time zone is used.
|
specified, the current time zone is used
|
||||||
</entry>
|
</entry>
|
||||||
<entry><literal>make_timestamptz(2013, 7, 15, 8, 15, 23.5)</literal></entry>
|
<entry><literal>make_timestamptz(2013, 7, 15, 8, 15, 23.5)</literal></entry>
|
||||||
<entry><literal>2013-07-15 08:15:23.5+01</literal></entry>
|
<entry><literal>2013-07-15 08:15:23.5+01</literal></entry>
|
||||||
@ -7127,6 +7119,19 @@ SELECT regexp_matches('abc01234xyz', '(?:(.*?)(\d+)(.*)){1,1}');
|
|||||||
<entry></entry>
|
<entry></entry>
|
||||||
<entry></entry>
|
<entry></entry>
|
||||||
</row>
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
<indexterm>
|
||||||
|
<primary>to_timestamp</primary>
|
||||||
|
</indexterm>
|
||||||
|
<literal><function>to_timestamp(<type>double precision</type>)</function></literal>
|
||||||
|
</entry>
|
||||||
|
<entry><type>timestamp with time zone</type></entry>
|
||||||
|
<entry>Convert Unix epoch (seconds since 1970-01-01 00:00:00+00) to
|
||||||
|
timestamp</entry>
|
||||||
|
<entry><literal>to_timestamp(1284352323)</literal></entry>
|
||||||
|
<entry><literal>2010-09-13 04:32:03+00</literal></entry>
|
||||||
|
</row>
|
||||||
</tbody>
|
</tbody>
|
||||||
</tgroup>
|
</tgroup>
|
||||||
</table>
|
</table>
|
||||||
@ -7377,16 +7382,13 @@ SELECT EXTRACT(EPOCH FROM INTERVAL '5 days 3 hours');
|
|||||||
</screen>
|
</screen>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Here is how you can convert an epoch value back to a time
|
You can convert an epoch value back to a time stamp
|
||||||
stamp:
|
with <function>to_timestamp</>:
|
||||||
</para>
|
</para>
|
||||||
<screen>
|
<screen>
|
||||||
SELECT TIMESTAMP WITH TIME ZONE 'epoch' + 982384720.12 * INTERVAL '1 second';
|
SELECT to_timestamp(982384720.12);
|
||||||
|
<lineannotation>Result: </lineannotation><computeroutput>2001-02-17 04:38:40.12+00</computeroutput>
|
||||||
</screen>
|
</screen>
|
||||||
<para>
|
|
||||||
(The <function>to_timestamp</> function encapsulates the above
|
|
||||||
conversion.)
|
|
||||||
</para>
|
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
@ -737,6 +737,64 @@ make_timestamptz_at_timezone(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_TIMESTAMPTZ(result);
|
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()
|
/* timestamptz_out()
|
||||||
* Convert a timestamp to external form.
|
* Convert a timestamp to external form.
|
||||||
*/
|
*/
|
||||||
|
@ -53,6 +53,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 201603291
|
#define CATALOG_VERSION_NO 201603292
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1202,7 +1202,7 @@ DATA(insert OID = 1154 ( timestamptz_lt PGNSP PGUID 12 1 0 0 0 f f f t t f i
|
|||||||
DATA(insert OID = 1155 ( timestamptz_le PGNSP PGUID 12 1 0 0 0 f f f t t f i s 2 0 16 "1184 1184" _null_ _null_ _null_ _null_ _null_ timestamp_le _null_ _null_ _null_ ));
|
DATA(insert OID = 1155 ( timestamptz_le PGNSP PGUID 12 1 0 0 0 f f f t t f i s 2 0 16 "1184 1184" _null_ _null_ _null_ _null_ _null_ timestamp_le _null_ _null_ _null_ ));
|
||||||
DATA(insert OID = 1156 ( timestamptz_ge PGNSP PGUID 12 1 0 0 0 f f f t t f i s 2 0 16 "1184 1184" _null_ _null_ _null_ _null_ _null_ timestamp_ge _null_ _null_ _null_ ));
|
DATA(insert OID = 1156 ( timestamptz_ge PGNSP PGUID 12 1 0 0 0 f f f t t f i s 2 0 16 "1184 1184" _null_ _null_ _null_ _null_ _null_ timestamp_ge _null_ _null_ _null_ ));
|
||||||
DATA(insert OID = 1157 ( timestamptz_gt PGNSP PGUID 12 1 0 0 0 f f f t t f i s 2 0 16 "1184 1184" _null_ _null_ _null_ _null_ _null_ timestamp_gt _null_ _null_ _null_ ));
|
DATA(insert OID = 1157 ( timestamptz_gt PGNSP PGUID 12 1 0 0 0 f f f t t f i s 2 0 16 "1184 1184" _null_ _null_ _null_ _null_ _null_ timestamp_gt _null_ _null_ _null_ ));
|
||||||
DATA(insert OID = 1158 ( to_timestamp PGNSP PGUID 14 1 0 0 0 f f f f t f i s 1 0 1184 "701" _null_ _null_ _null_ _null_ _null_ "select (''epoch''::pg_catalog.timestamptz + $1 * ''1 second''::pg_catalog.interval)" _null_ _null_ _null_ ));
|
DATA(insert OID = 1158 ( to_timestamp PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 1184 "701" _null_ _null_ _null_ _null_ _null_ float8_timestamptz _null_ _null_ _null_ ));
|
||||||
DESCR("convert UNIX epoch to timestamptz");
|
DESCR("convert UNIX epoch to timestamptz");
|
||||||
DATA(insert OID = 3995 ( timestamp_zone_transform PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ timestamp_zone_transform _null_ _null_ _null_ ));
|
DATA(insert OID = 3995 ( timestamp_zone_transform PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ timestamp_zone_transform _null_ _null_ _null_ ));
|
||||||
DESCR("transform a time zone adjustment");
|
DESCR("transform a time zone adjustment");
|
||||||
|
@ -124,6 +124,7 @@ extern Datum timestamp_cmp_timestamptz(PG_FUNCTION_ARGS);
|
|||||||
extern Datum make_timestamp(PG_FUNCTION_ARGS);
|
extern Datum make_timestamp(PG_FUNCTION_ARGS);
|
||||||
extern Datum make_timestamptz(PG_FUNCTION_ARGS);
|
extern Datum make_timestamptz(PG_FUNCTION_ARGS);
|
||||||
extern Datum make_timestamptz_at_timezone(PG_FUNCTION_ARGS);
|
extern Datum make_timestamptz_at_timezone(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum float8_timestamptz(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
extern Datum timestamptz_eq_timestamp(PG_FUNCTION_ARGS);
|
extern Datum timestamptz_eq_timestamp(PG_FUNCTION_ARGS);
|
||||||
extern Datum timestamptz_ne_timestamp(PG_FUNCTION_ARGS);
|
extern Datum timestamptz_ne_timestamp(PG_FUNCTION_ARGS);
|
||||||
|
@ -2307,6 +2307,53 @@ SELECT make_timestamptz(2007, 12, 9, 3, 0, 0, 'VET');
|
|||||||
Sun Dec 09 07:30:00 2007 UTC
|
Sun Dec 09 07:30:00 2007 UTC
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
SELECT to_timestamp( 0); -- 1970-01-01 00:00:00+00
|
||||||
|
to_timestamp
|
||||||
|
------------------------------
|
||||||
|
Thu Jan 01 00:00:00 1970 UTC
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT to_timestamp( 946684800); -- 2000-01-01 00:00:00+00
|
||||||
|
to_timestamp
|
||||||
|
------------------------------
|
||||||
|
Sat Jan 01 00:00:00 2000 UTC
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT to_timestamp(1262349296.7890123); -- 2010-01-01 12:34:56.789012+00
|
||||||
|
to_timestamp
|
||||||
|
-------------------------------------
|
||||||
|
Fri Jan 01 12:34:56.789012 2010 UTC
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- edge cases
|
||||||
|
SELECT to_timestamp(-1e20::float8); -- error, out of range
|
||||||
|
ERROR: timestamp out of range: "-1e+20"
|
||||||
|
SELECT to_timestamp(-210866803200.0625); -- error, out of range
|
||||||
|
ERROR: timestamp out of range: "-2.10867e+11"
|
||||||
|
SELECT to_timestamp(-210866803200); -- 4714-11-24 00:00:00+00 BC
|
||||||
|
to_timestamp
|
||||||
|
---------------------------------
|
||||||
|
Mon Nov 24 00:00:00 4714 UTC BC
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- The upper boundary differs between integer and float timestamps, so check the biggest one
|
||||||
|
SELECT to_timestamp(185331707078400::float8); -- error, out of range
|
||||||
|
ERROR: timestamp out of range: "1.85332e+14"
|
||||||
|
-- nonfinite values
|
||||||
|
SELECT to_timestamp(' Infinity'::float);
|
||||||
|
to_timestamp
|
||||||
|
--------------
|
||||||
|
infinity
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT to_timestamp('-Infinity'::float);
|
||||||
|
to_timestamp
|
||||||
|
--------------
|
||||||
|
-infinity
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT to_timestamp('NaN'::float);
|
||||||
|
ERROR: timestamp cannot be NaN
|
||||||
SET TimeZone to 'Europe/Moscow';
|
SET TimeZone to 'Europe/Moscow';
|
||||||
SELECT '2011-03-26 21:00:00 UTC'::timestamptz;
|
SELECT '2011-03-26 21:00:00 UTC'::timestamptz;
|
||||||
timestamptz
|
timestamptz
|
||||||
|
@ -403,6 +403,21 @@ SELECT '2007-12-09 04:00:00'::timestamp AT TIME ZONE 'VET';
|
|||||||
SELECT make_timestamptz(2007, 12, 9, 2, 0, 0, 'VET');
|
SELECT make_timestamptz(2007, 12, 9, 2, 0, 0, 'VET');
|
||||||
SELECT make_timestamptz(2007, 12, 9, 3, 0, 0, 'VET');
|
SELECT make_timestamptz(2007, 12, 9, 3, 0, 0, 'VET');
|
||||||
|
|
||||||
|
SELECT to_timestamp( 0); -- 1970-01-01 00:00:00+00
|
||||||
|
SELECT to_timestamp( 946684800); -- 2000-01-01 00:00:00+00
|
||||||
|
SELECT to_timestamp(1262349296.7890123); -- 2010-01-01 12:34:56.789012+00
|
||||||
|
-- edge cases
|
||||||
|
SELECT to_timestamp(-1e20::float8); -- error, out of range
|
||||||
|
SELECT to_timestamp(-210866803200.0625); -- error, out of range
|
||||||
|
SELECT to_timestamp(-210866803200); -- 4714-11-24 00:00:00+00 BC
|
||||||
|
-- The upper boundary differs between integer and float timestamps, so check the biggest one
|
||||||
|
SELECT to_timestamp(185331707078400::float8); -- error, out of range
|
||||||
|
-- nonfinite values
|
||||||
|
SELECT to_timestamp(' Infinity'::float);
|
||||||
|
SELECT to_timestamp('-Infinity'::float);
|
||||||
|
SELECT to_timestamp('NaN'::float);
|
||||||
|
|
||||||
|
|
||||||
SET TimeZone to 'Europe/Moscow';
|
SET TimeZone to 'Europe/Moscow';
|
||||||
|
|
||||||
SELECT '2011-03-26 21:00:00 UTC'::timestamptz;
|
SELECT '2011-03-26 21:00:00 UTC'::timestamptz;
|
||||||
|
Reference in New Issue
Block a user