Skip to content

Excluding times from schedules

avit edited this page Nov 7, 2014 · 1 revision

IceCube (and the iCalendar spec) provides only a simple way to exclude individual times from a schedule:

schedule.add_exception_time(Time.new(2014, 11, 6, 17, 0, 0))

These exception times must line up with the schedule exactly, which is workable for simple cases, but sometimes a different approach is needed.

If exclusions need to cover a range of time, a more flexible approach could be to store these ranges outside of the schedule itself. Here is an example using a "Vacation" class to store days off and compare them against the schedule:

# A dummy class
Vacation = Struct.new(:first, :last) do
  # Convert dates to a range of times based on the schedule's local time
  def range(schedule)
    Range.new(
      IceCube::TimeUtil.beginning_of_date(first, schedule.start_time),
      IceCube::TimeUtil.end_of_date(last, schedule.start_time)
    )
  end

  # emulating an ActiveRecord scope:
  def self.occurring_between(a, z)
    all.select { |v| v.first <= z && v.last >= a }
  end
end

# emulating some ActiveRecord data:
def Vacation.all
  [ 
    new(Date.new(2010, 1, 1), Date.new(2010, 3, 1)),
    new(Date.new(2014, 11, 6), Date.new(2014, 11, 12)),
    new(Date.new(2014, 12, 25), Date.new(2015, 1, 2)) 
  ]
end

Given that we have a daily schedule and need the occurrences between these two dates:

a = Date.new(2014, 11, 6)
z = Date.new(2015, 1, 6)
schedule = IceCube::Schedule.new { |s| s.rrule(IceCube::Rule.daily) }

We can then fetch all the occurrences and reject the ones within the vacation ranges independently:

vacations = Vacation.occurring_between(a, z)
occurrences = schedule.occurrences_between(a, z).keep_if { |t|
  vacations.none? { |vac| vac.range(schedule).cover?(t) }
}
Clone this wiki locally