2010-03-25 3 views
5

J'ai besoin de convertir un objet Date en un objet TimeWithZone représentant le début de ce jour dans un fuseau horaire donné.Conversion d'objet Date en TimeWithZone

L'approche suivante fonctionne, mais semble trop compliqué car il me demande de convertir la date à une chaîne:

?> date = Date.parse("2010-02-17") 
=> Wed, 17 Feb 2010 
>> ActiveSupport::TimeZone['Eastern Time (US & Canada)'].parse(date.to_s) 
=> Wed, 17 Feb 2010 00:00:00 EST -05:00 
>> ActiveSupport::TimeZone['UTC'].parse(date.to_s) 
=> Wed, 17 Feb 2010 00:00:00 UTC 00:00 

Y at-il une meilleure façon que je suis absent?

Edit: Les gens suggèrent des variations de:

?> date.to_datetime.in_time_zone('Eastern Time (US & Canada)').beginning_of_day 
=> Tue, 16 Feb 2010 00:00:00 EST -05:00 

Comme vous pouvez le voir, ce n'est pas une conversion équivalente car il me laisse au début du 16 février EST, au lieu du début de 17 février EST.

+0

Il semble que votre solution pourrait être la bonne façon d'aller. –

+0

J'ai modifié ma réponse pour résoudre ce problème. –

+0

Donnez-vous la solution! =) L'heure avec les fuseaux horaires est toujours désordonnée. – John

Répondre

0

Est-ce que quelque chose comme ceci fonctionnerait pour vous?

'2010-04-01'.to_time.in_time_zone('Eastern Time (US & Canada)').beginning_of_day 
+0

voir ma modification à la question pour la raison pour laquelle cela ne fonctionne pas – avaynshtok

0

Soustraire utc_offset:

d = Date.today 
Time.zone.class.all.map(&:name).map { |tz| dt = d.to_datetime.in_time_zone(tz); dt -= dt.utc_offset } 

En utilisant ActiveSupport :: TimeZone [tz] ne prend pas l'heure d'été en compte.

Time.zone.class.all.map(&:name).map { |tz| o = d.to_datetime.in_time_zone(tz).utc_offset - ActiveSupport::TimeZone[tz].utc_offset } 
4

Si vous avez Time.zone ensemble dans Rails vous pouvez appeler Date#at_beginning_of_day (voir http://api.rubyonrails.org/classes/Date.html#method-i-at_beginning_of_day). Cela contraste avec Date#to_datetime:

Time.zone 
=> #<ActiveSupport::TimeZone:0x10cf10858 @tzinfo=#<TZInfo::TimezoneProxy: Etc/UTC>, @utc_offset=nil, @current_period=nil, @name="UTC"> 

date = Date.today 
=> Thu, 31 May 2012 

date.to_datetime 
=> Thu, 31 May 2012 00:00:00 +0000 

date.at_beginning_of_day 
=> Thu, 31 May 2012 00:00:00 UTC +00:00 

Time.zone = 'America/Chicago' 
=> "America/Chicago" 

date.to_datetime 
=> Thu, 31 May 2012 00:00:00 +0000 

date.at_beginning_of_day 
=> Thu, 31 May 2012 00:00:00 CDT -05:00 
+2

'Time.zone' ** est ** threadsafe, il définit la zone dans« Thread.current [: time_zone] '. [source] (https://github.com/rails/rails/blob/08754f12e65a9ec79633a605e986d0f1ffa4b251/activesupport/lib/active_support/core_ext/time/zones.rb#L39) –

+1

Merci pour la correction, j'ai supprimé ce commentaire, mais je l'ai laissé un pour que les gens ne pensent pas que vous êtes fous;) – gtd

2

Je recommande fortement contre toute solution qui convertit la date à un moment à l'aide to_datetime ou to_time parce que ces méthodes ne sont pas conscients de la zone, et le clouage in_time_zone sur le résultat, comme certaines réponses suggèrent, ne corrigera pas rétroactivement l'erreur. En outre, n'essayez pas de créer votre propre calcul de l'heure d'été en utilisant les décalages UTC. Vous êtes obligé de vous tromper, et vous faites du travail inutilement. .

Utilisez le TimeZone lui-même qui a cette logique construite en

Étant donné une zone et une date, vous pouvez obtenir un TimeWithZone pour le début de la journée comme ceci:

time = zone.local(date.year, date.month, date.day) 

Si vous voulez une heure spécifique autre que le début, vous pouvez passer l'heure, la minute et la seconde en tant que 4ème, 5ème et 6ème arguments à #local.

Si zone est en fait l'heure locale de votre système zone (Time.zone), puis ActiveSupport vous permettra d'écourter ci-dessus à ceci:

time = date.to_time_in_current_zone 

Toute la lumière du jour poignée au-dessus d'économiser du temps correctement. Vérifions qu'en regardant les décalages UTC deux fois, celui qui est en dehors de l'heure d'été et qui est dans l'heure d'été:

irb(main):009:0> zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] 
=> (GMT-05:00) Eastern Time (US & Canada) 
irb(main):010:0> t1 = zone.local(2013, 1, 1) 
=> Tue, 01 Jan 2013 00:00:00 EST -05:00 
irb(main):011:0> t2 = zone.local(2013, 5, 1) 
=> Wed, 01 May 2013 00:00:00 EDT -04:00 
irb(main):012:0> t1.utc_offset 
=> -18000 
irb(main):013:0> t2.utc_offset 
=> -14400 
+0

a supprimé ma réponse et a voté pour vous. –

5

Je suis en retard à la fête, mais cela reste une grande question. Le in_time_zone d'ActiveSupport a été introduit depuis l'O.P., mais il fait exactement ce que vous voulez sans analyser une chaîne (lent) ou régler le temps.zone (risquée):

>> date = Date.parse("2010-02-17") 
=> Wed, 17 Feb 2010 
>> date.in_time_zone('Eastern Time (US & Canada)') 
=> Wed, 17 Feb 2010 00:00:00 EST -05:00 

Bien sûr, si vous voulez que le début de la journée exprimé à utc, vous pouvez le faire:

>> date.in_time_zone('Eastern Time (US & Canada)').utc 
=> 2010-02-17 05:00:00 UTC