【问题标题】:Why does this rails query behave differently depending on timezone?为什么这个 rails 查询的行为会因时区而异?
【发布时间】:2010-11-18 18:26:55
【问题描述】:

我有一个基于 Rails 时间的查询,它有一些奇怪的时区敏感行为,尽管据我所知我使用的是 UTC。简而言之,这些查询给出了不同的答案:

>> Model.find(:all,:conditions=>['created_at<=?',(Time.now-1.hours).gmtime]).length
=> 279
>> Model.find(:all,:conditions=>['created_at<=?',(Time.now-1.hours)]).length
=> 280

其中数据库实际上包含一个在过去一小时内创建的模型,模型总数为 280。因此只有第一个查询是正确的。

但是,在 environment.rb 我有:

config.time_zone = 'UTC'

系统时区(由“日期”报告)是 BST(即 GMT+1)——所以不知何故,这最终会被视为 UTC 并中断查询。

这给我带来了各种各样的问题,因为我需要将在不同时间传递的查询参数化为一个动作(然后使用 Time.parse() 进行转换),即使我以 UTC 时间发送,这个'off通过一小时的 DST 问题作物很多。即使使用 '.gmtime()' 似乎也不一定能解决问题。

显然,差异是由某处的隐式转换导致 BST 被错误地视为 UTC 造成的,但为什么呢? Rails 不是以 UTC 格式存储时间戳吗? Time 类时区不知道吗?我正在使用 Rails 2.2.2

那么这里发生了什么 - 围绕它进行编程的安全方法是什么?

编辑,一些额外的信息来显示 DB 和 Time 类在做什么:

>> Model.find(:last).created_at
=> Tue, 11 Aug 2009 20:31:07 UTC +00:00
>> Time.now
=> Tue Aug 11 22:00:18 +0100 2009
>> Time.now.gmtime
=> Tue Aug 11 21:00:22 UTC 2009

【问题讨论】:

    标签: ruby-on-rails datetime timezone dst


    【解决方案1】:

    Time 类不直接知道您配置的时区。 Rails 2.1 添加了许多时区支持,但Time 仍将作用于您的本地时区。这就是 Time.now 返回 BST 时间的原因。

    您可能想要与Time.zone 互动。您可以像调用 Time 类本身一样对此调用方法,但它会在指定的时区返回它。

    Time.zone.now # => Tue, 11 Aug 2009 21:31:45 UTC +00:00
    Time.zone.parse("2:30 PM Aug 23, 2009") # => Sun, 23 Aug 2009 14:30:00 UTC +00:00
    

    您必须注意的另一件事是,如果您曾经在比较时间的数据库上进行查询,但一定要使用 UTC 时间(即使您指定了不同的时区),因为 Rails 总是将 UTC 存储在数据库。

    Item.all(:conditions => ["published_at <= ?", Time.now.utc])
    

    另外,用1.hour.ago 代替Time.now-1.hour。它更易于阅读,Rails 会自动使用配置的时区。

    【讨论】:

    • 有趣...Time.utc 是 Time.gmtime 的同义词吗?我还得到 >> Time.zone => #<:timezone:0xb7b94c4c etc>>。我认为问题在于我的思维方式“2009 年 8 月 11 日星期二 22:00:18 +0100 2009”和“2009 年 8 月 11 日星期二 21:00:22 UTC”指的是相同的逻辑时间。我猜 Rails / ruby​​ 在构建 SQL 时只是忽略了偏移量。
    • 我相信 Time#utc 只是 Time#gmtime 的别名。此外,在处理正常时间 (Time.now) 时会忽略时区偏移,但在使用 Time.zone.now 时会考虑在内。所以最好总是使用 Time.zone 那么你不应该在它上面调用“utc”。
    • 谢谢,这很有帮助。它以这种方式工作对我来说是不直观的,但至少我了解现在正在发生的事情。我已经相应地更改了代码,它似乎已经解决了这个问题。抛出我的代码的另一件事是 1.years 不能像我预期的那样被 1.weeks 整除......我正在做一个循环并因此得到一个不相关但类似的错误。
    【解决方案2】:

    你需要设置的时区是英国,这会自动处理BST

    Time.zone = 'UK'
    Time.zone.now
     => Sun, 17 Oct 2010 02:09:54 BST +01:00
    

    【讨论】:

    • 我相信现在应该是 Time.zone = "London"
    • 是的,现在是伦敦
    【解决方案3】:
    start_date_format = DateTime.strptime(@start_date, date_format)
    start_date_format_with_hour = 
    DateTime.strptime((start_date_format.to_i + timezone_offset*60*60).to_s,'%s').strftime(date_format)
    
    end_date_format = DateTime.strptime(@end_date, date_format)
    end_date_format_with_hour = DateTime.strptime((end_date_format.to_i + timezone_offset*60*60).to_s,'%s').strftime(date_format)
    
    @filters_date = "invoices.created_at >= ? AND invoices.created_at < ?", start_date_format_with_hour, end_date_format_with_hour
    

    【讨论】:

    • 仅仅删除一段代码很少有帮助。请解释一下代码的作用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-02
    • 2011-10-18
    • 2010-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多