2009-08-11 12 views
8

Ich habe eine Rails-Time-basierte Abfrage, die einige seltsame Zeitzone-sensitive Verhalten hat, obwohl, soweit ich weiß, ich UTC verwende. Auf den Punkt gebracht, geben diese Abfragen unterschiedliche Antworten:Warum unterscheidet sich diese Rails-Abfrage je nach Zeitzone?

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

Wo der DB tut eigentlich ein Modell in der letzten Stunde erstellt werden, enthalten, und die Gesamtzahl der Modelle ist 280. So wird nur die erste Abfrage ist richtig.

jedoch in environment.rb ich habe:

config.time_zone = 'UTC' 

Die System-Zeitzone (wie durch 'Datum' berichtete) ist BST (die GMT + 1) - so irgendwie windet sich dies bis behandelt zu werden als UTC und Brechen von Abfragen.

Dies verursacht mir alle Arten von Problemen, wie ich die Abfrage zu verschiedenen Zeiten zu einer Aktion (die dann mit Time.parse() konvertiert werden muss parametrieren), und obwohl ich in UTC-Zeiten sende ' um eine Stunde aus "DST Ausgabe Getreide viel. Selbst die Verwendung von '.gmtime()' scheint es nicht immer zu beheben.

Offensichtlich wird der Unterschied irgendwie durch eine implizite Konvertierung verursacht, die irgendwo dazu führt, dass BST falsch als UTC behandelt wird, aber warum? Speichern Schienen nicht die Zeitstempel in UTC? Ist die Time-Class-Zeitzone nicht bekannt? Ich benutze Rails 2.2.2

Also was ist hier los - und was ist die sichere Art und Weise um es zu programmieren?

bearbeiten, einige zusätzliche Informationen zu zeigen, was die DB und Time-Klasse tun:

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

Antwort

13

Die Time Klasse nicht direkt bewusst Ihrer konfigurierten Zeitzone ist. Rails 2.1 hat eine Reihe von Zeitzonen-Unterstützung hinzugefügt, aber Time wird weiterhin auf Ihre lokale Zeitzone wirken. Deshalb gibt Time.now eine BST-Zeit zurück.

Was Sie wahrscheinlich wollen, ist die Interaktion mit Time.zone. Sie können Methoden wie die Time-Klasse selbst aufrufen, sie wird jedoch in der angegebenen Zeitzone zurückgegeben.

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 

Eine andere Sache, die Sie vorsichtig sein, wenn Sie jemals Abfragen in der Datenbank zu tun, wo man mal vergleichen, aber sicher die UTC-Zeit zu verwenden, da Rails (auch wenn Sie eine andere Zeitzone angegeben haben) speichert UTC immer in der Datenbank.

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

Auch anstelle von Time.now-1.hour tun 1.hour.ago. Es ist einfacher zu lesen und Rails wird automatisch die konfigurierte Zeitzone verwenden.

+0

Interessante ... Time.utc für Time.gmtime ein Synonym ist? Auch bekomme ich >> Time.zone => # >. Ich denke, das Problem ist, dass meine Art zu denken "Di Aug 11 22:00:18 +0100 2009" und "Di Aug 11 21:00:22 UTC 2009" auf die gleiche logische Zeit bezogen sind. Ich nehme an, dass rails/ruby ​​den Offset beim Erstellen des SQL ignoriert. – frankodwyer

+1

Ich glaube, Time # utc ist nur ein Alias ​​für Time # gmtime.Außerdem wird der Zeitzonen-Offset ignoriert, wenn eine normale Zeit behandelt wird (Time.now), aber bei der Verwendung von Time.zone.now berücksichtigt. Also am besten immer Time.zone benutzen, dann solltest du nicht "utc" aufrufen müssen. – ryanb

+0

danke, das hilft sehr. Es ist für mich nicht intuitiv, dass es so funktioniert, aber zumindest verstehe ich, was jetzt passiert. Ich habe den Code entsprechend geändert und es scheint das Problem geklärt zu haben. Eine andere Sache, die meinen Code warf war, dass 1.Jahre nicht durch 1.Woche teilbar ist, wie ich es erwartet hatte ... Ich machte eine Schleife und bekam deswegen einen nicht verwandten, aber ähnlichen Fehler. – frankodwyer

0

Die Time-Zone Sie einstellen müssen, ist in Großbritannien, wird dies automatisch handhaben BST

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

Ich glaube, das sollte jetzt Time.zone = "London" sein – simonmorley

+0

Ja, das wäre jetzt 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

Nur ein Stück Code zu löschen ist selten hilfreich. Bitte erläutern Sie etwas, was der Code tut. – lexicore

Verwandte Themen