根据时区按时间戳中的日期进行分组

问题描述 投票:0回答:1

这是我的询问:

SELECT COUNT(*)
     , DATE(created_ts)
FROM users 
GROUP BY DATE(created_ts);

如果用户选择一个时间范围,例如

2024-12-17 00:00:00
-
2024-12-17 15:59:59

但这里出现了一个问题。用户可能位于上海,前端发给我的时间始终是UTC时间。 服务器收到的时间是:
2024-12-16 16:00:00
-
2024-12-17 15:59:59

SELECT COUNT(*)
     , DATE(created_ts)
FROM users
WHERE NOT created_ts < '2024-12-16 16:00:00'
  AND created_ts <= '2024-12-17 15:59:59'
GROUP BY DATE(created_ts);

那么执行这条sql就会生成16-17号组。 这将导致

COUNT(*)
不准确。请告诉我如何处理。

我不知道该怎么办,希望查询结果是用户想要的,仅此而已

sql postgresql postgres15
1个回答
0
投票
  1. 默认情况下,

    timestamp
    使用微秒分辨率。将上限向下移动一秒同时使其包含在内会导致问题 - 只需对两者使用 16:00 并使用
    >
    使一个包含,一个使用
    <=
    排除(顺便说一下,您的第一个边界比较已翻转):
    db<>fiddle 的演示

    SELECT COUNT(*)
         , DATE(created_ts)
    FROM users
    WHERE NOT created_ts > '2024-12-16 16:00:00'
      AND created_ts <= '2024-12-17 16:00:00'
    GROUP BY DATE(created_ts);
    

    否则,从

    15:59:59.000001
    15:59:59.999999
    的整整一秒都不属于任何一个范围。

  2. 将该栏设为

    timestampTZ
    ,并让客户也向您发送
    timestampTZ
    ,以完全消除歧义。

    alter table users 
      alter column created_ts type timestampTZ;
    

    它们是时区感知时间戳,成本完全相同,而常规的

    timestamp
    只是一张“时钟图片”,这会导致像您所面临的问题。如果您的客户已经向您发送 UTC
    timestampTZ
    ,您只需确保该列已更改,就可以了。

  3. 如果您的客户向您发送不知道时区的时区

    timestamp
    ,但你们都同意它是 UTC,您可以使用
    at time zone
    应用转变并使其了解时区:

    SELECT COUNT(*)
         , DATE(created_ts)
    FROM users
    WHERE NOT created_ts > '2024-12-16 16:00:00'::timestamp at time zone 'UTC'
      AND created_ts <= '2024-12-17 16:00:00'::timestamp at time zone 'UTC'
    GROUP BY DATE(created_ts);
    

    确保运行此查询的会话使用

    set TimeZone='UTC';
    打开会产生相同的效果 - 将
    timestamptz
    timestamp
    进行比较,使得 Postgres 更喜欢将后者转换为
    timestamptz
    而不是丢失时区。当它将
    timestamp
    转换为
    timestamptz
    时,它假定其处于当前
    TimeZone
    设置指示的时区。

© www.soinside.com 2019 - 2024. All rights reserved.