在 PostgreSQL 中正确处理 TIME AND TIME ZONE

2024-02-20

我们有一个表,其中填充了来自另一个系统的遗留报告的数据。该表的列反映了报告的相同结构。

以下是该表的缩写结构:

CREATE TABLE IF NOT EXISTS LEGACY_TABLE (
  REPORT_DATE DATE NOT NULL,
  EVENT_ID BIGINT PRIMARY KEY NOT NULL,
  START_HOUR TIMESTAMP WITHOUT TIME ZONE,
  END_HOUR TIME WITHOUT TIME ZONE,
  EXPECTED_HOUR TIME WITHOUT TIME ZONE
);

我们正在重构此表以处理不同客户的不同时区。新的结构将类似于:

CREATE TABLE IF NOT EXISTS LEGACY_TABLE (
  REPORT_DATE DATE NOT NULL,
  EVENT_ID BIGINT PRIMARY KEY NOT NULL,
  START_HOUR TIMESTAMP WITH TIME ZONE,
  END_HOUR TIME WITH TIME ZONE,
  EXPECTED_HOUR TIME WITH TIME ZONE
);

这些小时字段表示 REPORT_DATE 列表示的一天中的特定时间点。我的意思是,每个 TIME 列代表 REPORT_DATE 中指定的一天中的某个时刻。

其他一些需要考虑的要点:

  • 我们不知道为什么我们从旧系统收到的报告中 START_HOUR 采用 TIMESTAMP 格式。但我们会按照数据到达我们的方式导入数据。
  • 报告中的字段根据客户端的时区进行格式化,因此要重构此表,我们需要结合客户端的时区(我们有此信息)以正确插入 UTC 格式的时间戳/时间。

但现在问题来了。这些列的值在我们的系统中用于多次计算另一个值,如下所示:

START_HOUR - END_HOUR (the result of this operation is currently being casted to TIME WITHOUT TIME ZONE)
START_HOUR < END_HOUR
START_HOUR + EXPECTED_HOUR
EXPECTED_HOUR - END_HOUR
EXPECTED_HOUR < '05:00' 

经过一番研究我发现不推荐使用该类型TIME WITH TIME ZONE (Postgres 时间与时区相等 https://stackoverflow.com/questions/20529284/postgres-time-with-time-zone-equality/20530283)现在我有点困惑重构这个表以处理不同时区并处理我们需要的不同列操作的最佳方法是什么。

除此之外,我已经知道减去两列类型是安全的TIMESTAMP WITH TIME ZONE。此减法运算考虑了 DST 变化(用时区减去时间戳类型的两列 https://stackoverflow.com/questions/50223332/subtracting-two-columns-of-type-timestamp-with-time-zone)但是其他人呢?还有从时间戳中减去时间的那个?

关于表重构,我们应该使用TIME WITH TIME ZONE无论如何?我们应该继续使用TIME WITHOUT TIME ZONE?或者最好忘记类型TIME总共并将日期与时间合并并将列更改为TIMESTAMP WITH TIME ZONE?

我认为这些问题是相关的,因为我们选择使用的新列类型将定义我们如何操作列。


您断言:

每个 TIME 列代表一天中指定的时刻REPORT_DATE.

So you never跨越同一行内的日期变更线。我建议保存1xdate 3x time时区 (as text或 FK 列):

CREATE TABLE legacy_table (
   event_id      bigint PRIMARY KEY NOT NULL
 , report_date   date NOT NULL
 , start_hour    time
 , end_hour      time
 , expected_hour time
 , tz            text  -- time zone
);

就像你已经发现的那样,timetz (time with time zone) 一般应避免 https://stackoverflow.com/a/20530283/939860。它无法正确处理 DST 规则(daylight saving time).

So 基本上是你已经拥有的。只需删除日期组件即可start_hour,那是死运费。投掷timestamp to time来截断日期。喜欢:(timestamp '2018-03-25 1:00:00')::time

tz可以是任何接受的字符串AT TIME ZONE https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-ZONECONVERT构造,但为了可靠地处理不同的时区,最好专门使用时区名称。任何name你发现在系统目录pg_timezone_names https://www.postgresql.org/docs/current/view-pg-timezone-names.html.

为了优化存储,您可以在小型查找表中收集允许的时区名称并替换tz text with tz_id int REFERENCES my_tz_table.

有和没有 DST 的两个示例行:

INSERT INTO legacy_table VALUES
   (1, '2018-03-25', '1:00', '3:00', '2:00', 'Europe/Vienna')  -- sadly, with DST
 , (2, '2018-03-25', '1:00', '3:00', '2:00', 'Europe/Moscow'); -- Russians got rid of DST

出于表示目的或计算,您可以执行以下操作:

SELECT (report_date + start_hour)    AT TIME ZONE tz AT TIME ZONE 'UTC' AS start_utc
     , (report_date + end_hour)      AT TIME ZONE tz AT TIME ZONE 'UTC' AS end_utc
     , (report_date + expected_hour) AT TIME ZONE tz AT TIME ZONE 'UTC' AS expected_utc
     -- START_HOUR - END_HOUR
     , (report_date + start_hour) AT TIME ZONE tz
     - (report_date + end_hour)   AT TIME ZONE tz AS start_minus_end
FROM   legacy_table;

您可以创建一个或多个views https://www.postgresql.org/docs/current/sql-createview.html以便根据需要轻松显示字符串。该表用于存储您的信息need.

注意括号!否则运营商+之前会绑定AT TIME ZONE due to 运算符优先级 https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-PRECEDENCE.

看看结果:

数据库小提琴

由于维也纳的时间受到操纵(就像任何适用愚蠢的 DST 规则的地方一样),您会得到“令人惊讶”的结果。

Related:

  • 选择计划项目时考虑 Postgres 中的 DST https://stackoverflow.com/questions/13239534/accounting-for-dst-in-postgres-when-selecting-scheduled-items/13243029#13243029
  • 在 Rails 和 PostgreSQL 中完全忽略时区 https://stackoverflow.com/questions/9571392/ignoring-time-zones-altogether-in-rails-and-postgresql/9576170#9576170
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 PostgreSQL 中正确处理 TIME AND TIME ZONE 的相关文章

  • 如何从主机连接到 Docker Postgres 容器

    我按照以下说明搭建了一个 Rails 开发环境https docs docker com compose rails https docs docker com compose rails 它可以工作 但我无法从主机连接到 Postgres
  • 如何覆盖 Ruby Ranges 的 .. 和 ... 运算符以接受 Float::INFINITY?

    我想覆盖 and Ruby 中的运算符Range 原因是 我正在处理数据库中的无限日期范围 如果你拉一个infinty从 Postgres 中取出日期时间 你会得到一个Float INFINITY在红宝石中 问题是 我无法使用Float I
  • 查找 postgres 提供的列表中不存在的值

    我试图找到一个查询来告诉我数据库中没有的值 例如 select seqID segment from flu where seqID IN 1 2 3 4 5 6 7 8 9 现在 如果我的数据库没有 seqID 3 8 9 我将如何查找
  • 我可以以编程方式配置 PostgreSQL 以不消除全文搜索中的停用词吗?

    我正在使用 PostgreSQL 全文搜索来进行项目 其中传统停用词 a the if 等 应该被索引和可搜索 这不是默认行为 例如 我可能希望我的用户找到查询 to be or not to be 的结果 The 文档 http www
  • 计算包含字母/数字的行数

    我想要实现的目标很简单 但是解释起来有点困难 我不知道在 postgres 中这是否真的可能 我处于相当基础的水平 SELECT FROM WHERE LEFT JOIN ON HAVING 等等基本的东西 我正在尝试计算包含特定字母 数字
  • 改进R中从google获取股票新闻数据的功能

    我已经编写了一个函数来从 Google 获取和解析给定股票代码的新闻数据 但我确信有一些方法可以改进它 对于初学者来说 我的函数返回一个 GMT 时区的对象 而不是用户当前的时区 如果传递的数字大于 299 它就会失败 可能是因为 goog
  • 为什么我的 postgis 不在几何字段上使用索引?

    Windows 上的 postgresql 9 5 postgis 2 2 我首先创建一个表 CREATE TABLE points id SERIAL ad CHAR 40 name VARCHAR 200 然后 添加一个几何字段 geo
  • PostgreSql“运行安装后步骤...数据库集群初始化失败”

    我是一名 Windows 用户 我花了几个小时不断地安装和卸载 然后才使其正常工作 前 10 次左右才看到标题中的错误消息 我将其作为一个自我回答的问题放在这里 以防止其他人在安装时可能遇到同样的问题 并为像我这样第一次使用 Postgre
  • Postgres LIMIT/OFFSET 奇怪的行为

    我正在使用 PostgreSQL 9 6 我有一个这样的查询 SELECT anon 1 id AS anon 1 id anon 1 is valid AS anon 1 is valid anon 1 first name AS ano
  • 唯一约束与唯一索引?

    之间有区别吗 CREATE TABLE p product no integer name text UNIQUE price numeric and CREATE TABLE p product no integer name text
  • PostgreSQL - 根据另一个单元格值设置默认单元格值

    如果我有一个专栏说column a任何给定值 我想要另一列column b有一个default value根据 的值column a 换句话说 if column a peter then column b default value do
  • Postgres:显示继承的字段

    我应该实现什么查询来获取继承的列 读过this http www alberton info postgresql meta info html综合帖子没有找到解决办法 如果我理解正确的话 您想知道作为表之间继承的一部分的列的名称 SELE
  • 如何创建不返回任何内容的函数

    我想写一个函数pl pgsql 我在用着Postgres 企业管理器 v3并使用 shell 来创建一个函数 但在 shell 中我必须定义返回类型 如果我不定义返回类型 我将无法创建函数 如何创建一个不返回结果的函数 即创建一个新表的函数
  • 如何计算 Postgres 上图表中所有连接的节点(行)?

    我的桌子有account id and device id One account id可以有多个device ids 反之亦然 我正在尝试计算每个连接的多对多关系的深度 Ex account id device id 1 10 1 11
  • Npgsql 参数化查询输出与 PostGIS 不兼容

    我在 Npgsql 命令中有这个参数化查询 UPDATE raw geocoding SET the geom ST Transform ST GeomFromText POINT longitude latitude 4326 3081
  • pg_dump 没有对象注释?

    有没有办法执行 pg dump 并排除表 视图和列的 COMMENT ON 我广泛使用 COMMENT ON 命令来描述所有对象 并且经常在其中包含换行符以获得更清晰的描述 例如 COMMENT ON TABLE mytable1 IS M
  • 如何引用下一行的数据?

    我正在 PostgreSQL 9 2 中编写一个函数 对于股票价格和日期的表 我想计算每个条目较前一天的百分比变化 对于最早一天的数据 不会有前一天 因此该条目可以简单地为 Nil 我知道WITH声明可能不应该高于IF陈述 到目前为止 这就
  • 本地 Postgres 实例和 Azure Cloud Postgres 实例之间的实时同步

    我需要在本地 postgresql 实例与云 postgresql 实例之间设置实时同步过程 请让我知道我可以通过哪些选项来实现它 我是否必须使用任何特定工具或者可以通过复制进行管理 请指教 使用 PgPool http www pgpoo
  • 带有可变 WHERE 子句的批量 UPDATE 表

    我有一堆值对 foo1 bar1 foo2 bar2 我想做一堆更新 将 foo 列设置为 foo1 其中 bar 列为 bar1 我正在使用 psycopg2 在 Python 中执行此操作 我可以executemany与查询UPDATE
  • 使用连接池后如何处理过多的并发连接?

    Scenario 假设您有一个拥有大量流量的网站或应用程序 即使使用数据库连接池 性能也会受到真正的打击 站点 应用程序甚至可能崩溃 因为并发连接太多 Question 人们有什么选择来处理这个问题 我的想法 我在想有这个问题的人可以创建多

随机推荐