【问题标题】:Get records created after a particular time of day获取在一天中的特定时间之后创建的记录
【发布时间】:2017-02-17 21:25:21
【问题描述】:

假设我有一个 Event 模型,其中有一个 date_time 字段表示活动举行的日期时间,我想查看所有举行的活动,比如“晚上 10 点之后”或“早上 7 点之前”多个日期。我怎么能这样做?


我的第一个想法是这样的:

scope :after_time ->(time){ where("events.date_time::time between ?::time and '23:59'::time", time) }

但这不起作用,因为日期以 UTC 格式存储并由 ActiveRecord 转换为应用程序的时区。

假设我正在搜索下午 5 点之后的活动,从我当地的阿德莱德时间开始。最终的查询是这样的:

WHERE (events.date_time::time between '2016-10-09 06:30:00.000000'::time and '23:59'::time)

也就是说,因为我的时区是 +10:30(阿德莱德时间),它现在正在尝试计算上午 6:30 到午夜之间,它确实需要找到在世界标准时间上午 6:30 到下午 1:30 之间创建的时间。

现在,特别是对于这个例子,我可能会一起破解一些东西来计算出“午夜”时间需要给出时区差异。但如果该时间段跨越世界标准时间午夜,between <given time> and <midnight in Adelaide> 计算将不起作用。所以这个解决方案是失败的。


更新:

认为我已经设法通过反复试验获得了我想要的结果,但我不确定我是否完全理解发生了什么。

scope :after_time, ->(time) { time = time.strftime('%H:%M:%S') where_clause = <<-SQL (events.date_time at time zone 'UTC' at time zone 'ACDT')::time between ? and '23:59:59' SQL joins(:performances).where(where_clause, time) }

它基本上把所有东西都变成了一个时区,所以每一行的查询最终看起来像WHERE '20:30:00' between '17:00:00' and '23:59:59',所以我不必担心跨越午夜的时间。

即便如此,我觉得可能有合适的方法来做到这一点,所以我愿意接受建议。

【问题讨论】:

    标签: ruby-on-rails postgresql activerecord


    【解决方案1】:

    检查这是否适合你,

    s = DateTime.now.change(hour: 6, min: 30).utc
    e = Date.today.end_of_day.utc
    Event.where("date_time::time between ?::time and ?::time", s, e)
    

    【讨论】:

    • 你误解了这个问题。我需要跨多个日期获取事件,但在这些日期的特定时间之后/之前。例如,数据库中可能有 3 个事件,一个在 1 月 1 日下午 5 点,一个在 1 月 2 日下午 6 点,一个在 1 月 3 日晚上 7 点。我希望能够在下午 6 点以后(或等于)时间找到所有事件。也就是说,我需要一个能找到第二个和第三个事件的查询,而不是第一个。
    • @Obversity 我已经更新了我的答案。我认为它会为你工作。请在您的数据上进行测试。
    • 这里是生成的 where 子句:WHERE (events.date_time::time between '2016-10-08 20:00:00.000000'::time and '2016-10-09 13:29:59.999999'::time)。正如您可能猜到的那样,这不会找到任何结果,因为它将查找 '20:00:00' 和 '13:29:59' 之间的记录,这不是有效的时间段. (我之前实际上已经尝试过,我可能应该在问题中提到这一点。对不起!)
    【解决方案2】:

    这可能会对您有所帮助,然后您无需转换 DB 的每个日期,而是可以将参数化时间戳转换为 UTC 时间:

    scope :after, ->(start_time) { where('created_at::time > :time', time: start_time.utc.strftime('%H:%M:%S')) }
    

    现在, 例如我确实有以下时间戳的 3 个事件(均以 UTC 表示):

    1. 2013-04-11 11:43:43
    2. 2013-04-11 15:10:40
    3. 2013-04-12 07:39:26

    然后你可以调用:

    start_time = Time.zone.parse('2016-01-01 20:00:00')
    # => Fri, 01 Jan 2016 20:00:00 ACDT +10:30
    Event.after(start_time) # this will return 2 events(1, 2)
    

    查询将是:

    SELECT "events".* FROM "events" WHERE (created_at::time > '09:30:00')
    

    注意:如果您将此查询与具有created_at 列的任何其他模型一起使用,这将引发错误ActiveRecord::StatementInvalid: PG::AmbiguousColumn: ERROR: column reference "created_at" is ambiguous

    【讨论】:

      猜你喜欢
      • 2017-02-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-23
      • 1970-01-01
      • 1970-01-01
      • 2016-01-19
      相关资源
      最近更新 更多