While Rails offers a number of useful classes and methods for manipulation of dates and times, it is not uncommon for developers to run into inconsistencies and issues when timezones begin to enter into the mix. Even for applications developed without a requirement of timezone support, often an application is actually dealing with a multitude of possible timezones.
Your Rails application will often need to correctly account for the timezone of the server running rails, the database server, and the Rails application itself.
To deal with these potential pitfalls, there are a few simple practices that you should begin implementing in your own development.
- Application Timezone
Begin by setting the timezone of your application, which can be done in the config/application.rb file using the config.time_zone method.
To see a list of appropriate timezone names, use the rake time:zones:all command in the terminal.
- ActiveRecord & UTC Conversion
Rails stores all dates and times in the database in UTC. With config.time_zone set for your application, Rails will attempt to convert ActiveRecord attributes from the UTC stored in the database to your application timezone. This means if you retrieve a time attribute from an ActiveRecord instance, the timezone will be automatically converted to the application timezone before being output:
If config.time_zone is unspecified, you’ll get the actual stored database value, which is in UTC:
- Use the Time Class, Not Date
If at all possible, always try to utilize the Time class in Rails instead of Date. This is simply because Date doesn’t innately manage time information, whereas the Time class does so, as expected.
- Parsing Input Times
When you need to parse a date and time from another source, always use Time.zone.parse (instead of Time.parse) to make use of the timezone specified in your application’s configuration.
- Beware of Database Engine and Time Comparisons
Be particularly mindful when performing comparisons of times from two different sources, since the comparison you expect may not actually be taking place.
For example, imagine a real-world scenario where your application needs to compare a date and time stored in the database with a date and time specified by the user. The user will, of course, use a datepicker object and provide your application with a date and time with precision up to the second if you require it. The user input that is processed by your application might look something like this:
In the application code, you might parse that user-input time like so:
Now, if your application attempts to compare the time parsed from the user above with the same date and time from a record in the database, you will often find that the two times do not, in fact, match as you would expect, in spite of being identical down to the second.
The issue here is that the database (in this case SQLite) may actually store time values with microsecond precision, whereas the user-specified time of course only has precision to the second. This difference can be seen by converting the times to floating point numbers:
To resolve this issue, be sure to convert your times to the appropriate precision first before making any comparisons. In the above example, if we only need to compare to the second, then the simplest option is to convert both times to integers, which is effectively evaluating the number of seconds in total:
On the flipside, also be aware of your database engine and what kind of time precision it is storing by default. This is particularly crucial for MySQL, which as of version 5.6.4 has support for times with fractional seconds, but you must configure your database to do so. See here.
Date and Time Cheat Sheet
To recap, here are a few good and bad practices working with time in Rails.
- The Good
- The Bad
Leave a Reply