From a93b937d1424b737cda03f9000483df175d8d32d Mon Sep 17 00:00:00 2001 From: Lars Haugseth Date: Sat, 3 Oct 2015 15:07:59 +0200 Subject: [PATCH] Added :within option to Period to run only within given time interval. --- lib/crono.rb | 2 ++ lib/crono/interval.rb | 43 ++++++++++++++++++++++++++++++++++++++++ lib/crono/period.rb | 17 +++++++++++++--- lib/crono/time_of_day.rb | 38 +++++++++++++++++++++++++++++++++++ spec/period_spec.rb | 11 ++++++++++ 5 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 lib/crono/interval.rb create mode 100644 lib/crono/time_of_day.rb diff --git a/lib/crono.rb b/lib/crono.rb index 81632ca..596f997 100644 --- a/lib/crono.rb +++ b/lib/crono.rb @@ -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' diff --git a/lib/crono/interval.rb b/lib/crono/interval.rb new file mode 100644 index 0000000..b534daf --- /dev/null +++ b/lib/crono/interval.rb @@ -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 diff --git a/lib/crono/period.rb b/lib/crono/period.rb index 92b7d9e..75c3309 100644 --- a/lib/crono/period.rb +++ b/lib/crono/period.rb @@ -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}" if @interval desc += format(' at %.2i:%.2i', @at_hour, @at_min) if @at_hour && @at_min desc += " on #{DAYS[@on].capitalize}" if @on desc diff --git a/lib/crono/time_of_day.rb b/lib/crono/time_of_day.rb new file mode 100644 index 0000000..3244bbc --- /dev/null +++ b/lib/crono/time_of_day.rb @@ -0,0 +1,38 @@ +module Crono + # TimeOfDay describes a certain hour and minute (on any day) + class TimeOfDay + include Comparable + + attr_accessor :hour, :min + + def self.parse(value) + case value + when String + time = Time.parse(value) + new time.hour, time.min + when Hash + new value[:hour], value[:min] + when Time + new value.hour, value.min + else + fail "Unknown TimeOfDay format: #{value.inspect}" + end + 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 diff --git a/spec/period_spec.rb b/spec/period_spec.rb index 688238c..ea3e7dd 100644 --- a/spec/period_spec.rb +++ b/spec/period_spec.rb @@ -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