Class: Tod::Shift

Inherits:
Object
  • Object
show all
Defined in:
lib/tod/shift.rb

Overview

Shift is a range-like class that handles wrapping around midnight. For example, the Shift of 2300 to 0200 would include 0100.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(beginning, ending, exclude_end = false) ⇒ Shift

Returns a new instance of Shift.

Raises:

  • (ArgumentError)

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/tod/shift.rb', line 8

def initialize(beginning, ending, exclude_end=false)
  raise ArgumentError, "beginning can not be nil" unless beginning
  raise ArgumentError, "ending can not be nil" unless ending
  unless [true, false].include? exclude_end
    raise ArgumentError, "exclude_end must be true or false"
  end

  @beginning = beginning
  @ending = ending
  @exclude_end = exclude_end

  normalized_ending = ending.to_i
  normalized_ending += TimeOfDay::NUM_SECONDS_IN_DAY if normalized_ending < beginning.to_i

  @range = Range.new(beginning.to_i, normalized_ending, @exclude_end)

  freeze # Shift instances are value objects
end

Instance Attribute Details

#beginningObject (readonly)

Returns the value of attribute beginning.


6
7
8
# File 'lib/tod/shift.rb', line 6

def beginning
  @beginning
end

#endingObject (readonly)

Returns the value of attribute ending.


6
7
8
# File 'lib/tod/shift.rb', line 6

def ending
  @ending
end

#rangeObject (readonly)

Returns the value of attribute range.


6
7
8
# File 'lib/tod/shift.rb', line 6

def range
  @range
end

Instance Method Details

#==(other) ⇒ Object


90
91
92
# File 'lib/tod/shift.rb', line 90

def ==(other)
  @range == other.range
end

#contains?(shift) ⇒ Boolean

Returns:

  • (Boolean)

76
77
78
# File 'lib/tod/shift.rb', line 76

def contains?(shift)
  self.include?(shift.beginning) && self.include?(shift.inclusive_ending)
end

#durationObject

Return shift duration in seconds. if ending is lower than beginning this method will calculate the duration as the ending time is from the following day


82
83
84
# File 'lib/tod/shift.rb', line 82

def duration
  @range.last - @range.first
end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)

94
95
96
# File 'lib/tod/shift.rb', line 94

def eql?(other)
  @range.eql?(other.range)
end

#exclude_end?Boolean

Returns:

  • (Boolean)

86
87
88
# File 'lib/tod/shift.rb', line 86

def exclude_end?
  @exclude_end
end

#hashObject


98
99
100
# File 'lib/tod/shift.rb', line 98

def hash
  @range.hash
end

#include?(tod) ⇒ Boolean

Returns true if the time of day is inside the shift, false otherwise.

Returns:

  • (Boolean)

32
33
34
35
36
# File 'lib/tod/shift.rb', line 32

def include?(tod)
  second = tod.to_i
  second += TimeOfDay::NUM_SECONDS_IN_DAY if second < @range.first
  @range.cover?(second)
end

#inspectObject


27
28
29
# File 'lib/tod/shift.rb', line 27

def inspect
  "#<#{self.class} #{beginning}#{exclude_end? ? '...' : '..'}#{ending}>"
end

#overlaps?(other) ⇒ Boolean

Returns true if ranges overlap, false otherwise.

Returns:

  • (Boolean)

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/tod/shift.rb', line 39

def overlaps?(other)
  a, b = [self, other].map(&:range)
  #
  #  Although a Shift which passes through midnight is stored
  #  internally as lasting more than TimeOfDay::NUM_SECONDS_IN_DAY
  #  seconds from midnight, that's not how it is meant to be
  #  handled.  Rather, it consists of two chunks:
  #
  #    range.first => Midnight
  #    Midnight => range.last
  #
  #  The second one is *before* the first.  None of it is more than
  #  TimeOfDay::NUM_SECONDS_IN_DAY after midnight.  We thus need to shift
  #  each of our ranges to cover all overlapping possibilities.
  #
  one_day = TimeOfDay::NUM_SECONDS_IN_DAY
  ashifted =
    Range.new(a.first + one_day, a.last + one_day, a.exclude_end?)
  bshifted =
    Range.new(b.first + one_day, b.last + one_day, b.exclude_end?)
  #
  #  For exclusive ranges we need:
  #
  #  a.ending > b.beginning && b.ending > a.beginning
  #
  #  and for inclusive we need:
  #
  #  a.ending >= b.beginning && b.ending >= a.beginning
  #
  aop = a.exclude_end? ? :> : :>=
  bop = b.exclude_end? ? :> : :>=
  #
  (a.last.send(aop, b.first) && b.last.send(bop, a.first)) ||
  (ashifted.last.send(aop, b.first) && b.last.send(bop, ashifted.first)) ||
  (a.last.send(aop, bshifted.first) && bshifted.last.send(bop, a.first))
end

#slide(seconds) ⇒ Object

Move start and end by a number of seconds and return new shift.


103
104
105
# File 'lib/tod/shift.rb', line 103

def slide(seconds)
  self.class.new(beginning + seconds, ending + seconds, exclude_end?)
end