преобразование объекта Date в TimeWithZone - PullRequest
6 голосов
/ 25 марта 2010

Мне нужно преобразовать объект Date в объект TimeWithZone, представляющий начало этого дня в данном часовом поясе.

Следующий подход работает, но кажется слишком запутанным, поскольку требует, чтобы я преобразовал дату в строку:

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

Есть ли лучший способ, по которому я скучаю?

Edit: Люди предлагают варианты:

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

Как видите, это не эквивалентное преобразование, поскольку оно оставляет меня в начале 16 февраля по восточному стандартному времени вместо начала 17 февраля по восточному поясному времени.

Ответы [ 5 ]

6 голосов
/ 02 ноября 2013

Я опаздываю на вечеринку, но это все еще отличный вопрос. ActiveSupport in_time_zone был введен после O.P., но он делает именно то, что вы хотите, без разбора строки (медленно) или установки Time.zone (рискованно):

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

Конечно, если вы хотите, чтобы начало дня было указано в utc, вы можете сделать это:

>> date.in_time_zone('Eastern Time (US & Canada)').utc
=> 2010-02-17 05:00:00 UTC
4 голосов
/ 01 июня 2012

Если в Rails установлено Time.zone, вы можете позвонить Date#at_beginning_of_day (см. http://api.rubyonrails.org/classes/Date.html#method-i-at_beginning_of_day). Сравните это с 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 голосов
/ 12 февраля 2013

Я настоятельно рекомендую против любого решения, которое преобразует дату во время, используя to_datetime или to_time, потому что эти методы не знают о зоне, и привязка in_time_zone к результату, как предлагают некоторые ответы, не задним числом исправить ошибку. Кроме того, не пытайтесь строить свою собственную математику летнего времени, используя смещения UTC. Вы непременно поймете это неправильно и выполняете работу без необходимости.

Используйте саму TimeZone, в которую встроена эта логика.

Учитывая зону и дату, вы можете получить TimeWithZone для начала дня следующим образом:

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

Если вам нужно определенное время дня, отличное от начала, вы можете передать час, минуту и ​​секунду в качестве 4-го, 5-го и 6-го аргументов #local.

Если zone на самом деле является локальным часовым поясом вашей системы (Time.zone), то ActiveSupport позволит вам сократить приведенное выше значение до:

time = date.to_time_in_current_zone

Все вышеперечисленное правильно обрабатывает переход на летнее время. Давайте проверим это, посмотрев на сдвиги UTC два раза, один вне DST, а второй внутри 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 голосов
/ 19 мая 2011

Вычесть 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 }

Использование ActiveSupport :: TimeZone [tz] не учитывает переход на летнее время.

Time.zone.class.all.map(&:name).map { |tz| o = d.to_datetime.in_time_zone(tz).utc_offset - ActiveSupport::TimeZone[tz].utc_offset }
0 голосов
/ 26 марта 2010

Хотелось бы что-нибудь подобное для вас?

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