-
Notifications
You must be signed in to change notification settings - Fork 2
Dates and Times in Rails
This is a cheat-sheet for dates and times Rails.
- Core Ruby has a
Time
class that supports UTC and the host computer's time zone, if different from UTC - The Ruby standard library has
Date
,DateTime
, andTime
classes - Rails has
Date
,DateTime
, andTime
classes - Rails provides a lot of support to handle most if not all time zones
- It's very easy to get confused with all these options
- http://danilenko.org/2012/7/6/rails_timezones/ is an excellent summary of some of what you need to know to effectively use date and time in a Rails app
- https://www.varvet.com/blog/working-with-time-zones-in-ruby-on-rails/ is also good. The two articles cover a lot of the same ground, but complement each other
- https://technopragmatica.blogspot.ca/2014/01/timezone-in-rails.html is my rather dated blog post about time in Rails
-
Leave the Rails server time zone as the host's time zone. There are enough things to confuse you without this additional variable
-
You need to be careful how you get the current date or time. Only certain ways use the Rails application time zone. The first two articles above have good summaries of this topic
-
You need to be careful if you parse times, for the same reason as above
-
You will need a preferred time zone for each user. In
app/controllers/application_controller.rb
, do this:def user_time_zone current_user.time_zone end
User#time_zone
returns a RailsActiveSupport::TimeZone
object. More on it later. -
Make sure that all controllers are using the user's time zone:
around_action :use_user_time_zone, if: :current_user def use_user_time_zone(&block) Time.use_zone(user_time_zone, &block) end
The
around_action
executes the controllers in a block, so the time zone gets unset after your action completes. It's possible to confuse Rails (apparently) if you just do abefore_action
. -
In testing, you should use a time zone that is neither the server's time zone, nor the time zone of the client where you're testing from. You're more likely to uncover problems where the time zone is not getting set correctly, if the tests run in a time zone that's different from your workstation and your server. I like to use Samoa's time zone, as it's one I'm not likely to ever actually be in.
This approach requires extra thinking in setting up and checking the test data, but that's sort of the point.
- For unit tests that will be testing time-related functionality, do this:
test "demo time test" do
Time.use_zone(ActiveSupport::TimeZone["Samoa"]) do
# Write your test here
end
end
- For other types of tests, you have to arrange for
user_time_zone
to return the right value for your test. Thearound_action
inApplicationController
will then handle it for you
Sometimes you need to test time-related events. For example, you need to test that the application generates a reminder when the current time is 24 hours before the scheduled time of some event. Rails has some simple built-in support (as of Rails 4.1): http://api.rubyonrails.org/v5.1.1/classes/ActiveSupport/Testing/TimeHelpers.html.
Using the built-in support, you could write a test case that sets the "time" to 24 hours before the start time of the event (which might be in a fixture, for example). Then you can always test the code without worrying about having to change the test data.
There is also the timecop
gem, which has extensive support for mucking with the time. One of the things
timecop
can do is simply freeze the time, which is useful in some cases. Note, however, that freezing the time is incompatible with Capybara testing, as it messes with Capybara's handling of asynchronous events in the browser.
There are some general tips and tricks about programming dates and times, but I'll have to leave that for another day.
[Collaborating on Projects](Using GitHub to Collaborate on We Enhance IT Projects)