Rails supports time zones, but there are several pitfalls. Most importantly because Time.now
and Time.current
are completely different things and code from gems might use one or the other.
Especially configuring an application that cares only about one time zone is a bit tricky.
The following was tested on Rails 5.1 but should apply to Rails 4.2 as well.
Your life will be easier if your application does not need to support time zones. Disable them like this:
config.time_zone = 'Berlin' # Your local time zone
config.active_record.default_timezone = :local
config.active_record.time_zone_aware_attributes = false
Note that we disable ActiveRecord's time zone logic while still configuring a time zone.
This seems odd at first, but is required for Time.current
to work properly.
If possible, put your server into local time zone, since Ruby's Time
will use that. In the example above, it should be in the Berlin zone.
If your application should support time zones, the default ActiveRecord configuration will respect a config.time_zone
. If you do not configure a default time zone, all times will be UTC.
You need to use Time#in_time_zone
or ActiveSupport::TimeWithZone#in_time_zone
on all time objects to convert them to a user's current time zone.
Read on for examples on different configurations and what will happen and/or go wrong.
This happens:
But:
Time.current
will be UTCTime.now
will be your local time (or the local time of your server)>> Project.create!
>> Project.connection.select_one('SELECT created_at FROM projects')
=> {"created_at"=>"2017-06-12 08:00:00.000000"}
>> Project.last.created_at
=> Mon, 12 Jun 2017 08:00:00 UTC +00:00
>> Time.now
=> 2017-06-12 10:00:00 +0200
>> Time.current
=> Mon, 12 Jun 2017 08:00:00 UTC +00:00
config.time_zone = 'Berlin'
This happens:
ActiveSupport::TimeWithZone
objectsTime.current
will be in the configured default time zoneTime.now
will be your local time (or the local time of your server)While this might be okay even for the "we don't need time zone" use case, UTC timestamps in the database are harder to read for humans.
>> Project.create!
>> Project.connection.select_one('SELECT created_at FROM projects')
=> {"created_at"=>"2017-06-12 08:00:00.000000"}
>> Project.last.created_at
=> Mon, 12 Jun 2017 10:00:00 CEST +02:00
>> Time.now
=> 2017-06-12 10:00:00 +0200
>> Time.current
=> Mon, 12 Jun 2017 10:00:00 CEST +02:00
config.active_record.default_timezone = :local
config.active_record.time_zone_aware_attributes = false
This happens:
Time
objectsBut:
Time.current
will be UTCWhile ActiveRecord seems to be working alright, using Time.current
is incorrect in this case and would cause problems.
>> Project.create!
>> Project.connection.select_one('SELECT created_at FROM projects')
=> {"created_at"=>"2017-06-12 10:00:00.000000"}
>> Project.last.created_at
=> 2017-06-12 10:00:00 +0200
>> Time.now
=> 2017-06-12 10:00:00 +0200
>> Time.current
=> Mon, 12 Jun 2017 08:00:00 UTC +00:00
config.time_zone = 'Berlin'
config.active_record.default_timezone = :local
config.active_record.time_zone_aware_attributes = false
This happens:
Time
objectsTime.current
will be in the configured (local) time zoneAs described above, this is our suggested configuration for an application that needs to support only one time zone.
Note that you can not distinguish times around a DST switch from summer time to winter time, as any time between 2 and 3 AM could be either!
>> Project.create!
>> Project.connection.select_one('SELECT created_at FROM projects')
=> {"created_at"=>"2017-06-12 10:00:00.000000"}
>> Project.last.created_at
=> 2017-06-12 10:00:00 +0200
>> Time.now
=> 2017-06-12 10:00:00 +0200
>> Time.current
=> Mon, 12 Jun 2017 10:00:00 CEST +02:00