sql - PostgreSQL:选择一周中某一天、特定时区发生的行

标签 sql ruby-on-rails postgresql timezone

我有一个存储“快照”数据的表 - 每 10 分钟捕获一次工作人员数量并将其存储在其中。我想生成一份报告来显示特定工作日(例如过去四个星期日)一天中工作的员 worker 数。

在 Rails 中,我的查询如下所示:

<model>.where("EXTRACT(dow FROM (time + interval '#{time_zone_offset} hours')) = ?", Time.zone.now.wday)
       .where('time BETWEEN ? AND ?', 5.weeks.ago.end_of_week, 1.week.ago.end_of_week)
       .select("organisation_id, date_trunc('hour', time) as grouped_time, avg(staff) as staff")
       .group("grouped_time").order("grouped_time")

这会转化为这个 SQL:

SELECT date_trunc('hour', time) as grouped_time, avg(staff) as staff
FROM <model>
WHERE (EXTRACT(dow FROM (time + interval '10 hours')) = 0)
AND (time BETWEEN '2013-09-22 13:59:59.999999' AND '2013-10-20 13:59:59.999999')
GROUP BY grouped_time
ORDER BY grouped_time

在这种情况下,time_zone_offset 将根据我正在查找的用户而有所不同:

def time_zone_offset
  @tzoffset ||= ActiveSupport::TimeZone[current_user.organisation.time_zone].utc_offset / 60 / 60
end

(当前时区也在 around_filter 中设置,以便 1.week.ago 等位于正确的时区。)

我的数据库的时区是 UTC(set TIME ZONE 'UTC')。

这可行,但我确信有一种更好的方法来查找一周中特定一天的记录,而不是手动操作间隔。我也不认为这适用于适用的时区的夏令时。我知道 PostgreSQL 能够 converting a time with time zone to a particular time zone ,但不包括日期,所以我无法计算出星期几!我尝试过的其他 WHERE 子句:

  • EXTRACT (dow from time') = 0 - 这为我提供周日上午 10 点到周一上午 10 点之间的快照

  • EXTRACT(来自时区“布里斯类/澳大利亚”的时间的 dow)= 0 - 这为我提供了周日晚上 8 点到周一晚上 8 点之间的操作。我的理解是,发生这种情况是因为 Postgres 将 time 字段视为布里斯类时间,而不是将其从 UTC 转换为布里斯类时间。

所以我很想知道是否应该做一些事情来让这个查询正常工作。

最佳答案

AT TIME ZONE 当应用于没有时区的时间戳时会生成有时区的时间戳(反之亦然)。这个带有时区的时间戳会在您 session 的时区中进行解释(在您的情况下强制为 UTC)。

因此,表达式 EXTRACT (dow from time at time at 'Brisbane/Australia') 不会提取布里斯类的日期 time (UTC),它从虚拟生活在 UTC 时区的人的角度提取与转换后的时间相对应的日期。

举个例子,当我输入此内容时,如果假装使用 UTC:

=> set timezone to 'UTC';
=> select now(),now() at time zone 'Australia/Brisbane';
             now              |         timezone          
------------------------------+---------------------------
 2013-10-27 18:01:03.15286+00 | 2013-10-28 04:01:03.15286

Fine, it's Sunday 18:01 in UTC and Monday 04:01 at Brisbane

But if applying the timezone displacement to a timestamp without timezone:

select now(),now()::timestamp at time zone 'Australia/Brisbane';
              now              |           timezone            
-------------------------------+-------------------------------
 2013-10-27 18:01:57.878541+00 | 2013-10-27 08:01:57.878541+00

Notice how the second column differs from the previous result. It's actually 20 hours off of Brisbane expressed in UTC: it's presumably the technically correct answer to a question that doesn't make much sense.

Presumably you want this:

EXTRACT (dow from (time AT TIME ZONE 'UTC') at time zone 'Brisbane/Australia')=0

哪个应该回答:以 UTC 测量的日期和时间 time 是否对应于布里斯类的星期日?

关于sql - PostgreSQL:选择一周中某一天、特定时区发生的行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19617048/

相关文章:

sql - 如何聚合行并创建数据透视表

mysql - 如何从两个表中获取数据

ruby-on-rails - 使用参数引发自定义异常

css - 带有 Twitter Bootstrap 问题的响应式 CSS

mysql - PostgreSQL 或 MySQL 复制粘贴部署

sql - 使用 ANSI sql 将列转置为行

ruby-on-rails - 如何在子文件夹中使用不同的部分和布局?

postgresql - 将带有分号分隔符的 CSV 文件从 S3 导入到 RDS Postgres 数据库

postgresql - 条件唯一约束未正确更新

mysql - 从 MySQL 获取多列百分比