PostgreSQL:选择在特定时区的某一天的某一天发生的行

时间:2013-10-27 10:30:18

标签: sql ruby-on-rails postgresql timezone

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

在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')。

这样可行,但我确信有更好的方法可以在一周中的特定日期查找记录,然后手动操作interval。我也认为这不适用于适用时区的DST。我知道PostgreSQL能够converting a time with time zone to a particular time zone,但是这不包括日期,所以我无法弄清楚星期几!我试过的其他WHERE条款:

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

  • EXTRACT (dow from time at time zone 'Brisbane/Australia') = 0 - 这让我在星期一晚上8点到星期一晚上8点之间采取行动。我的理解是,这是因为Postgres将time字段视为布里斯班时间,而不是从UTC转换为布里斯班时间。

所以我很想知道我是否应该采取措施让这个查询正常运作。

1 个答案:

答案 0 :(得分:3)

应用于AT TIME ZONE时,

timestamp without timezone会生成timestamp with timezone(反之亦然)。此timestamp with timezone在您的会话的时区(在您的情况下被强制为UTC)中进行解释。

因此表达式EXTRACT (dow from time at time zone 'Brisbane/Australia')不会在 time(UTC)中提取布里斯班的那一天,它会提取与转换后的 {{1}相对应的日期从实际生活在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

很好,它是UTC的周日18:01和布里斯班的周一04:01

但是如果将时区位移应用于time

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

注意第二列与先前结果的不同之处。它实际上是在布里斯班以20小时的时间表示在UTC:这可能是对一个没有多大意义的问题的技术上正确的答案。

大概你想要这个:

timestamp without timezone

应该回答:以UTC衡量的日期和时间 EXTRACT (dow from (time AT TIME ZONE 'UTC') at time zone 'Brisbane/Australia')=0 是否与布里斯班的星期日相对应?