You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

PGSQL自定义函数返回类型不匹配:时区转换问题求助

解决PostgreSQL自定义函数时区转换的类型不匹配问题

咱们先拆解你遇到的核心问题:你用两次AT TIME ZONE得到的结果类型,和函数声明的返回类型不匹配,这才触发了报错。先理清楚PostgreSQL里AT TIME ZONE的行为逻辑:

  1. 当你用timestamp without time zone AT TIME ZONE 'UTC'时,会把无时区的时间当作UTC时间,转换成timestamp with time zone类型的带时区时间戳(内部存储为UTC)。
  2. 紧接着再调用AT TIME ZONE 'MST7MDT',会把这个带时区的时间转换为山地时区(含夏令时切换)的本地时间,类型变成timestamp without time zone

所以如果你的函数直接返回这两次调用的结果,得到的是无时区时间,但你声明要返回带时区时间,自然会类型不匹配。

下面分两种常见场景给你正确的函数写法:

场景1:输入的无时区时间是UTC时间,要返回对应的山地时区带时区时间

如果你的输入是没有时区标记的UTC时间,想要把它转换成山地时区(MST/MDT)的带时区时间戳,函数可以这么写:

PL/pgSQL版本

CREATE OR REPLACE FUNCTION utc_to_mst_tz(tzless_utc_ts timestamp without time zone)
RETURNS timestamp with time zone AS $$
BEGIN
    -- 逻辑:先把UTC无时区时间标记为UTC带时区 → 转成山地本地时间 → 再标记为山地时区的带时区时间
    RETURN (tzless_utc_ts AT TIME ZONE 'UTC' AT TIME ZONE 'MST7MDT') AT TIME ZONE 'MST7MDT';
END;
$$ LANGUAGE plpgsql;

更简洁的SQL版本

CREATE OR REPLACE FUNCTION utc_to_mst_tz(tzless_utc_ts timestamp without time zone)
RETURNS timestamp with time zone AS $$
    SELECT (tzless_utc_ts AT TIME ZONE 'UTC' AT TIME ZONE 'MST7MDT') AT TIME ZONE 'MST7MDT';
$$ LANGUAGE sql;

场景2:输入的无时区时间本身就是山地时区的本地时间,要返回带时区的山地时间

如果你的输入是没有时区标记的山地本地时间(比如用户直接输入的MST时间),只需要一步就能转换成带时区时间:

CREATE OR REPLACE FUNCTION mst_local_to_tz(tzless_mst_ts timestamp without time zone)
RETURNS timestamp with time zone AS $$
BEGIN
    -- 直接把无时区的山地时间标记为山地时区的带时区时间
    RETURN tzless_mst_ts AT TIME ZONE 'MST7MDT';
END;
$$ LANGUAGE plpgsql;

测试验证

你可以用下面的语句测试函数是否符合预期:

-- 测试场景1:UTC夏令时时间转山地时间
SELECT utc_to_mst_tz('2024-06-01 12:00');
-- 预期返回:2024-06-01 06:00:00-06(MDT,比UTC晚6小时)

-- 测试场景1:UTC冬令时时间转山地时间
SELECT utc_to_mst_tz('2024-01-01 12:00');
-- 预期返回:2024-01-01 05:00:00-07(MST,比UTC晚7小时)

-- 测试场景2:山地本地时间转带时区时间
SELECT mst_local_to_tz('2024-06-01 06:00');
-- 预期返回:2024-06-01 06:00:00-06

为什么单独用两次AT TIME ZONE能工作?

你提到单独执行AT TIME ZONE 'UTC' AT TIME ZONE 'MST7MDT'能得到预期结果,那是因为这个语句返回的是timestamp without time zone类型的山地本地时间,而你的函数声明要返回timestamp with time zone,所以才会报错。要么调整函数返回类型为timestamp without time zone,要么按照上面的逻辑修改转换步骤即可解决问题。

内容的提问来源于stack exchange,提问作者jdarrell

火山引擎 最新活动