2010-03-25 14 views
5

Potrzebuję przekonwertować obiekt Date na obiekt TimeWithZone reprezentujący początek tego dnia w danej strefie czasowej.Konwertowanie obiektu Date na TimeWithZone

następujące podejście działa, ale wydaje się zbyt zawiłe, gdyż wymaga mi przekonwertować datę na ciąg znaków:

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

Czy istnieje lepszy sposób mi brakuje?

EDIT: Ludzie sugerują odmiany:

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

Jak widać, nie jest to równoważne konwersji, ponieważ na początku lutego 16 EST pozostawia mnie, zamiast na początku 17 lutego EST.

+0

Wygląda na to rozwiązanie może być właściwa droga. –

+0

Edytowałem swoją odpowiedź, aby rozwiązać ten problem. –

+0

Daj sobie rozwiązanie! =) Czas ze strefami czasowymi jest zawsze brudny. – John

Odpowiedz

0

Czy coś takiego działa dla Ciebie?

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

zobacz moją zmianę na pytanie, dlaczego to nie zadziała – avaynshtok

0

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

Korzystanie ActiveSupport :: TimeZone [tz] nie ma czasu letniego pod uwagę.

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

Jeśli masz Time.zone zestaw w Rails to można nazwać Date#at_beginning_of_day (patrz http://api.rubyonrails.org/classes/Date.html#method-i-at_beginning_of_day). Porównajmy to z 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' ** jest ** Threadafe, ustawia strefę w' Thread.current [: time_zone] '. [źródło] (https://github.com/rails/rails/blob/08754f12e65a9ec79633a605e986d0f1ffa4b251/activesupport/lib/active_support/core_ext/time/zones.rb#L39) –

+1

Dzięki za poprawkę, usunąłem ten komentarz, ale zostawiłem to tak, aby ludzie nie myśleli, że jesteś szalony;) ​​ – gtd

2

Zdecydowanie odradzam jakiekolwiek rozwiązanie, które konwertuje datę na czas korzystania to_datetime lub to_time ponieważ metody te nie są świadomi tej strefy, i sklejaniu in_time_zone na skutek, jak niektóre odpowiedzi sugerują, nie naprawi błędu z mocą wsteczną. Nie próbuj też budować własnej matematyki czasu letniego za pomocą przesunięć UTC. Z pewnością źle to zrobisz i niepotrzebnie pracujesz. .

Zastosowanie sama TimeZone, który ma tę logikę wbudowany

Biorąc pod uwagę strefę i datę, można uzyskać TimeWithZone na początku dnia tak:

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

Jeśli chcesz mieć określoną porę dnia inną niż początek, możesz przekazać godzinę, minutę i sekundę jako 4, 5 i 6 argumenty do #local.

Jeśli zone jest rzeczywiście lokalna strefa czasowa systemu (Time.zone), następnie ActiveSupport pozwoli skrócić powyższy do tego:

time = date.to_time_in_current_zone 

wszystkich powyższych uchwyt pozwala zaoszczędzić czas poprawnie świetle dziennym. Chodźmy sprawdzić, patrząc na offsetu UTC dwa razy, jeden, który jest poza DST i który w ciągu DST:

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

Usunąłem moją odpowiedź i przegłosowałem twoje. –

5

jestem późno do partii, ale to nadal jest wielkie pytanie. ActiveSupport's in_time_zone został wprowadzony od O.P., ale robi dokładnie to, czego chcesz, bez parsowania łańcucha (slow) lub ustawienia czasu.strefa (ryzykowne):

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

Oczywiście jeśli chcesz początek dnia wyrażoną w UTC, można to zrobić:

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