Recently, we had a need to limit when certain rake tasks would run. The tasks sync our data with a third party data store that doesn’t change over night for our continental US-based customers. What we wanted is for the tasks to only run between 7am-1am Eastern (4am-10pm Western) which should cover typical waking hours. In other words, we want to ensure the tasks do not run between 1am-7am Eastern.
If you’ve ever worked with time zones before, then you know this is often trickier than you’d expect. This would be no different.
Thankfully, the Ruby Time and Rails ActiveSupport::TimeWithZone classes provide everything we need.
Goal: Do not run certain rake tasks between 1am-7am Eastern
We want an easy way to skip running certain tasks during certain hours of the day. In addtion, we want to be able to define those hours of the day using the time zone of our choice so that it’s easier to understand. Ideally, we’ll be able to write a little helper class so we can do something like this in our rake tasks:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Finding the correct time zone
To achieve our goal, we first need find out how Rails refers to the Eastern time zone. To see all time zones in Rails, just run rake time:zones:all
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Cool! For our purposes, we’ll need to use Eastern Time (US & Canada) when dealing with time zones.
Using our time zone in a UTC world
By default, Ruby and Rails default time to UTC. In Rails, this can be overridden in config/application.rb
:
1 2 |
|
In practice, it’s best to leave the system time zone set to UTC. Instead, we can use Rails Time.use_zone
to override Ruby Time.zone
inside a block. Once block execution completes, the original time zone is restored.
1 2 3 4 5 6 7 8 |
|
Putting it all together
Here’s a small module we can use in our rake task to see if it is “allowed” or “not allowed” at the current time. Notice we check to see if the time is allowed in the Time.use_zone
block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
Testing our implementation
An implementation is nice, but a tested, working implementation is better. Once again we’ll use Time.use_zone
, but we’ll also add ActiveSupport::Testing::TimeHelpers travel_to
to set the times we wish to test. The code inside the travel_to
block will use the time we provide, then revert to the original time after block execution completes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
|
Wrapping Up
Working with time zones is tricky. Many developers, myself included, have been tripped up by intricasies of working with time zones. However, by using Rails Time.use_zone
, we were able to achieve our stated goal of writing simple code to short-circuit rake tasks when they’re not allowed to run. And by using Rails test helper travel_to
we were able to write tests to ensure our helper class implemenation is working correctly.
Finally, many thanks to @datachomp and @jagthedrummer for helpful suggestions to improve this post.
Resources
There is much more to time zones in Ruby and Rails than covered here. These blog posts were super helpful in understanding how to deal with time zones in Rails.
Elle Meredith’s excellent two-part series: It’s About Time (Zones) and A Case Study in Multiple Time Zones.
And Nicklas Ramhö’s wonderful Working with time zones in Ruby on Rails.