First,考虑数据类型timestamptz
(timestamp with time zone
) 代替timestamp
(timestamp without time zone
)。那么用户的时区就无关紧要了。now()
回报timestamptz
,你的问题源于强迫它timestamp
. See:
- 在 Rails 和 PostgreSQL 中完全忽略时区 https://stackoverflow.com/questions/9571392/ignoring-time-zones-altogether-in-rails-and-postgresql/9576170#9576170
“但我需要那种类型timestamp
!"
If你的联系发生在可以与时区绑定的数据库角色(登录名),那么 PostgreSQL 提供了一个简单的解决方案:
ALTER ROLE my_role SET TIMEZONE = '+1';
使用此角色发起的每个连接都将自动在其预设时区中运行(除非另有说明)。
注意,引用手册 https://www.postgresql.org/docs/current/sql-alterrole.html:
这只发生在登录时;执行SET ROLE
or SET SESSION AUTHORIZATION
不会导致设置新的配置值。
你可能想使用时区名称而不是遵循 DST 规则和其他对当地时间的政治操纵的简单偏移。更多的:
- 在 Rails 和 PostgreSQL 中完全忽略时区 https://stackoverflow.com/questions/9571392/ignoring-timezones-altogether-in-rails-and-postgresql/9576170#9576170
或者,您可以为您的登录创建一个查找表,在其中存储相应的时区(这可能有其他用途):
CREATE TABLE usertime(
username text PRIMARY KEY -- careful, "user" is a reserved word
, timezone text -- ... but "timezone" is not
);
INSERT INTO usertime VALUES
('postgres', '+4')
, ('my_role' , '+3')
;
编写一个小型 SQL 函数:
CREATE FUNCTION f_user_ts()
RETURNS timestamp
LANGUAGE sql STABLE AS
$func$
SELECT now() AT TIME ZONE u.timezone
FROM usertime u
WHERE u.username = current_user
$func$;
现在,这将返回当前角色(用户)的本地时间戳:
SELECT f_user_ts();
更多信息
请参阅详细手册AT TIME ZONE https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-ZONECONVERT构造。这两种语法变体都是有效的:
SET TIME ZONE TO 'UTC';
SET TIMEZONE TO 'UTC';
但是now() AT TIMEZONE foo;
不是!它一定要是:
SELECT now() AT TIME ZONE foo;
foo
成为一个text
保存时区偏移量、缩写或名称的变量(或如上面函数中的列)。您还可以直接提供字符串文字。