2010-03-25 12 views
5

Ho bisogno di convertire un oggetto Date in un oggetto che rappresenta TimeWithZone l'inizio di quel giorno in una determinata fascia oraria.conversione Data oggetto TimeWithZone

L'approccio seguito funziona, ma sembra troppo complicata in quanto mi obbliga a convertire la data in una stringa:

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

Esiste un modo migliore che mi manca?

Modifica: persone sono suggerendo variazioni di:

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

Come si può vedere, non si tratta di una conversione equivalente dal momento che mi lascia all'inizio del 16 febbraio EST, invece l'inizio di 17 febbraio EST.

+0

Sembra che la soluzione potrebbe essere il modo giusto per andare. –

+0

Ho modificato la mia risposta per risolvere questo problema. –

+0

Datti la soluzione! =) Il tempo con i fusi orari è sempre disordinato. – John

risposta

0

Sarebbe qualcosa di simile a questo lavoro per voi?

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

vedi la mia modifica alla domanda per il motivo per cui questo non funziona – avaynshtok

0

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

Utilizzando ActiveSupport :: TimeZone [tz] non prende l'ora legale in considerazione.

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

Se hai Time.zone set in Rails, allora è possibile chiamare Date#at_beginning_of_day (vedi http://api.rubyonrails.org/classes/Date.html#method-i-at_beginning_of_day). Questo contrasto con 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' ** è ** threadsafe, imposta la zona in' Thread.current [: time_zone] '. [fonte] (https://github.com/rails/rails/blob/08754f12e65a9ec79633a605e986d0f1ffa4b251/activesupport/lib/active_support/core_ext/time/zones.rb#L39) –

+1

Grazie per la correzione, ho rimosso quel commento, ma ho lasciato questo uno così la gente non pensa che tu sia pazzo;) – gtd

2

vi raccomando vivamente contro qualsiasi soluzione che converte la data per volta utilizzando to_datetime o to_time perché questi metodi non sono a conoscenza della zona, e virate in_time_zone sul risultato, come alcune risposte suggeriscono, non risolverà retroattivamente l'errore. Inoltre, non provare a creare la tua matematica dell'ora legale utilizzando gli offset UTC. Sei destinato a sbagliare e stai facendo del lavoro inutilmente. .

Usa fuso orario in sé che ha questa logica costruita nel

Dato un orario e una data, è possibile ottenere un TimeWithZone per l'inizio della giornata come questa:

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

Se vuoi un orario specifico diverso dall'inizio, puoi passare l'ora, il minuto e il secondo come quarto, quinto e sesto argomento a #local.

Se zone è in realtà fuso orario locale del sistema (Time.zone), poi ActiveSupport vi permetterà di abbreviare quanto sopra a questo:

time = date.to_time_in_current_zone 

Tutto quanto sopra maniglia ora legale in modo corretto. Facciamo verificare che guardando le offset UTC per due volte, uno che è al di fuori DST e uno che è dentro l'ora legale:

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

Ho cancellato la mia risposta e ho aumentato il tuo. –

5

Sono in ritardo alla festa, ma questo è ancora una grande domanda. ActiveSupport's in_time_zone è stato introdotto da O.P., ma fa esattamente quello che vuoi senza analizzare una stringa (lento) o impostando il Tempo.Zona (rischioso):

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

Naturalmente se si desidera che l'inizio della giornata espressa in UTC, si può fare questo:

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