Time zone is a tricky subject in any application. You could rapidly find yourself in a position where you tell your users we are Saturday but really it is Sunday where they live. Fortunately, Rails has some great built-in features to help us deal with it. Still, there is some gotchas you need to be aware of.
First of all, you need to know that there is 3 different time zone configurations in any Rails web app :
- Your system (or server) time zone configuration
- ActiveRecord time zone configuration
- Rails time zone configuration
What is the best configuration ?
The best configuration for an app that does not have to change its time zone based on its user, it is to change Rails timezone, but continue to have Active Record save in the database in UTC.
Just put this configuration in
# config/application.rb config.time_zone = 'Paris'
If you use this configuration…
- … ActiveRecord will write UTC to the database.
- … ActiveRecord will convert time attributes back to the configured default time zone.
Time.currentwill be in the configured default time zone (here Paris)
Time.nowwill be your local time (or the local time of your server)
To see the list of all available time zones, run this command:
$ rails time:zones:all
An alternative approach
This is not recommended, but if you want to change Rails timezone AND have Active Record store times in this timezone, you can do it as follow:
# config/application.rb config.time_zone = 'Paris' config.active_record.default_timezone = :local
/!\ Warning: remember that saving times in the database in a non-UTC format is not recommended and you should really think this through before implementing this approach.
Also important to note that
config.active_record.default_timezone can only take two values:
:local(converts to the timezone defined in
:utc(converts to UTC)
What about Heroku ?
You also need to take into account your server time zone.
Heroku, for instance, uses UTC by default.
To change this, set your preferred timezone using the TZ Database Timezone format :
$ heroku config:add TZ="Europe/Paris"
1.hours.ago # => Fri, 12 Oct 2018 16:40:36 AFT +02:00 2.day.from_now # => Sun, 14 Oct 2018 18:40:36 AFT +02:00 Time.zone.parse("2015-08-27 14:15:00 +0300") # => Thu, 27 Aug 2015 14:15:00 AFT +03:00 Time.current # => Thu, 27 Aug 2015 16:39:36 AFT +02:00 Time.current.utc.iso8601 # When you have to supply an API ("2018-10-12T14:40:36Z") Date.current # If you really can’t have a Time or DateTime (Thu, 27 Aug 2015) Date.current.in_time_zone # If you have a date and want to make the best out of it (Thu, 27 Aug 2015 00:00:00 AFT +04:30)
Date.today # This could be yesterday or tomorrow depending on the machine’s time zone (Sat, 13 Oct 2018) Time.now # Returns system time and ignores your configured time zone. (2018-10-12 18:12:34 +0200) Time.parse("2018-10-12T14:09:36Z") # Will assume time string given is in the system’s time zone. (2018-10-12 14:09:36 UTC) Time.strptime("2018-10-12T14:09:36Z", "%Y-%m-%dT%H:%M:%S%z") # Same problem as with Time.parse. (2018-10-12 14:09:36 UTC)