Time.now против Time.zone.now в Rails
Функции Time.now и Time.zone.now в Ruby on Rails практически идентичны. Возвращают одни и те же значения, ведут себя одинаково. Из-за этого легко перепутать и использовать более короткую формулировку Time.now. А зря – по крайней мере, если ты используешь часовой пояс, отличный от UTC.
В чем же разница между этими функциями? А вот в чем:
>> Time.now.class => Time >> Time.now.to_s :db => "2010-06-04 19:00:00" >> Time.zone.now.class => ActiveSupport::TimeWithZone >> Time.zone.now.to_s :db => "2010-06-04 16:00:11"
Дело в том, что Rails, начиная с версии 2.1, хранят время в базе в UTC, а при сохранении и чтении переводят его из и в часовой пояс, используемый в приложении.
За перевод времени в UTC отвечает, как ни странно, метод to_s(:db). Точнее странно то, что, хоть он и доступен у объекта типа Time, работает он неверно и отдает время в текущем часовом поясе. Если использовать Time.now в условиях запроса – они сместятся на определенное количество часов. Если записывать Time.now в базу – в базу запишется неправильное значение, потому что при чтении рельсы лишний раз добавят к нему смещение часового пояса.
Короче, наиболее простым и адекватным решением этой проблемы будет замена по всему проекту Time.now на Time.zone.now.
…А еще из-за это проблемы не стоит использовать в запросах функцию MySQL NOW() и аналогичные, поскольку они ничего не знают о настройках рельсов.

«NOW() и аналогичные» функции MySQL не стоит использовать еще и потому, что если в запросе есть эти функции – то результат запроса не кешируется.
Паша, но если в запросе фигурирует текущее время, то как ты его не пиши, все равно его кешировать бессмысленно.
FX Poster, видимо идет речь о вставке. что так кешировать?
Нет, не только о вставке, а и о выборке. Например, выборке объектов, у которых не истек какой-нибудь срок. Вот в таком случае запрос с NOW() будет возвращать (во 2 часовом поясе) объекты, которые уже два часа как истекли.
Я сейчас делаю сайт по теме планирования встреч (appointment scheduling) и мне пришлось вообще перекрыть метод to_s в классе ActiveSupport::TimeWithZone так, чтобы время в базе хранилось не в UTC. Требования таковы, что для каждого пользователя хранится часовой пояс и он должен иметь возможность его поменять в любой момент. При этом время для ранее забронированных встреч меняться не должно. Чуть позже хочу написать заметку про хранение времени в базе не в UTC для сайтов на Rails.
Так можно же указывать часовой пояс для каждого пользователя непосредственно в рельсе.
Мне кажется странным, что при смене часового пояса пользователем время встречи для него должно оставаться тем же. А как же другие участники той же встречи?
Не, нифига чувак ты не прав. У тебя время будет с точностью до секунды, скорее всего. А за секунду тот же MySQL может много операций выполнить при хорошей нагрузке.
Ну да, в before filter что-то типа Time.zone = @user.time_zone происходит.
Это пока еще Minimal Viable Product, если пользоваться модной терминологией, нужна максимальная гибкость, а потом возможно будут изменения согласно пожеланиям пользователей. У встречи только два участника – специалист и его посетитель. На сайте планируется заранее время, когда посетитель попадет к специалисту.
Спасибо, пригодилось
А где в конфигах Rails можно установить часовой пояс, который хочу использовать?
Ибо серверное время отличается от того пояса, который мне нужен (+1)
В Rails 2.3 –
config/environment.rb, в Rails 3 –config/application.rbНе прокатило =(
Value assigned to config.time_zone not recognized.Run «rake -D time» for a list of tasks for finding appropriate time zone names.
Rails 3.0.3
Тогда список доступных временных зон можно посмотреть командой
rake time:zones:all, что и предлагается. :)Да я посмотрел, и там точь-в-точь ‘UTC +01:00’ только с ноликом, как в вашем ответе!
А рельсы все равно ошибку выкатывают…
Я думаю, все-таки надо написать одну из зон, которые под UTC +01:00:
config.time_zone = "Prague"– так заработало!А
rake time:zones:allназвания городов не выводил :( Спасибо!А как же Time.now.utc ?
Можно, конечно, и
Time.now.utc. Но по-моему это неправильно, поскольку вносит в код лишнюю зависимость – знание часового пояса, используемого БД.Спасибо за разъяснение, меня всегда бесило странное поведение Time.now.
Но вообще это странно. Это больше похоже на баг.
статья потеряла актуальность?
удалите если так
Time.zone.now
=> Wed, 28 Dec 2011 14:36:52 YEKT +06:00
ruby-1.9.2-p290 :048 > Time.now
=> 2011-12-28 08:36:58 +0000
ruby-1.9.2-p290 :049 > Time.zone.now.to_s :db
=> «2011-12-28 08:37:07»
ruby-1.9.2-p290 :050 > Time.now.to_s :db
=> «2011-12-28 08:37:18»
ruby-1.9.2-p290 :051 >
Time.now.utc.to_s :db
=> «2011-12-28 08:40:57»