module ActsAsBookable::TimeUtils

Provide helper functions to manage operations and queries related to times and schedules

Public Class Methods

interval_in_schedule?(schedule, interval_start, interval_end) click to toggle source

Check if there is an occurrence of a schedule that contains a time interval

@param schedule The schedule @param interval_start The beginning Time of the interval @param interval_end The ending Time of the interval @return true if the interval falls within an occurrence of the schedule, otherwise false

# File lib/acts_as_bookable/time_utils.rb, line 27
def interval_in_schedule?(schedule, interval_start, interval_end)
  # Check if interval_start and interval_end falls within any occurrence
  return false if(!time_in_schedule?(schedule,interval_start) || !time_in_schedule?(schedule,interval_end))

  # Check if both interval_start and interval_end falls within the SAME occurrence
  between = schedule.occurrences_between(interval_start, interval_end, true)
  contains = false
  between.each do |oc|
    oc_end = oc + schedule.duration
    contains = true if (time_in_interval?(interval_start,oc,oc_end) && time_in_interval?(interval_end,oc,oc_end))
    break if contains
  end

  contains
end
subintervals(intervals, &block) click to toggle source

Returns an array of sub-intervals given another array of intervals, which are the overlapping insersections of each-others.

@param intervals an array of intervals @return an array of subintervals, sorted by time_start

An interval is defined as a hash with at least the following fields: `time_from` and `time_end`. An interval may contain more fields. In that case, it's suggested to give a block with the instructions to correctly merge two intervals when needed.

e.g: given these 7 intervals

|------|    |---|       |----------|
   |---|          |--|
   |------|       |--|      |-------------|
the output is an array containing these 8 intervals:
|--|   |--| |---| |--|  |---|      |------|
   |---|                    |------|
the number of subintervals may increase or decrease because some intervals may be split, while
some others may be merged.

If a block is given, it's called before merging two intervals. The block should provide instructions to merge intervals, and should return the merged fields in a hash

# File lib/acts_as_bookable/time_utils.rb, line 73
def subintervals(intervals, &block)
  raise ArgumentError.new('intervals must be an array') unless intervals.is_a? Array

  steps = [] # Steps will be extracted from intervals
  subintervals = [] # The output
  last_time = nil
  last_attrs = nil
  started_count = 0 # The number of intervals opened inside the cycle

  # Extract start times and end times from intervals, and create steps
  intervals.each do |el|
    begin
      ts = el[:time_start].to_time
      te = el[:time_end].to_time
    rescue NoMethodError
      raise ArgumentError.new('intervals must define :time_start and :time_end as Time or Date')
    end
    attrs = el.clone
    attrs.delete(:time_start)
    attrs.delete(:time_end)
    steps << { opening: 1, time: el[:time_start], attrs: attrs } # Start step
    steps << { opening: -1, time: el[:time_end], attrs: attrs.clone } # End step
  end

  # Sort steps by time (and opening if time is the same)
  steps.sort! do |a,b|
    diff = a[:time] <=> b[:time]
    diff = a[:opening] <=> b[:opening] if (diff == 0)
    diff
  end

  # Iterate over steps
  steps.each do |step|
    if (started_count == 0)
      last_time = step[:time]
      last_attrs = step[:attrs]
    else
      if(step[:time] > last_time)
        subintervals << ({
          time_start: last_time,
          time_end: step[:time]
        }.merge(last_attrs))

        last_time = step[:time]
      end

      if block_given?
        last_attrs = block.call(last_attrs.clone, step[:attrs],(step[:opening] == 1 ? :open : :close))
      else
        last_attrs = step[:attrs]
      end
    end

    # Update started_count
    started_count += step[:opening]
  end

  subintervals
end
time_in_interval?(time, interval_start, interval_end) click to toggle source

Check if time is included in a time interval. The ending time is excluded

@param time The time to check @param interval_start The beginning time of the interval to match against @param interval_end The ending time of the interval to match against

# File lib/acts_as_bookable/time_utils.rb, line 15
def time_in_interval? (time, interval_start, interval_end)
  time >= interval_start && time < interval_end
end
time_in_schedule?(schedule, time) click to toggle source

Check if there is an occurrence of a schedule that contains a time @param schedule The schedule @param time The time @return true if the time falls within an occurrence of the schedule, otherwise false

# File lib/acts_as_bookable/time_utils.rb, line 49
def time_in_schedule?(schedule, time)
  return schedule.occurring_at? time
end