为什么这个rails查询的行为取决于时区?

时间:2021-06-20 04:16:33

I have a rails time-based query which has some odd timezone sensitive behaviour, even though as far as I know I'm using UTC. In a nutshell, these queries give different answers:

我有一个基于时间的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

Where the DB actually does contain one model created in the last hour, and the total number of models is 280. So only the first query is correct.

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

However, in environment.rb I have:

但是,在environment.rb中,我有:

config.time_zone = 'UTC'

The system time zone (as reported by 'date') is BST (which is GMT+1) - so somehow this winds up getting treated as UTC and breaking queries.

系统时区(由“日期”报告)是BST(GMT + 1) - 因此不知何故,这会被视为UTC并打破查询。

This is causing me all sorts of problems as I need to parameterise the query passing in different times to an action (which are then converted using Time.parse()), and even though I send in UTC times, this 'off by one hour' DST issue crops a lot. Even using '.gmtime()' doesn't always seem to fix it.

这引起了我各种各样的问题,因为我需要将在不同时间传递的查询参数化为一个动作(然后使用Time.parse()进行转换),即使我以UTC时间发送,这个'关闭一小时'DST问题很多。即使使用'.gmtime()'也似乎并不总能修复它。

Obviously the difference is caused somehow by an implicit conversion somewhere resulting in BST being incorrectly treated as UTC, but why? Doesn't rails store the timestamps in UTC? Isn't the Time class timezone aware? I am using Rails 2.2.2

显然,这种差异是由某个地方的隐式转换造成的,导致BST被错误地视为UTC,但为什么呢? rails是否以UTC格式存储时间戳? Time class timezone不是很清楚吗?我正在使用Rails 2.2.2

So what is going on here - and what is the safe way to program around it?

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

edit, some additional info to show what the DB and Time class are doing:

编辑,一些额外的信息来显示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

3 个解决方案

#1


13  

The Time class isn't directly aware of your configured timezone. Rails 2.1 added a bunch of timezone support, but Time will still act upon your local timezone. This is why Time.now returns a BST time.

Time类不直接了解您配置的时区。 Rails 2.1增加了一堆时区支持,但时间仍然会对你当地的时区起作用。这就是Time.now返回BST时间的原因。

What you likely want is to interact with Time.zone. You can call methods on this like you would the Time class itself but it will return it in the specified time zone.

您可能想要的是与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

Another thing you have to be careful with is if you ever do queries on the database where you are comparing times, but sure to use the UTC time (even if you have a different time zone specified) because Rails always stores UTC in the database.

另外需要注意的是,如果您对要比较时间的数据库进行查询,但确保使用UTC时间(即使您指定了不同的时区),因为Rails始终将UTC存储在数据库中。

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

Also, instead of Time.now-1.hour do 1.hour.ago. It is easier to read and Rails will automatically use the configured timezone.

而且,而不是Time.now-1.hour do 1.hour.ago。它更容易阅读,Rails将自动使用配置的时区。

#2


0  

The TimeZone you need to set is UK, this will automatically handle BST

您需要设置的TimeZone是UK,这将自动处理BST

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

#3


0  

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

#1


13  

The Time class isn't directly aware of your configured timezone. Rails 2.1 added a bunch of timezone support, but Time will still act upon your local timezone. This is why Time.now returns a BST time.

Time类不直接了解您配置的时区。 Rails 2.1增加了一堆时区支持,但时间仍然会对你当地的时区起作用。这就是Time.now返回BST时间的原因。

What you likely want is to interact with Time.zone. You can call methods on this like you would the Time class itself but it will return it in the specified time zone.

您可能想要的是与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

Another thing you have to be careful with is if you ever do queries on the database where you are comparing times, but sure to use the UTC time (even if you have a different time zone specified) because Rails always stores UTC in the database.

另外需要注意的是,如果您对要比较时间的数据库进行查询,但确保使用UTC时间(即使您指定了不同的时区),因为Rails始终将UTC存储在数据库中。

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

Also, instead of Time.now-1.hour do 1.hour.ago. It is easier to read and Rails will automatically use the configured timezone.

而且,而不是Time.now-1.hour do 1.hour.ago。它更容易阅读,Rails将自动使用配置的时区。

#2


0  

The TimeZone you need to set is UK, this will automatically handle BST

您需要设置的TimeZone是UK,这将自动处理BST

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

#3


0  

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