2009-08-11 9 views
8

Tengo una consulta basada en el tiempo de los raíles que tiene algún comportamiento extraño de zona horaria, aunque, por lo que sé, estoy usando UTC. En pocas palabras, estas consultas dan diferentes respuestas:¿Por qué esta consulta de rieles se comporta de manera diferente según la zona horaria?

>> 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 

Cuando el DB contiene realmente un modelo creado en la última hora, y el número total de modelos es 280. Así que sólo la primera consulta es correcta.

Sin embargo, en environment.rb tengo:

config.time_zone = 'UTC' 

La zona horaria del sistema (según lo informado por 'fecha') es BST (que es GMT + 1) - por lo que de alguna manera esto termina siendo tratados como UTC y consultas interrumpidas.

Esto me está causando todo tipo de problemas, ya que necesito parametrizar la consulta que pasa en momentos diferentes a una acción (que luego se convierten usando Time.parse()), y aunque envío horas UTC, esto ' fuera por una hora, el tema de DST cosecha mucho. Incluso el uso de '.gmtime()' no siempre parece arreglarlo.

Obviamente, la diferencia está causada de alguna manera por una conversión implícita en algún lugar que da lugar a que BST se trate incorrectamente como UTC, pero ¿por qué? ¿Los rieles no almacenan las marcas de tiempo en UTC? ¿No es consciente de la zona horaria de la clase Time? Estoy usando Rails 2.2.2

Entonces, ¿qué está pasando aquí, y cuál es la forma más segura de programarlo?

edición, algo de información adicional para mostrar lo que el PP y el horario de clases están haciendo:

>> 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 

Respuesta

13

La clase Time no es directamente consciente de su zona horaria configurada. Rails 2.1 agregó un montón de soporte de zona horaria, pero Time seguirá actuando en su zona horaria local. Esta es la razón por la que Time.now devuelve una hora BST.

Lo que probablemente quiera es interactuar con Time.zone. Puede invocar métodos como lo haría con la clase de tiempo en sí, pero la devolverá en la zona horaria especificada.

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 

Otra cosa que hay que tener cuidado con es si alguna vez las consultas en la base de datos donde están los tiempos comparando, pero asegúrese de usar la hora UTC (incluso si tiene una zona horaria diferente especificada), ya que los carriles siempre almacena UTC en la base de datos.

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

Además, en lugar de hacer Time.now-1.hour1.hour.ago. Es más fácil de leer y Rails utilizará automáticamente la zona horaria configurada.

+0

... Interesante es sinónimo de Time.utc Time.gmtime? También obtengo >> Time.zone => # >. Creo que el problema es que, a mi modo de pensar, "Tue Aug 11 22:00:18 +0100 2009" y "Mar Aug 11 21:00:22 UTC 2009" se refieren a la misma hora lógica. Supongo que rails/ruby ​​simplemente está ignorando el desplazamiento al construir el SQL. – frankodwyer

+1

Creo que Time # utc es solo un alias de Time # gmtime.Además, el desplazamiento de la zona horaria se ignora cuando se trata de un tiempo normal (Time.now) pero se tiene en cuenta cuando se utiliza Time.zone.now. Lo mejor es usar siempre Time.zone, entonces no debería tener que llamar "utc" en él. – ryanb

+0

gracias, eso ayuda mucho. no es intuitivo para mí que funcione de esa manera, pero al menos entiendo lo que está sucediendo ahora. Cambié el código en consecuencia y parece haber aclarado el problema. Otra cosa que arrojaba mi código era que 1.years no es divisible de manera uniforme por 1. semanas como esperaba ... Estaba haciendo un ciclo y obteniendo un error no relacionado pero similar debido a eso. – frankodwyer

0

la zona horaria es necesario establecer es Reino Unido, esto va a manejar de forma automática BST

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

Creo que esto debería ser ahora Time.zone = "London" – simonmorley

+0

Sí, esto ahora sería London – MatthewFord

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 
+0

Solo soltar un fragmento de código raramente es útil. Por favor explica un poco lo que hace el código. – lexicore

Cuestiones relacionadas