mirror of
https://github.com/zammad/zammad
synced 2026-05-24 09:48:36 +00:00
174 lines
3.9 KiB
Ruby
174 lines
3.9 KiB
Ruby
# Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/
|
|
|
|
class TimeplanCalculation
|
|
DAY_MAP = {
|
|
0 => 'Sun',
|
|
1 => 'Mon',
|
|
2 => 'Tue',
|
|
3 => 'Wed',
|
|
4 => 'Thu',
|
|
5 => 'Fri',
|
|
6 => 'Sat'
|
|
}.freeze
|
|
|
|
attr_reader :timeplan
|
|
|
|
def initialize(timeplan, timezone)
|
|
@timeplan = timeplan.deep_transform_keys(&:to_s)
|
|
@timezone = timezone
|
|
end
|
|
|
|
# Checks if given time matches timeplan
|
|
# @param [Time]
|
|
# @return [Boolean]
|
|
def contains?(time)
|
|
return false if !valid?
|
|
|
|
time_in_zone = ensure_matching_time(time)
|
|
|
|
day?(time_in_zone) && hour?(time_in_zone) && minute?(time_in_zone)
|
|
end
|
|
|
|
# Calculates next time in timeplan after the given time
|
|
# @param [Time]
|
|
# @return [Time, nil]
|
|
def next_at(time)
|
|
return nil if !valid?
|
|
|
|
time_in_zone = ensure_matching_time(time)
|
|
|
|
next_run_at_same_day(time_in_zone) || next_run_at_coming_week(time_in_zone)
|
|
end
|
|
|
|
# Calculates previous time in timeplan before the given time
|
|
# @param [Time]
|
|
# @return [Time, nil]
|
|
def previous_at(time)
|
|
return nil if !valid?
|
|
|
|
time_in_zone = ensure_matching_time(time)
|
|
|
|
previous_run_at_same_day(time_in_zone) || previous_run_at_past_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
|
|
|
|
def match_minutes(minutes)
|
|
minutes / 10 * 10
|
|
end
|
|
|
|
def day?(time)
|
|
timeplan['days'][DAY_MAP[time.wday]]
|
|
end
|
|
|
|
def hour?(time)
|
|
timeplan.dig 'hours', time.hour.to_s
|
|
end
|
|
|
|
def minute?(time)
|
|
timeplan.dig 'minutes', match_minutes(time.min).to_s
|
|
end
|
|
|
|
def next_loop_minutes(base_time)
|
|
loop_minutes base_time, range: 0.step(50, 10)
|
|
end
|
|
|
|
def previous_loop_minutes(base_time)
|
|
loop_minutes base_time, range: 50.step(0, -10)
|
|
end
|
|
|
|
def loop_minutes(base_time, range:)
|
|
return if !hour?(base_time)
|
|
|
|
range
|
|
.lazy
|
|
.map { |minute| base_time.change min: minute }
|
|
.find { |time| minute?(time) }
|
|
end
|
|
|
|
def next_loop_hours(base_time)
|
|
loop_hours base_time, range: (base_time.hour..23), minutes_symbol: :next_loop_minutes
|
|
end
|
|
|
|
def previous_loop_hours(base_time)
|
|
loop_hours base_time, range: (0..base_time.hour).entries.reverse, minutes_symbol: :previous_loop_minutes
|
|
end
|
|
|
|
def loop_hours(base_time, range:, minutes_symbol:)
|
|
return if !day?(base_time)
|
|
|
|
range
|
|
.lazy
|
|
.map { |hour| send(minutes_symbol, base_time.change(hour: hour)) }
|
|
.find(&:present?)
|
|
end
|
|
|
|
def next_loop_partial_hour(base_time)
|
|
loop_partial_hour base_time, range: base_time.min.step(50, 10)
|
|
end
|
|
|
|
def previous_loop_partial_hour(base_time)
|
|
loop_partial_hour base_time, range: base_time.min.step(0, -10)
|
|
end
|
|
|
|
def loop_partial_hour(base_time, range:)
|
|
return if !day?(base_time)
|
|
|
|
range
|
|
.lazy
|
|
.map { |minute| base_time.change(min: minute) }
|
|
.find { |time| hour?(time) && minute?(time) }
|
|
end
|
|
|
|
def next_run_at_same_day(time)
|
|
day_to_check = time.change min: match_minutes(time.min)
|
|
|
|
if day_to_check.min.nonzero?
|
|
date = next_loop_partial_hour(day_to_check)
|
|
|
|
return date if date
|
|
|
|
day_to_check = day_to_check.change(min: 0)
|
|
day_to_check += 1.hour
|
|
end
|
|
|
|
next_loop_hours(day_to_check)
|
|
end
|
|
|
|
def next_run_at_coming_week(time)
|
|
(1..7)
|
|
.lazy
|
|
.map { |day| next_loop_hours (time + day.day).beginning_of_day }
|
|
.find(&:present?)
|
|
end
|
|
|
|
def previous_run_at_same_day(time)
|
|
day_to_check = time.change min: match_minutes(time.min)
|
|
|
|
if day_to_check.min.nonzero?
|
|
date = previous_loop_partial_hour(day_to_check)
|
|
|
|
return date if date
|
|
|
|
day_to_check = day_to_check.change(min: 0)
|
|
day_to_check -= 1.second
|
|
end
|
|
|
|
previous_loop_hours(day_to_check)
|
|
end
|
|
|
|
def previous_run_at_past_week(time)
|
|
(1..7)
|
|
.lazy
|
|
.map { |day| previous_loop_hours (time - day.day).end_of_day }
|
|
.find(&:present?)
|
|
end
|
|
end
|