Rails 6 adds support for timezones to Active Job


Rails 6 now preserves timezones for Active Job. It records what was the current timezone in effect when the job was enqueued and then restores when the job is executed.

Before

When using an application in different timezones, it can get tricky to handle timezones, on Rails Request level, passing to background jobs and more.

In one of our applications, we allow users to create tasks that can be assigned a due at date. This date can take “today”, “tomorrow”, etc relative date values, which are also relative to the timezone the user is accessing the Application in.
We process this task creation in a background job as it has some extra work that it needs to take care of after creating the task.

task = Task.new(task_params)
# params[:timezone] contains browser timezone value calculated 
# using https://momentjs.com/timezone/ library.  
TaskCreationJob.perform_later(task, params[:timezone])

class TaskCreationJob < ActiveJob::Base
    def perform(task, timezone)
        Time.use_zone(timezone) do
          # Persist task
          # Send notifications, triggers using zone and more
        end
    end
end

After

After changes in Rails 6 for ActiveJob, passing timezone to Job, is taken care of, by Rails itself.

Time.use_zone(params[:timezone]) do
# All operations for the Job 
# or inline will respect and use params[:timezone] as zone.
task = Task.new(task_params)  
TaskCreationJob.perform_later(task)
end

class TaskCreationJob < ActiveJob::Base
    def perform(task)
       # Time zone is set to the zone, when the Job was invoked.         
       # Persist task
       # Send notifications, triggers using zone and more      
    end  
end

Propagation to children

This kind of timezone propagation is also useful, when calling Jobs in a nested fashion. We don’t need to make sure or take care of, passing the timezone to all subsequent nested Jobs.

 Time.use_zone(user_timezone) do
   TaskCreationJob.perform_later(task) 
 end
 
 class TaskCreationJob < ActiveJob::Base
     def perform(task)
        # performs operation in user_timezone
        NotifyUserAboutTaskJob.perform_later(task)      
     end  
 end
 
 class NotifyUserAboutTaskJob < ActiveJob::Base
     def perform(task)
        # performs all subsequent jobs in user_timezone
         UserMailer.with(task: task).task_creation.deliver_later
         PushNotificationServiceJob.perform_later(task)      
      end
 end 
 

More useful ENV settings from Active Job

Apart from timezone, Rails already supported propagating the current locale when the Job is Enqueued

I18n.available_locales = [:en, :de]
I18n.locale = :de
TaskCreationJob.perform_later(task)

class TaskCreationJob < ActiveJob::Base
    def perform(task)
       # Uses :de locale when performing this task
       # or any other jobs invoked here
       # like sending emails.      
    end  
end