Rails 6 now preserves timezones for Active Job. It records what the current timezone in effect was 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