When reviewing Rails apps, I often find many uses of Time.now
and Date.today
, each of which comes from Ruby itself. Although these methods seem convenient and accessible, they should be avoided inside of Rails apps, as they aren’t converted to the local time zone set inside Rails.
Imagine the following (admittedly naive) method inside an Order model:
def shipped?
shipping_date < Date.today
end
By default, Date.today
and Time.now
will both use the system time and time zone of the server that’s serving the request. In a cloud environment, this could be the time zone where the servers are physically located or, more commonly, it is simply UTC.
Imagine the application runs for a company based in New York and generally doing business in the Eastern Time zone. At 10 PM Eastern Time on January 17th 2019, one would expect an Order with a shipping_date
of 2019-01-17
to not be shipped?
according to this logic. However, if the server time is UTC, the date will have already rolled over and Date.today
is going to return 2019-01-18
, thus breaking this method in an unexpected way.
There is an easy solution, however: Time.zone.today
and Time.zone.now
. In any Rails app, where the working time zone is set in application.rb
:
config.time_zone = 'Eastern Time (US & Canada)'
… then Time.zone.now
and Time.zone.today
will be automatically converted to that time zone.
In all cases inside a Rails app, one should use Time.zone.now
instead of Time.now
and Time.zone.today
instead of Date.today
to avoid unexpected bugs and time zone quirks. A good way to enforce the use of the Rails implementations of these is with RuboCop‘s built-in Rails::Date cop. RuboCop can be run locally or in CI. CodeClimate is a great SaaS tool that can run RuboCop and other static analysis for you and prevent merging of PRs that contain improper time zone usage.