Я настоятельно рекомендую против любого решения, которое преобразует дату во время, используя 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