diff --git a/app/models/job.rb b/app/models/job.rb index 76e361533..e52d5b73b 100644 --- a/app/models/job.rb +++ b/app/models/job.rb @@ -81,7 +81,7 @@ job.run(true) end def in_timeplan?(time = Time.zone.now) - Job::TimeplanCalculation.new(timeplan).contains?(time) + timeplan_calculation.contains?(time) end def matching_count @@ -89,6 +89,8 @@ job.run(true) ticket_count || 0 end + private + def next_run_at_calculate(time = Time.zone.now) return nil if !active @@ -96,11 +98,9 @@ job.run(true) time += 10.minutes end - Job::TimeplanCalculation.new(timeplan).next_at(time) + timeplan_calculation.next_at(time) end - private - def updated_matching self.matching = matching_count end @@ -184,4 +184,10 @@ job.run(true) end end end + + def timeplan_calculation + timezone = Setting.get('timezone_default').presence || 'UTC' + + Job::TimeplanCalculation.new(timeplan, timezone) + end end diff --git a/app/models/job/timeplan_calculation.rb b/app/models/job/timeplan_calculation.rb index 3b989fbc5..0fad9ca29 100644 --- a/app/models/job/timeplan_calculation.rb +++ b/app/models/job/timeplan_calculation.rb @@ -13,24 +13,33 @@ class Job::TimeplanCalculation attr_reader :timeplan - def initialize(timeplan) + def initialize(timeplan, timezone) @timeplan = timeplan.deep_transform_keys(&:to_s) + @timezone = timezone end def contains?(time) 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 def next_at(time) 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 private + def ensure_matching_time(time) + time.in_time_zone @timezone + end + def valid? timeplan.key?('days') && timeplan.key?('hours') && timeplan.key?('minutes') end diff --git a/spec/models/job/timeplan_calculation_spec.rb b/spec/models/job/timeplan_calculation_spec.rb index 9e778b545..5ee453652 100644 --- a/spec/models/job/timeplan_calculation_spec.rb +++ b/spec/models/job/timeplan_calculation_spec.rb @@ -3,7 +3,9 @@ require 'rails_helper' RSpec.describe Job::TimeplanCalculation do - subject(:instance) { described_class.new(timeplan) } + subject(:instance) { described_class.new(timeplan, timezone) } + + let(:timezone) { nil } describe '#contains?' 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-20 9:20')) } 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 - describe 'next_at?' do + describe '#next_at' do context 'without a valid timeplan' do 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-20 9:20'))).to eq(Time.zone.parse('2020-12-21 09:10')) } 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 describe 'legacy tests moved from Job model' do diff --git a/spec/models/job_spec.rb b/spec/models/job_spec.rb index 97c429388..4b205f383 100644 --- a/spec/models/job_spec.rb +++ b/spec/models/job_spec.rb @@ -345,6 +345,22 @@ RSpec.describe Job, type: :model do 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 describe 'Attributes:' do @@ -451,6 +467,24 @@ RSpec.describe Job, type: :model do 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 describe '#perform' do