module Mailings class SendClock def initialize(mailing, time: Time.zone.now) @mailing = mailing @time = time.in_time_zone end def open?(at = @time) t = at.in_time_zone return true unless @mailing.send_window_enabled? working_day?(t.to_date) && minutes_in_window?(t) end def next_send_at(earliest: @time) t = earliest.in_time_zone return t unless @mailing.send_window_enabled? next_open_at(from: t) end def next_open_at(from: @time) t = from.in_time_zone return t unless @mailing.send_window_enabled? 0.upto(21) do |offset| day = t.to_date + offset next unless working_day?(day) start_at = time_on(day, start_minutes) end_at = time_on(day, end_minutes) if offset.zero? return t if t >= start_at && t < end_at return start_at if t < start_at next end return start_at end t + 21.days end def window_label "#{format_minutes(start_minutes)}–#{format_minutes(end_minutes)}" end private def working_day?(date) date.on_weekday? && !Italy::Holidays.holiday?(date) end def minutes_in_window?(time) minutes = (time.hour * 60) + time.min minutes >= start_minutes && minutes < end_minutes end def start_minutes @mailing.send_window_start_minutes.presence || (10 * 60) end def end_minutes @mailing.send_window_end_minutes.presence || (18 * 60) end def time_on(date, minutes) if minutes >= (24 * 60) Time.zone.local(date.year, date.month, date.day).beginning_of_day + 1.day else Time.zone.local(date.year, date.month, date.day, minutes / 60, minutes % 60, 0) end end def format_minutes(minutes) format("%02d:%02d", minutes / 60, minutes % 60) end end end