Merge pull request #34 from lhz/intervals

Added :within option to Period to run only within given time interval.
This commit is contained in:
Dzmitry Plashchynski
2016-01-16 01:17:54 +02:00
5 changed files with 106 additions and 3 deletions

View File

@@ -6,6 +6,8 @@ require 'active_support/all'
require 'crono/version'
require 'crono/logging'
require 'crono/period'
require 'crono/time_of_day'
require 'crono/interval'
require 'crono/job'
require 'crono/scheduler'
require 'crono/config'

43
lib/crono/interval.rb Normal file
View File

@@ -0,0 +1,43 @@
module Crono
# Interval describes a period between two specific times of day
class Interval
attr_accessor :from, :to
def self.parse(value)
from_to =
case value
when Array then value
when Hash then value.values_at(:from, :to)
when String then value.split('-')
else
fail "Unknown interval format: #{value.inspect}"
end
from, to = from_to.map { |v| TimeOfDay.parse(v) }
new from, to
end
def initialize(from, to)
@from, @to = from, to
end
def within?(value)
tod = ((value.is_a? TimeOfDay) ? value : TimeOfDay.parse(value))
if @from <= @to
tod >= @from && tod < @to
else
tod >= @from || tod < @to
end
end
def next_within(time, period)
begin
time = period.since(time)
end until within? TimeOfDay.parse(time)
time
end
def to_s
"#{@from}-#{@to}"
end
end
end

View File

@@ -4,15 +4,25 @@ module Crono
DAYS = [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday,
:sunday]
def initialize(period, at: nil, on: nil)
def initialize(period, at: nil, on: nil, within: nil)
@period = period
@at_hour, @at_min = parse_at(at) if at
@interval = Interval.parse(within) if within
@on = parse_on(on) if on
end
def next(since: nil)
return initial_next unless since
@next = @period.since(since)
if @interval
if since
@next = @interval.next_within(since, @period)
else
return initial_next if @interval.within?(initial_next)
@next = @interval.next_within(initial_next, @period)
end
else
return initial_next unless since
@next = @period.since(since)
end
@next = @next.beginning_of_week.advance(days: @on) if @on
@next = @next.change(time_atts)
return @next if @next.future?
@@ -21,6 +31,7 @@ module Crono
def description
desc = "every #{@period.inspect}"
desc += " between #{@interval.from} and #{@interval.to} UTC" if @interval
desc += format(' at %.2i:%.2i', @at_hour, @at_min) if @at_hour && @at_min
desc += " on #{DAYS[@on].capitalize}" if @on
desc

36
lib/crono/time_of_day.rb Normal file
View File

@@ -0,0 +1,36 @@
module Crono
# TimeOfDay describes a certain hour and minute (on any day)
class TimeOfDay
include Comparable
attr_accessor :hour, :min
def self.parse(value)
time =
case value
when String then Time.parse(value).utc
when Hash then Time.now.change(value).utc
when Time then value.utc
else
fail "Unknown TimeOfDay format: #{value.inspect}"
end
new time.hour, time.min
end
def initialize(hour, min)
@hour, @min = hour, min
end
def to_i
@hour * 60 + @min
end
def to_s
'%02d:%02d' % [@hour, @min]
end
def <=>(other)
to_i <=> other.to_i
end
end
end

View File

@@ -110,6 +110,17 @@ describe Crono::Period do
expect(@period.next.utc.to_s).to be_eql Time.now.beginning_of_hour.advance(minutes: 15).utc.to_s
end
end
it 'should return next hour minutes within the given interval' do
Timecop.freeze(Time.now.change(hour: 16, min: 10)) do
@period = Crono::Period.new(1.hour, at: { min: 15 }, within: '08:00-16:00')
expect(@period.next.utc.to_s).to be_eql Time.now.tomorrow.change(hour: 8, min: 15).utc.to_s
end
Timecop.freeze(Time.now.change(hour: 16, min: 10)) do
@period = Crono::Period.new(1.hour, at: { min: 15 }, within: '23:00-07:00')
expect(@period.next.utc.to_s).to be_eql Time.now.change(hour: 23, min: 15).utc.to_s
end
end
end
end
end