Fixes #3802 - Job scheduling respects time zone
This commit is contained in:
parent
3f48c222b5
commit
e26d3ecfd3
4 changed files with 87 additions and 9 deletions
|
@ -81,7 +81,7 @@ job.run(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
def in_timeplan?(time = Time.zone.now)
|
def in_timeplan?(time = Time.zone.now)
|
||||||
Job::TimeplanCalculation.new(timeplan).contains?(time)
|
timeplan_calculation.contains?(time)
|
||||||
end
|
end
|
||||||
|
|
||||||
def matching_count
|
def matching_count
|
||||||
|
@ -89,6 +89,8 @@ job.run(true)
|
||||||
ticket_count || 0
|
ticket_count || 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
def next_run_at_calculate(time = Time.zone.now)
|
def next_run_at_calculate(time = Time.zone.now)
|
||||||
return nil if !active
|
return nil if !active
|
||||||
|
|
||||||
|
@ -96,11 +98,9 @@ job.run(true)
|
||||||
time += 10.minutes
|
time += 10.minutes
|
||||||
end
|
end
|
||||||
|
|
||||||
Job::TimeplanCalculation.new(timeplan).next_at(time)
|
timeplan_calculation.next_at(time)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def updated_matching
|
def updated_matching
|
||||||
self.matching = matching_count
|
self.matching = matching_count
|
||||||
end
|
end
|
||||||
|
@ -184,4 +184,10 @@ job.run(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def timeplan_calculation
|
||||||
|
timezone = Setting.get('timezone_default').presence || 'UTC'
|
||||||
|
|
||||||
|
Job::TimeplanCalculation.new(timeplan, timezone)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,24 +13,33 @@ class Job::TimeplanCalculation
|
||||||
|
|
||||||
attr_reader :timeplan
|
attr_reader :timeplan
|
||||||
|
|
||||||
def initialize(timeplan)
|
def initialize(timeplan, timezone)
|
||||||
@timeplan = timeplan.deep_transform_keys(&:to_s)
|
@timeplan = timeplan.deep_transform_keys(&:to_s)
|
||||||
|
@timezone = timezone
|
||||||
end
|
end
|
||||||
|
|
||||||
def contains?(time)
|
def contains?(time)
|
||||||
return false if !valid?
|
return false if !valid?
|
||||||
|
|
||||||
day?(time) && hour?(time) && minute?(time)
|
time_in_zone = ensure_matching_time(time)
|
||||||
|
|
||||||
|
day?(time_in_zone) && hour?(time_in_zone) && minute?(time_in_zone)
|
||||||
end
|
end
|
||||||
|
|
||||||
def next_at(time)
|
def next_at(time)
|
||||||
return nil if !valid?
|
return nil if !valid?
|
||||||
|
|
||||||
next_run_at_same_day(time) || next_run_at_coming_week(time)
|
time_in_zone = ensure_matching_time(time)
|
||||||
|
|
||||||
|
next_run_at_same_day(time_in_zone) || next_run_at_coming_week(time_in_zone)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def ensure_matching_time(time)
|
||||||
|
time.in_time_zone @timezone
|
||||||
|
end
|
||||||
|
|
||||||
def valid?
|
def valid?
|
||||||
timeplan.key?('days') && timeplan.key?('hours') && timeplan.key?('minutes')
|
timeplan.key?('days') && timeplan.key?('hours') && timeplan.key?('minutes')
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Job::TimeplanCalculation do
|
RSpec.describe Job::TimeplanCalculation do
|
||||||
subject(:instance) { described_class.new(timeplan) }
|
subject(:instance) { described_class.new(timeplan, timezone) }
|
||||||
|
|
||||||
|
let(:timezone) { nil }
|
||||||
|
|
||||||
describe '#contains?' do
|
describe '#contains?' do
|
||||||
context 'without a valid timeplan' do
|
context 'without a valid timeplan' do
|
||||||
|
@ -55,9 +57,17 @@ RSpec.describe Job::TimeplanCalculation do
|
||||||
it { is_expected.not_to be_contains(Time.zone.parse('2020-12-22 9:20')) }
|
it { is_expected.not_to be_contains(Time.zone.parse('2020-12-22 9:20')) }
|
||||||
it { is_expected.not_to be_contains(Time.zone.parse('2020-12-20 9:20')) }
|
it { is_expected.not_to be_contains(Time.zone.parse('2020-12-20 9:20')) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with monday 09:00 and time zone' do
|
||||||
|
let(:timeplan) { { 'days' => { 'Mon' => true }, 'hours' => { '1' => true }, 'minutes' => { '0' => true } } }
|
||||||
|
let(:timezone) { 'Europe/Vilnius' }
|
||||||
|
|
||||||
|
it { is_expected.to be_contains(Time.use_zone('Europe/Vilnius') { Time.zone.parse('2020-12-21 01:00') }) }
|
||||||
|
it { is_expected.to be_contains(Time.zone.parse('2020-12-20 23:00')) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'next_at?' do
|
describe '#next_at' do
|
||||||
context 'without a valid timeplan' do
|
context 'without a valid timeplan' do
|
||||||
let(:timeplan) { {} }
|
let(:timeplan) { {} }
|
||||||
|
|
||||||
|
@ -125,6 +135,25 @@ RSpec.describe Job::TimeplanCalculation do
|
||||||
it { expect(instance.next_at(Time.zone.parse('2020-12-22 9:20'))).to eq(Time.zone.parse('2020-12-28 09:10')) }
|
it { expect(instance.next_at(Time.zone.parse('2020-12-22 9:20'))).to eq(Time.zone.parse('2020-12-28 09:10')) }
|
||||||
it { expect(instance.next_at(Time.zone.parse('2020-12-20 9:20'))).to eq(Time.zone.parse('2020-12-21 09:10')) }
|
it { expect(instance.next_at(Time.zone.parse('2020-12-20 9:20'))).to eq(Time.zone.parse('2020-12-21 09:10')) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with monday 09:00 and time zone' do
|
||||||
|
let(:timezone) { 'Europe/Vilnius' }
|
||||||
|
let(:timeplan) { { 'days' => { 'Mon' => true }, 'hours' => { '1' => true }, 'minutes' => { '0' => true } } }
|
||||||
|
|
||||||
|
it 'calculates time respecting time zone' do
|
||||||
|
from = Time.use_zone('Europe/Vilnius') { Time.zone.parse('2020-12-21 01:15') }
|
||||||
|
to = Time.use_zone('Europe/Vilnius') { Time.zone.parse('2020-12-28 01:00') }
|
||||||
|
|
||||||
|
expect(instance.next_at(from)).to eq(to)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'calculates time converting to time zone' do
|
||||||
|
from = Time.zone.parse('2020-12-20 23:10')
|
||||||
|
to = Time.use_zone('Europe/Vilnius') { Time.zone.parse('2020-12-28 01:00') }
|
||||||
|
|
||||||
|
expect(instance.next_at(from)).to eq(to)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'legacy tests moved from Job model' do
|
describe 'legacy tests moved from Job model' do
|
||||||
|
|
|
@ -345,6 +345,22 @@ RSpec.describe Job, type: :model do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#in_timeplan?' do
|
||||||
|
before do
|
||||||
|
job.timeplan = { days: { Mon: true }, hours: { 0 => true }, minutes: { 0 => true } }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'checks in selected time zone' do
|
||||||
|
Setting.set 'timezone_default', 'Europe/Vilnius'
|
||||||
|
|
||||||
|
expect(job).to be_in_timeplan Time.zone.parse('2020-12-27 22:00')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'checks in UTC' do
|
||||||
|
expect(job).to be_in_timeplan Time.zone.parse('2020-12-28 00:00')
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'Attributes:' do
|
describe 'Attributes:' do
|
||||||
|
@ -451,6 +467,24 @@ RSpec.describe Job, type: :model do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when updating #next_run_at' do
|
||||||
|
before do
|
||||||
|
travel_to Time.zone.parse('2020-12-27 00:00')
|
||||||
|
|
||||||
|
job.timeplan = { days: { Mon: true }, hours: { 0 => true }, minutes: { 0 => true } }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets #next_run_at time in selected time zone' do
|
||||||
|
Setting.set 'timezone_default', 'Europe/Vilnius'
|
||||||
|
|
||||||
|
expect { job.save }.to change(job, :next_run_at).to(Time.zone.parse('2020-12-27 22:00'))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets #next_run_at time in UTC' do
|
||||||
|
expect { job.save }.to change(job, :next_run_at).to(Time.zone.parse('2020-12-28 00:00'))
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#perform' do
|
describe '#perform' do
|
||||||
|
|
Loading…
Reference in a new issue