以文本形式查询日期时间并包含不同的时区

Query datetime as text and include different timezones

短版: 我在数据库中有 2 个用户,每个用户都在不同的时区创建:

User.find(1).created_at
=> Thu, 04 Aug 2016 11:15:29 IDT +03:00
User.find(33).created_at
=> Sun, 01 Jan 2017 17:50:20 IST +02:00

所以我的 table 显示 11:15 和 17:50。例如,我想搜索 17:50,然后搜索 11:15 作为文本:

search_param = '17:50'

没问题,我只是将日期转换为文本,然后进行搜索,但是找不到用户,因为它已保存为 UTC:

User.where("to_char(created_at,'DD-Mon-YYYY HH24:MI:SS') ilike ?", "%#{search_param}%").first
=> nil

为了找到它,我只需将偏移量应用于我的查询(添加时区 UTC+2),并且确实找到了用户:

User.where("to_char(created_at AT TIME ZONE 'UTC+2','DD-Mon-YYYY HH24:MI:SS') ilike ?", "%#{search_param}%").first
=> User #33 @2017-01-01 17:50:20 +0200

但是有些用户保存为 UTC+3,有些保存为 UTC+2..我不能同时应用两个偏移量...所以如果我将 search_param 更改为 11:15 我赢了找不到 user_id_1 因为我还需要将 UTC+2 更改为 UTC+3

我的问题:如何进行 where 查询 - 对两个用户的 created_at 小时进行文本搜索,因为他们都保存在不同的时区偏移量中?

或者在这个例子中一个查询对于search_param17:50会找到user_id_33并且对于search_param11:15 会找到 user_id_1?


更多详情:

我注意到在数据库中它们被保存为 UTC(我认为):

User.select("created_at as created_at_db").find(33).created_at_db
=> "2017-01-01 15:50:20.903289"
User.select("created_at as created_at_db").find(1).created_at_db
=> "2016-08-04 08:15:29.171776"

时间设置:

#application.rb
    config.time_zone = 'Jerusalem'
    config.active_record.default_timezone = :utc

created_at 列信息:

User.columns.select{|table_col| table_col.name == 'created_at'}
=> [#<ActiveRecord::ConnectionAdapters::PostgreSQLColumn:0x81f9c48
  @coder=nil,
  @default=nil,
  @limit=nil,
  @name="created_at",
  @null=false,
  @precision=nil,
  @primary=false,
  @scale=nil,
  @sql_type="timestamp without time zone",
  @type=:datetime>]

您应该能够通过以下方式查询您的用户实体:

date_trunc('minute', created_at AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Jerusalem')::time = '17:50'

或者,to_char()(但少index-friendly):

to_char(created_at AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Jerusalem', 'HH24:MI') = '17:50'

关键是 created_attimestamp(或 timestamp without time zone,这只是一个别名)。首先,您可以告诉 PostgreSQL,它使用 <timestamp> AT TIME ZONE <time zone> 运算符将其值解释为 UTC 中的值:

created_at AT TIME ZONE 'UTC'

然后,告诉 PostgreSQL 使用 <timestamp with time zone> AT TIME ZONE <time zone> 运算符(与上面的同名运算符完全不同)将此值偏移为 Asia/Jerusalem 中的本地时间:

created_at AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Jerusalem'

现在,您只需截断此值以仅提取小时和分钟部分。

也许值得一提的是使用这些:

created_at AT TIME ZONE 'UTC+2'
created_at AT TIME ZONE 'UTC+3'

不小心为你工作了。这些分别创建 timestamp with time zonewithin the -2 and -3 UTC 偏移量。然后,因为你的 config.active_record.default_timezone 设置为 :utc,它在 UTC 时区内发送给你的客户端 (ruby),这意味着它添加了 23小时,分别。

Another issue to keep in mind is that in POSIX time zone names, positive offsets are used for locations west of Greenwich. Everywhere else, PostgreSQL follows the ISO-8601 convention that positive timezone offsets are east of Greenwich.