AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 309964
Accepted
Ουιλιαμ Αρκευα
Ουιλιαμ Αρκευα
Asked: 2022-03-21 17:51:24 +0800 CST2022-03-21 17:51:24 +0800 CST 2022-03-21 17:51:24 +0800 CST

从函数返回带有时区的时间戳时丢失时区

  • 772

我有这个功能:

CREATE OR REPLACE FUNCTION laborable_day(dtDateTime TIMESTAMP WITH TIME ZONE) 
RETURNS TIMESTAMP WITH TIME ZONE AS $$
DECLARE
  _isHoliday BOOLEAN;
BEGIN
  LOOP
    SELECT COUNT(*) > 0 INTO _isHoliday 
    FROM holidays 
    WHERE holiday = DATE(dtDateTime);
    
    IF _isHoliday THEN
      dtDateTime := dtDateTime + INTERVAL '1 DAY';
    ELSE
      EXIT;
    END IF;
  END LOOP;
  RETURN dtDateTime;
END;
$$ LANGUAGE plpgsql STRICT SECURITY DEFINER;

如果我运行这个命令:

select laborable_day('2022-01-01 18:53:11.14297-05'::TIMESTAMPTZ);

我得到:

+------------------------------+
| laborable_day                |
|------------------------------|
| 2022-01-02 23:53:11.14297+00 |
+------------------------------+
SELECT 1
Time: 0.012s

为什么时区信息会丢失?

postgresql functions
  • 1 1 个回答
  • 377 Views

1 个回答

  • Voted
  1. Best Answer
    Erwin Brandstetter
    2022-03-21T20:41:16+08:002022-03-21T20:41:16+08:00

    数据类型timestamp with time zone( timestamptz)不存储任何时区信息。这是一个常见的误解,受误导性名称的启发。(责怪 SQL 标准委员会!)你不是第一个为此而堕落的人:

    • 数据类型“带时区的时间戳”中的时区存储

    时间偏移只是一个输入修饰符/输出修饰符。Postgres 总是在内部存储 UTC 时间。这里的基础:

    • 在 Rails 和 PostgreSQL 中完全忽略时区

    考虑这个演示:

    test=> SET timezone = 'UTC';
    SET
    test=> SELECT timestamptz '2022-01-01 18:53:11.14297-05';
             timestamptz          
    ------------------------------
     2022-01-01 23:53:11.14297+00
    (1 row)
    
    test=> SET timezone = 'America/New_York';
    SET
    
    test=> SELECT timestamptz '2022-01-01 18:53:11.14297-05';
             timestamptz          
    ------------------------------
     2022-01-01 18:53:11.14297-05
    
    test=> SELECT timestamptz '2022-03-21 18:53:11.14297-05';
             timestamptz          
    ------------------------------
     2022-03-21 19:53:11.14297-04
    (1 row)
    

    还要注意最后一次通话的 DST 是如何变化的。看:

    • 带有 AT TIME ZONE 和 UTC 偏移量的怪事

    这也说明了为什么您当前的功能本质上是不可靠的。一个普通的 from timestamptztodate假定你的会话的当前时区设置。如果没有给出它适用的时区,日期就没有明确定义。如果您对模糊定义感到满意,请考虑这个改进的(但仍然很幼稚)的功能:

    CREATE OR REPLACE FUNCTION laborable_day(INOUT _dt timestamptz)
      LANGUAGE plpgsql STRICT AS  -- why SECURITY DEFINER ?
    $func$
    BEGIN
       LOOP
          IF EXISTS (
             SELECT FROM holidays
             WHERE  holiday = _dt::date  -- depends on current time zone! 
             ) THEN
             _dt := _dt + interval '1 day';
          ELSE
             EXIT;
          END IF;
      END LOOP;
    END
    $func$;
    

    我删除了SECURITY DEFINER. 仅在必要时使用它,因为它具有潜在危险。相反,SELECT将您的holidays表授予PUBLIC.

    为简单起见使用INOUT参数。

    关于EXISTS:

    • PL/pgSQL 检查一行是否存在

    我还替换了您的混合大小写标识符。看:

    • PostgreSQL 列名是否区分大小写?

    确定性函数

    要获得确定性结果,还要定义时区。喜欢

    CREATE OR REPLACE FUNCTION laborable_day(INOUT _dt timestamptz, _tz text DEFAULT 'UTC')  -- default UTC?
      LANGUAGE plpgsql STRICT AS  -- why SECURITY DEFINER ?
    $func$
    DECLARE
       _day date := (_dt AT TIME ZONE _tz)::date;
       _inc int := 0;
    BEGIN
       LOOP
          IF EXISTS (
             SELECT FROM holidays
             WHERE  holiday = _day + _inc
             ) THEN
             _inc := _inc + 1;
          ELSE
             EXIT;
          END IF;
      END LOOP;
      
      _dt := _dt + interval '1 day' * _inc;
    END
    $func$;
    

    我添加了“UTC”作为DEFAULT第二个参数。适应您的需求。关于参数默认值:

    • PL/pgSQL 函数中的可选参数

    您只需添加integer到date.

    我建议不要“重载”函数(创建具有不同函数参数的变体),这可能会很棘手。

    这样你仍然可以调用只给出一个的函数timestamptz:

    SELECT laborable_day('2022-01-01 18:53:11.14297-05');
    

    要获得给定时区的结果,即“欧洲/维也纳”:

    SELECT laborable_day('2022-01-01 18:53:11.14297-05', 'Europe/Vienna');
    

    使用时区名称,而不是缩写或数字偏移量,使其与 DST 和其他奇怪的东西一起正常工作。

    • 带有 AT TIME ZONE 和 UTC 偏移量的怪事

    在 中查找可用时区名称pg_catalog.pg_timezone_names。

    • 3

相关问题

  • 我可以在使用数据库后激活 PITR 吗?

  • 运行时间偏移延迟复制的最佳实践

  • 存储过程可以防止 SQL 注入吗?

  • PostgreSQL 中 UniProt 的生物序列

  • PostgreSQL 9.0 Replication 和 Slony-I 有什么区别?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve