Подводные камни и true way :)
При использовании класса Time в ruby используется серверное время, как точка отсчета времени.
$ date
Sat Sep 21 16:49:56 CEST 2013
$ irb
2.0.0-p247 :001 > Time.now
=> 2013-09-21 16:49:59 +0200
Чем это может быть опасно?
Допустим вы разрабатываете программу, которая печатает время прилета самолетов. В вашем часовом поясе сейчас 17:00, а на компьютере установлено правильное время.
time = Time.new(2013, 09, 21, 16, 49, 59)
puts "#{time} - #{plane.name} now #{message}"
2.0.0-p247 :001 > plane_message (plane, "landed")
=> 2013-09-21 16:49:59 +0100 - Airbus 22 now landed
С этой программой все хорошо, пока мы исполняем ее у себя на сервере. Стоит нам ее вылить, например, на продакшен, который находится в другом часовом поясе или случайно не правильно настроен, как мы получим странный баг – время будет отличным от ожидаемого. И все потому, что ruby взяло за точку отсчета таймзону сервера.
Получить ожидаемый результат довольно просто – нужно просто передавать нужное смещение в конструктор:
time = Time.new(2013, 09, 21, 16, 49, 59, "+02:00")
Над полученным объектом можно выполнять различные преобразования и все они будут представлены в указанной таймзоне.
Согласитесь, что каждый раз помнить о таймзоне рано или поздно надоест! :)
Например, в фреймворке Ruby on Rails эта проблема решается конфигурируемой таймзоной.
#config/application.rb
config.time_zone = 'Europe/Moscow'
В фреймворке переопределен класс Time (rails Time) и умеет работать с нашей конфигурируемой таймзоной! Теперь, чтобы всегда использовать нужный часовой пояс, не нужно передавать смещение в конструктор класса Time. Просто вызывайте метод zone():
2.0.0-p247 :001 > Time.now()
=> 2013-09-21 17:42:33 +0200
2.0.0-p247 :002 > Time.zone.now()
=> Sat, 21 Sep 2013 19:42:38 MSK +04:00
Ну и если очень хочется сменить точку отсчета времени, например парсить время относительно Новосибирска, находясь в Московской зоне, можно либо вручную переписать таймзону (и не забыть ее вернуть обратно), либо использовать возможности rails:
Time.use_zone('Novosibirsk') do
Time.zone.now
end
Что хотелось бы отметить – рельсовый ActiveRecord умеет работать с временными зонами и использует зону из application.rb. В базу данных, по умолчанию, сохраняется время в UTC, т.е. переводится в +0. Для этого используют метод to_s(:db). В большинстве случаев вызывать его нет необходимости, так как rails сами его вызовут.
Исходя из того, что рельсы сохраняют время в UTC, то и из базы они хотят получить время в UTC. А это значит, что хранить таймзону в бд нам не нужно.
UPD 1:
В качестве альтернативы к Time.zone.now можно использовать Time.current.
Его результат – вызов Time.zone.now, но только при установленной таймзоне в конфигурационном файле (или напрямую через Time.zone), иначе
возвращает Time.now. На мой взгляд, поведение этого метода в некоторой степени неявно – необходимо понимать, какой результат будет в
конкретном случае использования.