1

背景

具有默认created_at列的文章模型

导轨config.time_zone = 'Warsaw'

我有一篇文章 created_at = local time 2012-08-19 00:15 (2012-08-18 22:15 in UTC)。

目标

接收 2012-08-19 (当地时间)创建的所有文章。

我的(无法正常工作)解决方案

Article.where(
  "date_trunc('day', created_at AT TIME ZONE '#{Time.zone.formatted_offset}')
   = '#{Date.civil(2012, 8, 19)}'"
)

生成SQL:

SELECT "articles".* FROM "articles"
WHERE (date_trunc('day', created_at AT TIME ZONE '+01:00') = '2012-08-19')

并返回一个空集。但是,如果我在 psql 中运行相同的查询,它会返回一篇文章……这让我很困惑。

问题

我做错了什么以及如何解决?

4

2 回答 2

5

目标:接收2012-08-19(当地时间)创建的所有文章。

'+01:00'(就像你使用它一样)是一个固定的时间偏移量,不能考虑 DST(夏令时)。使用时区名称(不是缩写)。这些在 PostgreSQL 中可用:

SELECT * FROM pg_timezone_names;

对于华沙来说,这应该是'Europe/Warsaw'。系统从其存储的信息中知道 DST 的界限并应用相应的时间偏移。


此外,您的查询可以简化。

同样,保存created_attimestamp [without time zone]值反映了创建行时服务器的本地时间(在内部保存为 UTC 时间戳)。

基本上只有两种可能性,具体取决于您客户的时区。

  1. 您的阅读客户端使用与写作客户端相同的设置运行:只需投射到日期即可。timezone

    SELECT *
    FROM   articles
    WHERE  created_at::date = '2012-08-19';
    
  2. 您的阅读客户端使用与写入客户端不同的设置运行timezone:添加AT TIME ZONE '<tz name of *writing* client here>'. 例如,如果是Europe/Warsaw,它看起来像:

    ...
    WHERE (created_at AT TIME ZONE 'Europe/Warsaw')::date = '2012-08-19';
    

没有必要在您发布的答案中双重应用AT TIME ZONE您在发布的答案中拥有它。

注意时区名称而不是缩写。看:

如果您的应用程序跨越多个时区..

.. 将列默认设置created_atnow() AT TIME ZONE 'UTC'- 或其他时区,重点是:在任何地方都使用相同的。

.. 或者,最好切换到timestamptz( timestamp with time zone)。

于 2012-08-19T13:55:06.273 回答
1

链接的答案有帮助。我必须运行以下查询:

SELECT *
FROM   articles
WHERE (created_at AT TIME ZONE 'UTC' AT TIME ZONE 'CEST')::date = '2012-08-19';

这个问题需要列 created_at 的确切定义(究竟是什么数据类型?)

Rails 总是创建 created_at 列作为没有时区的时间戳。所以我必须首先AT TIME ZONE 'UTC'说 dbms 这个时间戳是在 UTC,第二个是在 CEST 区域显示日期。

于 2012-08-19T20:59:07.793 回答