Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Help grokking Time and Time Zones in Rails 3

I know there are a few dozen similar questions out there, not to mention countless articles on the Interwebs in general, but I'm still having a hard time understanding how Rails works with Time Zones internally.

I currently have config.time_zone = 'Eastern Time (US & Canada)' configured in my application file because that's where I and the other project administrators are. The company that owns the site is based in CA, so they will be in Pacific time. The application has a global audience and while we haven't done so yet we will eventually be implementing user preferred time zones.

So my current questions:

  1. I know Rails magically transforms datetime column values to and from UTC when stored and retrieved respectively, but what is the proper way to view the local version of a given datetime attribute?

  2. When would one use Time.now versus Time.zone.now versus Time.now.in_time_zone versus DateTime.now versus DateTime.now.in_time_zone?

  3. What is the proper way to compare a given datetime attribute with the methods listed above or some other specific time relative to the currently configured time zone? With UTC?

  4. We will have some time-sensitive things like articles that need to be published at a specific time according to the application's time zone, so how do I make the application do that comparison in our specified timezone as opposed to the currently configured one (assuming a user timezones are implemented?)

  5. (New Question) What happens if I change config.time_zone to UTC at a later date? Do I have to reset all of my times in the database or does it otherwise affect old times?

like image 225
Chris Bloom Avatar asked Jun 27 '11 16:06

Chris Bloom


1 Answers

First off, its important to understand that rails' timezone stuff is largely about presentation. Behind the scenes, everything happens in UTC.

Q1. At the console Rails will display times in you default timezone, so Something.last.created_at displays that timestamp in the zone given by Time.zone

Q2. All of these return an object that represents 'now'. The choice of DateTime versus Time is not related to timezones. If you need to be able to represent times outside of the unix epoch for example, use DateTime. The difference between Time.now and Time.zone.now is whether you get back an instance of Time (which will be in the server's local timezone as controller by). This controls for example what to_s returns but not what instant in time is represented:

SomeModel.create(:time_attribute => Time.now)
SomeModel.create(:time_attribute => Time.zone.now)

will insert the same row into the database. If you're just displaying a time to the user (for example if you're site displays the current time in the header) then you should use Time.zone.now so that it is displayed in the correct timezone. If you're just storing it in the db then it doesn't really matter - activerecord converts it to a TimeWithZone anyway.

Q3. The comparison methods on the TimeWithZone are implemented by comparing the utc version of the date, so you can safely compare times that are in different zones - you don't need to convert them to some common time zone. You can also compare TimeWithZone instances with plain time objects.

Q4. You normally don't need to do anything. A common way of implementing this would be to have a published_at attribute on your model. Pages displaying the list articles would add a

where('published_at <= ?', Time.now)

condition to the query. When you create your article, Rails takes the date time from the form and converts it to utc, so what is stored in the database is the utc version of that published_at time. Comparisons are time zone independant, so the query just works no matter what the time zone is.

This can get complicated with pure time of day stuff (eg '4pm') because a time zone conversion can only be done properly when you know the date too (i.e. you know the precise instant in time), as depending on DST (or political events like countries changing their timezone) the offset from UTC changes. In this sort of situation you normally want to store just the time of day and only convert it into a full time as late as possible, when you know the date too.

Q5. Rails always stores UTC in the database. A direct consequence of this is that changing config.time_zone doesn't require you to change the data stored in the database. You can even set Time.zone on a per user basis so that users see times in their timezone - config.time_zone just controls the default value of Time.zone

like image 137
Frederick Cheung Avatar answered Nov 08 '22 12:11

Frederick Cheung