8
votes

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?

1
This is a good question; I also have yet to find good docs about implementing time zones properly. I would add two questions to the list: 6. Is Rails sensitive to Daylight Savings Time and/or how do you implement time zone support that is sensitive? and 7. Is there a difference between how Rails handles time zones and DST for date columns vs. datetime columns? and perhaps 8. Is this database agnostic or does it need to be handled differently for SQLite vs. MySQL vs. others?Clay
@Clay: Were you able to find any more information on these questions? I'm still looking for more information on the subject.Chris Bloom
I haven't found much good information. Working with time zones is easily the most frustrating and time consuming part of the hobby app that I'm working with. There was a good reply to this question that I asked: stackoverflow.com/questions/8466903/… but it's only relevant for postgres.Clay

1 Answers

8
votes

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