2009-08-11 9 views
8

J'ai une requête basée sur le temps de rails qui a un comportement bizarre sensible au fuseau horaire, même si pour autant que je sais que j'utilise UTC. En un mot, ces requêtes donnent des réponses différentes:Pourquoi cette requête de rails se comporte-t-elle différemment selon le fuseau horaire?

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

Lorsque le DB contient en fait un modèle créé dans la dernière heure, et le nombre total de modèles est 280. Ainsi, seule la première requête est correcte.

Cependant, je environment.rb:

config.time_zone = 'UTC' 

Le temps du système zone (tel que rapporté par 'date') est BST (qui est GMT + 1) - donc en quelque sorte ce serpente jusqu'à se traiter comme UTC et briser les requêtes. Cela me pose toutes sortes de problèmes car j'ai besoin de paramétrer la requête passant à différents moments d'une action (qui est ensuite convertie en utilisant Time.parse()), et même si j'envoie des temps UTC, ceci ' au large d'une heure, le problème de l'heure d'été est très important. Même en utilisant '.gmtime()' ne semble pas toujours le réparer.

Évidemment, la différence est causée d'une manière ou d'une autre par une conversion implicite quelque part, ce qui fait que la BST est mal traitée comme UTC, mais pourquoi? Les rails ne stockent-ils pas les horodatages en UTC? Le fuseau horaire Time Class n'est-il pas au courant? J'utilise Rails 2.2.2

Alors, que se passe-t-il ici - et quel est le moyen sûr de programmer autour d'elle?

modifier, quelques informations supplémentaires pour montrer ce que la DB et la classe du temps font:

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

Répondre

13

La classe Time n'est pas directement au courant de votre fuseau horaire configuré. Rails 2.1 a ajouté un tas de prise en charge du fuseau horaire, mais Time agira toujours sur votre fuseau horaire local. C'est pourquoi Time.now renvoie une heure BST.

Ce que vous voulez probablement est d'interagir avec Time.zone. Vous pouvez appeler des méthodes comme la classe Time elle-même, mais elle le retournera dans le fuseau horaire spécifié.

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 

Une autre chose que vous devez être prudent avec est si vous faites jamais des requêtes sur la base de données où vous comparez fois, mais sûr d'utiliser le temps UTC (même si vous avez un fuseau horaire différent spécifié) parce que Rails stocke toujours UTC dans la base de données.

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

En outre, au lieu de faire Time.now-1.hour1.hour.ago. Il est plus facile à lire et Rails utilisera automatiquement le fuseau horaire configuré.

+0

Intéressant ... est Time.utc synonyme de Time.gmtime? J'ai aussi >> Time.zone => # >. Je pense que le problème est que selon ma façon de penser "Tue Aug 11 22:00:18 +0100 2009" et "Tue Aug 11 21:00:22 UTC 2009" se réfèrent à la même heure logique. Je suppose que rails/ruby ​​ignore simplement le décalage lors de la construction du SQL. – frankodwyer

+1

Je crois que Time # utc n'est qu'un alias de Time # gmtime.De même, le décalage de fuseau horaire est ignoré lors de l'utilisation d'une heure normale (Time.now) mais pris en compte lors de l'utilisation de Time.zone.now. Donc, mieux vaut toujours utiliser Time.zone alors vous ne devriez pas avoir à appeler "utc" dessus. – ryanb

+0

merci, cela aide beaucoup. il est inintéressant pour moi que cela fonctionne de cette façon mais au moins je comprends ce qui se passe maintenant. J'ai changé le code en conséquence et il semble avoir résolu le problème. Une autre chose qui jetait mon code était que 1.years n'est pas divisible par 1.weeks comme je m'attendais à ce que ce soit ... Je faisais une boucle et obtenais une erreur non liée mais semblable à cause de cela. – frankodwyer

0

fuseauHoraire vous devez définir est au Royaume-Uni, cela gérera automatiquement BST

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

Je crois que cela devrait maintenant être Time.zone = "London" – simonmorley

+0

Oui, ce serait maintenant Londres – 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

Juste tomber un morceau de code est rarement utile. S'il vous plaît expliquer un peu ce que fait le code. – lexicore