Scheduling email delivery in Rails

I’ve been banging my head against dealing with time zones in Rails for a few days now. I want users on my site to be able to pick a time of day to receive a reminder email. This is how I have my time configured in application.rb:

 config.time_zone = 'Eastern Time (US & Canada)'
config.active_record.default_timezone = :local

But when I set the reminder on Heroku and check in console, it looks like this:

User.last.reminder.time
=> 2000-01-01 11:00:00 +0000
User.last.reminder.time.zone
=> 'UTC'

My goal is to run a rake task every hour that will add upcoming reminders to an email queue. Any tips on getting this to work? Many thanks!

@charliegaines, even if you think you’re only going to be serving users in one time zone, I think you’re better off always storing the time in UTC and then doing the translation when you pull the time out of the database (or put it back in).

I’ve had a lot of luck with Sidekiq for scheduling jobs and delivering them reliably. You can schedule the generation of email delivery jobs in Sidekiq using an extension they have to ActionMailer.

Thanks @geoffharcourt. A separate problem re timezones. For an activity feed I want to display when users completed a given activity. Is the best approach to have a timezone column in the users table and set the current user’s time zone in ApplicationController on each visit? I don’t know why but this solution seems clunky to me.

You can use an around_filter to accomplish this task, but I find it easier and cleaner to just build a presenter object in the relevant controller that knows the user, the time zone (probably stored on the user table or maybe a user_preferences table), and whatever other model you were planning on displaying on the page. The presenter object knows to apply the user’s time zone to whatever time you fetch out of the database.

Presenters are just plain-old Ruby objects, you don’t need a special library to create them. They are a great way to adhere to the Sandi Metz rule that controllers can only create one instance variable that gets passed to your views.

Also: Basecamp likes to do this time zone conversion by storing the time in an HTML data attribute as UTC and then using JavaScript to make the conversion. I’ve had good experiences with Moment.js and Moment-Timezone. Basecamp does this because by storing UTC and then letting the user’s browser convert they can cache the same HTML for users in different time zones, but the technique can be used even if you aren’t doing any caching.

Just add another small tip to good response from @geoffharcourt. I found GitHub - alindeman/zonebie: Zonebie prevents bugs in code that deals with timezones by randomly assigning a zone on every run is good to find bugs in the app that bases on different timezone for requests.

Thanks @geoffharcourt and @samnang, very helpful advice!