PostgreSQL:如何测试时间戳是否有时区?

时间:2016-09-27 13:33:57

标签: postgresql datetime timezone timestamp-with-timezone

我想防止在PostgreSQL SQL函数中插入任何没有时区的时间戳。我设法提出的唯一方法并不起作用,因为Postgres似乎忽略了任何时区字符串后缀,而是使用服务器的时区:

hostname:~ username$ date
Tue Sep 27 15:30:16 CEST 2016

hostname:~ username$ uname -a
Darwin hostname.domain 14.5.0 Darwin Kernel Version 14.5.0: Mon Aug 29 21:14:16 PDT 2016; root:xnu-2782.50.6~1/RELEASE_X86_64 x86_64

hostname:~ username$ psql -U postgres
psql (9.5.3)
Type "help" for help.

postgres=# SELECT extract(TIMEZONE FROM '2016-09-19 01:00:00'::timestamptz);
 date_part
-----------
      7200
(1 row)

postgres=# SELECT extract(TIMEZONE FROM '2016-09-19 01:00:00+0200'::timestamptz);
 date_part
-----------
      7200
(1 row)

postgres=# SELECT extract(TIMEZONE FROM '2016-09-19 01:00:00+0800'::timestamptz);
 date_part
-----------
      7200
(1 row)

我有一种感觉,我通常会误解某些东西。有人可以帮忙吗?

更新20160928:

为了给出一些解释性的背景,这是我的数据库(剥离到手头问题的必要条件):

CREATE DATABASE my_db;
CREATE TABLE my_table
(
    id serial PRIMARY KEY,
    timestamp_utc timestamp with time zone NOT NULL,
    title text NOT NULL
);

这是我以当前形式插入数据的功能:

CREATE FUNCTION insert_row
(
    p_timestamp_utc timestamp with time zone,
    p_title         text
)
RETURNS integer
LANGUAGE SQL
SECURITY DEFINER
VOLATILE
AS
$BODY$
    INSERT INTO my_table
                (  timestamp_utc    title)
         VALUES (p_timestamp_utc, p_title)
      RETURNING id;
$BODY$;

我创建了一个SQLFiddle;尽管创造了这个功能但仍然失败......

问题:

即使timestamp参数没有携带时区,调用函数也会成功:

SELECT insert_row('2016-09-01 01:00:00'::timestamptz, 'some title');

我需要找到一种让这个函数调用失败的方法。

为什么我需要使其失败的原因是我想要通过代码强制执行任何时间戳的时区定义;另一种选择是数据库服务器和客户端在某种程度上隐含地/默默地同意一个约定 - 这听起来并不像是一个明智的举动......

1 个答案:

答案 0 :(得分:1)

在PostgreSQL中,时区信息不会与timestamp with time zone一起存储。 相反,当时间戳转换为其内部表示时,时间戳将转换为UTC

timestamp with time zone转换为字符串时,例如显示时,它将转换为TimeZone配置参数中设置的时区。

使用EXTRACT检索时区信息时也会发生同样的情况。文档对此有点不清楚。

因此,如果您的时区设置如下:

test=> SHOW TimeZone;
   TimeZone
---------------
 Europe/Vienna
(1 row)

你会得到:

test=> SELECT extract(TIMEZONE FROM '2016-01-01 00:00:00-07'::timestamptz);
 date_part
-----------
      3600
(1 row)

因为中欧时间偏离UTC 1小时,

test=> SELECT extract(TIMEZONE FROM '2016-08-01 00:00:00-07'::timestamptz);
 date_part
-----------
      7200
(1 row)

因为中欧夏令时与UTC相差2小时