Module: ActsAsBookable::TimeUtils

Defined in:
lib/acts_as_bookable/time_utils.rb

Overview

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

Class Method Summary collapse

Class Method Details

.interval_in_schedule?(schedule, interval_start, interval_end) ⇒ Boolean

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

Parameters:

  • schedule

    The schedule

  • interval_start

    The beginning Time of the interval

  • interval_end

    The ending Time of the interval

Returns:

  • (Boolean)

    true if the interval falls within an occurrence of the schedule, otherwise false



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 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) ⇒ Object

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

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

Parameters:

  • intervals

    an array of intervals

Returns:

  • an array of subintervals, sorted by time_start

Raises:

  • (ArgumentError)


73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# 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) ⇒ Boolean

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

Parameters:

  • time

    The time to check

  • interval_start

    The beginning time of the interval to match against

  • interval_end

    The ending time of the interval to match against

Returns:

  • (Boolean)


15
16
17
# 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) ⇒ Boolean

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

Parameters:

  • schedule

    The schedule

  • time

    The time

Returns:

  • (Boolean)

    true if the time falls within an occurrence of the schedule, otherwise false



49
50
51
# File 'lib/acts_as_bookable/time_utils.rb', line 49

def time_in_schedule?(schedule, time)
  return schedule.occurring_at? time
end