time_frame 0.0.6 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -13
- data/Rakefile +1 -1
- data/lib/time_frame.rb +2 -0
- data/lib/time_frame/empty.rb +50 -0
- data/lib/time_frame/time_frame.rb +14 -13
- data/lib/time_frame/time_frame_covered.rb +2 -2
- data/lib/time_frame/time_frame_overlaps.rb +9 -10
- data/lib/time_frame/time_frame_uniter.rb +1 -1
- data/lib/time_frame/version.rb +1 -1
- data/spec/spec_helper.rb +2 -2
- data/spec/time_frame_spec.rb +1098 -0
- data/time_frame.gemspec +6 -7
- metadata +34 -33
- data/spec/time_range_spec.rb +0 -1081
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
ZjRkOTNjYmVmMjMxY2MwZjYyMTgwNmM1ODBmZDFkZTA0YmI2ZjE5OQ==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3a7329ea6aa8db160440c921d298bb8037194707
|
4
|
+
data.tar.gz: 445018532eec6574f3c254f388b11162d34a23af
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
NTQ4MTk3NTgyZmNkMmQyYzUyMmQxNWMxNDIwODdmNmI5MDc0NjIzZDE3Nzc5
|
11
|
-
OGFhYzA5OTZmZGU0OWU0YzBmZjM0M2U3NzMwMGZmNzRhOTJiYmM=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
OGU5YTZhZDE3ZDc0NTY5MmI5OGUyYjU5OGEzNTY2NjljMDkwMTQ3OWQ3MTEw
|
14
|
-
YTgwMWNiZmU1YTFjNjYzZTczZmU5NTc0YTRjOTMxNjg0ODFjOTk0YzE2OTcw
|
15
|
-
N2Q0MDIzYjYyNWVmMzllYjcxZTEzZjBjY2U1YmQ3MzFhMTllOTE=
|
6
|
+
metadata.gz: eef4ffa1fbc1a5bf4154582445d7dea4fcae7e21f3b6c019651c4d8e4bb2562180017c629f50669c2d671c549b66ab05f4792f88285296158c5a909791636a00
|
7
|
+
data.tar.gz: 08d510eb2c5b59d6eec01334183d582b44c93d623c75dd3f98db55817d915f339d829fc9ced18fe667239038abe3da6440a62b2d8ccbf9985295245cc0293256
|
data/Rakefile
CHANGED
data/lib/time_frame.rb
CHANGED
@@ -0,0 +1,50 @@
|
|
1
|
+
class TimeFrame
|
2
|
+
# Singleton class for the empty time frame object
|
3
|
+
class Empty < TimeFrame
|
4
|
+
include Singleton
|
5
|
+
|
6
|
+
def duration
|
7
|
+
0
|
8
|
+
end
|
9
|
+
|
10
|
+
def cover?(element)
|
11
|
+
element == EMPTY
|
12
|
+
end
|
13
|
+
|
14
|
+
def empty?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def deviation_of(_)
|
19
|
+
fail TypeError, 'deviation_of is undefined for empty time frame'
|
20
|
+
end
|
21
|
+
|
22
|
+
def &(_other)
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def overlaps?(_)
|
27
|
+
false
|
28
|
+
end
|
29
|
+
|
30
|
+
def split_by_interval(_)
|
31
|
+
[]
|
32
|
+
end
|
33
|
+
|
34
|
+
def shift_by(_)
|
35
|
+
fail TypeError, 'can\'t shift empty time frame'
|
36
|
+
end
|
37
|
+
|
38
|
+
def shift_to(_)
|
39
|
+
fail TypeError, 'can\'t shift empty time frame'
|
40
|
+
end
|
41
|
+
|
42
|
+
def without(*_)
|
43
|
+
[]
|
44
|
+
end
|
45
|
+
|
46
|
+
def inspect
|
47
|
+
'EMPTY'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -4,6 +4,8 @@
|
|
4
4
|
class TimeFrame
|
5
5
|
attr_reader :min, :max
|
6
6
|
|
7
|
+
EMPTY = Empty.instance
|
8
|
+
|
7
9
|
def initialize(args)
|
8
10
|
min = args.fetch(:min)
|
9
11
|
max = args.fetch(:max, nil) || min + args.fetch(:duration)
|
@@ -23,18 +25,19 @@ class TimeFrame
|
|
23
25
|
|
24
26
|
def cover?(element)
|
25
27
|
if element.respond_to?(:min) && element.respond_to?(:max)
|
26
|
-
|
28
|
+
element.empty? || min <= element.min && element.max <= max
|
29
|
+
else
|
30
|
+
min <= element && element <= max
|
27
31
|
end
|
28
|
-
min <= element && element <= max
|
29
32
|
end
|
30
33
|
|
31
34
|
def deviation_of(item)
|
32
|
-
|
33
|
-
|
34
|
-
[deviation_of(item.min), deviation_of(item.max)].min_by
|
35
|
-
|
35
|
+
if item.respond_to?(:min) && item.respond_to?(:max)
|
36
|
+
fail ArgumentError, 'time frame is empty' if item.empty?
|
37
|
+
[deviation_of(item.min), deviation_of(item.max)].min_by(&:abs)
|
38
|
+
elsif cover?(item)
|
36
39
|
0
|
37
|
-
|
40
|
+
elsif item < min
|
38
41
|
item - min
|
39
42
|
else
|
40
43
|
item - max
|
@@ -42,7 +45,7 @@ class TimeFrame
|
|
42
45
|
end
|
43
46
|
|
44
47
|
def empty?
|
45
|
-
|
48
|
+
false
|
46
49
|
end
|
47
50
|
|
48
51
|
def self.union(time_frames, options = {})
|
@@ -51,20 +54,21 @@ class TimeFrame
|
|
51
54
|
|
52
55
|
def self.intersection(time_frames)
|
53
56
|
time_frames.reduce(time_frames.first) do |intersection, time_frame|
|
54
|
-
return unless intersection
|
55
57
|
intersection & time_frame
|
56
58
|
end
|
57
59
|
end
|
58
60
|
|
59
61
|
# Returns true if the interior intersect.
|
60
62
|
def overlaps?(other)
|
63
|
+
return false if other.duration == 0
|
61
64
|
other.max > min && other.min < max
|
62
65
|
end
|
63
66
|
|
64
67
|
def &(other)
|
68
|
+
return EMPTY if other.empty?
|
65
69
|
new_min = [min, other.min].max
|
66
70
|
new_max = [max, other.max].min
|
67
|
-
TimeFrame.new(min: new_min, max: new_max)
|
71
|
+
new_min <= new_max ? TimeFrame.new(min: new_min, max: new_max) : EMPTY
|
68
72
|
end
|
69
73
|
|
70
74
|
def shift_by(duration)
|
@@ -107,9 +111,6 @@ class TimeFrame
|
|
107
111
|
|
108
112
|
def without_frame(other)
|
109
113
|
intersection = self & other
|
110
|
-
# this case is never used up to now (15.03.2013),
|
111
|
-
# since without selects the values correctly
|
112
|
-
return [self] unless intersection
|
113
114
|
|
114
115
|
result = []
|
115
116
|
if intersection.min > min
|
@@ -3,11 +3,11 @@ class TimeFrame
|
|
3
3
|
# Getting the covering time frame from a bunch of time_frame's.
|
4
4
|
class CoveredFrame
|
5
5
|
def initialize(time_frames)
|
6
|
-
@time_frames = time_frames
|
6
|
+
@time_frames = time_frames.reject(&:empty?)
|
7
7
|
end
|
8
8
|
|
9
9
|
def frame
|
10
|
-
return
|
10
|
+
return EMPTY unless @time_frames.any?
|
11
11
|
min = @time_frames.min_by(&:min).min
|
12
12
|
max = @time_frames.max_by(&:max).max
|
13
13
|
TimeFrame.new(min: min, max: max)
|
@@ -8,16 +8,16 @@ class TimeFrame
|
|
8
8
|
# * requires each of the arrays to be sorted
|
9
9
|
class Overlaps
|
10
10
|
def initialize(array1, array2)
|
11
|
-
@array1 = array1.
|
12
|
-
@array2 = array2.
|
11
|
+
@array1 = array1.reject(&:empty?)
|
12
|
+
@array2 = array2.reject(&:empty?)
|
13
13
|
end
|
14
14
|
|
15
15
|
def each(&block)
|
16
16
|
return [] if @array1.empty? || @array2.empty?
|
17
|
-
|
18
|
-
while
|
17
|
+
yield_current_pair(&block) if current_pair_overlaps?
|
18
|
+
while each_array_has_many_items?
|
19
19
|
shift
|
20
|
-
|
20
|
+
yield_current_pair(&block) if current_pair_overlaps?
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
@@ -32,17 +32,16 @@ class TimeFrame
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
def
|
35
|
+
def each_array_has_many_items?
|
36
36
|
@array1.many? || @array2.many?
|
37
37
|
end
|
38
38
|
|
39
|
-
def
|
39
|
+
def yield_current_pair
|
40
40
|
yield @array1.first, @array2.first
|
41
41
|
end
|
42
42
|
|
43
|
-
def
|
44
|
-
|
45
|
-
time_frame && time_frame.duration > 0
|
43
|
+
def current_pair_overlaps?
|
44
|
+
@array1.first.overlaps? @array2.first
|
46
45
|
end
|
47
46
|
end
|
48
47
|
end
|
data/lib/time_frame/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,1098 @@
|
|
1
|
+
# Encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe TimeFrame do
|
5
|
+
let(:time) { Time.zone.local(2012) }
|
6
|
+
let(:duration) { 2.hours }
|
7
|
+
|
8
|
+
before do
|
9
|
+
# Avoid i18n deprecation warning
|
10
|
+
I18n.enforce_available_locales = true
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#min and #max' do
|
14
|
+
context 'when given two times' do
|
15
|
+
context 'and min is smaller than max' do
|
16
|
+
subject { TimeFrame.new(min: time, max: time + duration) }
|
17
|
+
|
18
|
+
describe '#min' do
|
19
|
+
subject { super().min }
|
20
|
+
it { should eq time }
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#max' do
|
24
|
+
subject { super().max }
|
25
|
+
it { should eq time + duration }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
context 'and max is smaller than min' do
|
29
|
+
specify do
|
30
|
+
expect do
|
31
|
+
TimeFrame.new(min: time, max: time - 1.day)
|
32
|
+
end.to raise_error(ArgumentError)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
context 'which are equal' do
|
36
|
+
subject { TimeFrame.new(min: time, max: time) }
|
37
|
+
|
38
|
+
describe '#min' do
|
39
|
+
subject { super().min }
|
40
|
+
it { should eq time }
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#max' do
|
44
|
+
subject { super().max }
|
45
|
+
it { should eq time }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'when time and duration is given' do
|
51
|
+
context ' and duration is positive' do
|
52
|
+
subject { TimeFrame.new(min: time, duration: 1.hour) }
|
53
|
+
|
54
|
+
describe '#min' do
|
55
|
+
subject { super().min }
|
56
|
+
it { should eq time }
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#max' do
|
60
|
+
subject { super().max }
|
61
|
+
it { should eq time + 1.hour }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
context 'and duration is negative' do
|
65
|
+
let(:invalid_t_r) { TimeFrame.new(min: time, duration: - 1.hour) }
|
66
|
+
specify { expect { invalid_t_r }.to raise_error }
|
67
|
+
end
|
68
|
+
context 'and duration is 0' do
|
69
|
+
subject { TimeFrame.new(min: time, duration: 0.hour) }
|
70
|
+
|
71
|
+
describe '#min' do
|
72
|
+
subject { super().min }
|
73
|
+
it { should eq time }
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '#max' do
|
77
|
+
subject { super().max }
|
78
|
+
it { should eq time }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
context 'and time sframe covers a DST shift' do
|
82
|
+
let(:time) do
|
83
|
+
Time.use_zone('Europe/Berlin') { Time.zone.local(2013, 10, 27) }
|
84
|
+
end
|
85
|
+
subject { TimeFrame.new(min: time, duration: 1.day) }
|
86
|
+
|
87
|
+
describe '#min' do
|
88
|
+
subject { super().min }
|
89
|
+
it { should eq time }
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#max' do
|
93
|
+
subject { super().max }
|
94
|
+
it { should eq time + 25.hours }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
specify '#min and #max are nil if time frame is empty' do
|
100
|
+
expect(TimeFrame::EMPTY.min).to be_nil
|
101
|
+
expect(TimeFrame::EMPTY.max).to be_nil
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe '#duration' do
|
106
|
+
context 'when borders are different' do
|
107
|
+
subject { TimeFrame.new(min: time, duration: 2.hours).duration }
|
108
|
+
it { should eq 2.hours }
|
109
|
+
end
|
110
|
+
context 'when borders are equal' do
|
111
|
+
subject { TimeFrame.new(min: time, max: time).duration }
|
112
|
+
it { should eq 0 }
|
113
|
+
end
|
114
|
+
context 'when time frame containts a DST shift' do
|
115
|
+
it 'should gain 1 hour on summer -> winter shifts' do
|
116
|
+
Time.use_zone('Europe/Berlin') do
|
117
|
+
time_frame = TimeFrame.new(min: Time.zone.local(2013, 10, 27),
|
118
|
+
max: Time.zone.local(2013, 10, 28))
|
119
|
+
expect(time_frame.duration).to eq 25.hours
|
120
|
+
end
|
121
|
+
end
|
122
|
+
it 'should lose 1 hour on winter -> summer shifts' do
|
123
|
+
Time.use_zone('Europe/Berlin') do
|
124
|
+
time_frame = TimeFrame.new(min: Time.zone.local(2013, 3, 31),
|
125
|
+
max: Time.zone.local(2013, 4, 1))
|
126
|
+
expect(time_frame.duration).to eq 23.hours
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
it 'returns 0 when time frame is empty' do
|
131
|
+
expect(TimeFrame::EMPTY.duration).to eq 0
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe '#==' do
|
136
|
+
let(:time_frame) { TimeFrame.new(min: time, duration: 2.hours) }
|
137
|
+
context 'when borders are equal' do
|
138
|
+
let(:other) { TimeFrame.new(min: time, duration: 2.hours) }
|
139
|
+
subject { time_frame == other }
|
140
|
+
it { should be true }
|
141
|
+
end
|
142
|
+
context 'when min value is different' do
|
143
|
+
let(:other) do
|
144
|
+
TimeFrame.new(min: time - 1.hour, max: time + 2.hours)
|
145
|
+
end
|
146
|
+
subject { time_frame == other }
|
147
|
+
it { should be false }
|
148
|
+
end
|
149
|
+
context 'when max value is different' do
|
150
|
+
let(:other) { TimeFrame.new(min: time, duration: 3.hours) }
|
151
|
+
subject { time_frame == other }
|
152
|
+
it { should be false }
|
153
|
+
end
|
154
|
+
it 'returns true when self and the argument are both empty' do
|
155
|
+
expect(TimeFrame::EMPTY).to eq TimeFrame::EMPTY
|
156
|
+
end
|
157
|
+
it 'returns false if argument is empty, but self is not' do
|
158
|
+
expect(
|
159
|
+
TimeFrame.new(min: time, duration: 3.hours)
|
160
|
+
).not_to eq TimeFrame::EMPTY
|
161
|
+
end
|
162
|
+
it 'returns false if self is empty, but argument is not' do
|
163
|
+
expect(
|
164
|
+
TimeFrame::EMPTY
|
165
|
+
).not_to eq TimeFrame.new(min: time, duration: 3.hours)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe '#cover?' do
|
170
|
+
let(:time_frame) { TimeFrame.new(min: time, duration: 4.hours) }
|
171
|
+
context 'when argument is a Time instance' do
|
172
|
+
context 'and its covered' do
|
173
|
+
context 'and equal to min' do
|
174
|
+
subject { time_frame.cover?(time_frame.min) }
|
175
|
+
it { should be true }
|
176
|
+
end
|
177
|
+
context 'and equal to max' do
|
178
|
+
subject { time_frame.cover?(time_frame.max) }
|
179
|
+
it { should be true }
|
180
|
+
end
|
181
|
+
context 'and is an inner value' do
|
182
|
+
subject { time_frame.cover?(time_frame.min + 1.hour) }
|
183
|
+
it { should be true }
|
184
|
+
end
|
185
|
+
end
|
186
|
+
context 'and its not covered' do
|
187
|
+
context 'and smaller than min' do
|
188
|
+
subject { time_frame.cover?(time_frame.min - 1.hour) }
|
189
|
+
it { should be false }
|
190
|
+
end
|
191
|
+
context 'and greater than max' do
|
192
|
+
subject { time_frame.cover?(time_frame.max + 5.hours) }
|
193
|
+
it { should be false }
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
context 'when argument is a TimeFrame' do
|
198
|
+
context 'and its covered' do
|
199
|
+
context 'and they have the same min value' do
|
200
|
+
let(:other) { TimeFrame.new(min: time_frame.min, duration: 2.hours) }
|
201
|
+
subject { time_frame.cover?(other) }
|
202
|
+
it { should be true }
|
203
|
+
end
|
204
|
+
context 'and they have the same max value' do
|
205
|
+
let(:other) do
|
206
|
+
TimeFrame.new(min: time_frame.min + 1.hour, max: time_frame.max)
|
207
|
+
end
|
208
|
+
subject { time_frame.cover?(other) }
|
209
|
+
it { should be true }
|
210
|
+
end
|
211
|
+
context 'and it is within the interior of self' do
|
212
|
+
let(:other) do
|
213
|
+
TimeFrame.new(
|
214
|
+
min: time_frame.min + 1.hour, max: time_frame.max - 1.hour
|
215
|
+
)
|
216
|
+
end
|
217
|
+
subject { time_frame.cover?(other) }
|
218
|
+
it { should be true }
|
219
|
+
end
|
220
|
+
context 'and are equal' do
|
221
|
+
let(:other) { time_frame.clone }
|
222
|
+
subject { time_frame.cover?(other) }
|
223
|
+
it { should be true }
|
224
|
+
end
|
225
|
+
end
|
226
|
+
context 'and it is not covered' do
|
227
|
+
context 'and other is left of self' do
|
228
|
+
let(:other) { time_frame.shift_by(-5.hours) }
|
229
|
+
subject { time_frame.cover?(other) }
|
230
|
+
it { should be false }
|
231
|
+
end
|
232
|
+
context 'and other overlaps left hand side' do
|
233
|
+
let(:other) { time_frame.shift_by(-1.hour) }
|
234
|
+
subject { time_frame.cover?(other) }
|
235
|
+
it { should be false }
|
236
|
+
end
|
237
|
+
context 'and other overlaps left hand side at the border only' do
|
238
|
+
let(:other) { time_frame.shift_by(-time_frame.duration) }
|
239
|
+
subject { time_frame.cover?(other) }
|
240
|
+
it { should be false }
|
241
|
+
end
|
242
|
+
context 'and other is right of self' do
|
243
|
+
let(:other) { time_frame.shift_by(5.hours) }
|
244
|
+
subject { time_frame.cover?(other) }
|
245
|
+
it { should be false }
|
246
|
+
end
|
247
|
+
context 'and other overlaps right hand side' do
|
248
|
+
let(:other) { time_frame.shift_by(1.hours) }
|
249
|
+
subject { time_frame.cover?(other) }
|
250
|
+
it { should be false }
|
251
|
+
end
|
252
|
+
context 'and other overlaps right hand side at the border only' do
|
253
|
+
let(:other) { time_frame.shift_by(time_frame.duration) }
|
254
|
+
subject { time_frame.cover?(other) }
|
255
|
+
it { should be false }
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
it 'returns true when argument is empty' do
|
260
|
+
expect(
|
261
|
+
TimeFrame.new(min: time, duration: 3.hours)
|
262
|
+
).to cover(TimeFrame::EMPTY)
|
263
|
+
end
|
264
|
+
it 'returns true when self and argument are both empty' do
|
265
|
+
expect(TimeFrame::EMPTY).to cover(TimeFrame::EMPTY)
|
266
|
+
end
|
267
|
+
it 'returns false when only self is empty' do
|
268
|
+
expect(
|
269
|
+
TimeFrame::EMPTY
|
270
|
+
).not_to cover(TimeFrame.new(min: time, duration: 3.hours))
|
271
|
+
end
|
272
|
+
it 'returns true when only the argument is empty' do
|
273
|
+
expect(
|
274
|
+
TimeFrame.new(min: time, duration: 3.hours)
|
275
|
+
).to cover(TimeFrame::EMPTY)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
describe '#deviation_of' do
|
280
|
+
let(:time_frame) do
|
281
|
+
TimeFrame.new(min: Time.zone.local(2012), duration: 2.days)
|
282
|
+
end
|
283
|
+
context 'when providing a time object' do
|
284
|
+
describe 'when self covers time' do
|
285
|
+
context 'and time equals min' do
|
286
|
+
let(:time) { time_frame.min }
|
287
|
+
subject { time_frame.deviation_of(time) }
|
288
|
+
it { should eq 0.minutes }
|
289
|
+
end
|
290
|
+
context 'and time equals max' do
|
291
|
+
let(:time) { time_frame.max }
|
292
|
+
subject { time_frame.deviation_of(time) }
|
293
|
+
it { should eq 0.minutes }
|
294
|
+
end
|
295
|
+
context 'and time is an interior point of self' do
|
296
|
+
let(:time) { time_frame.min + (time_frame.duration / 2.0) }
|
297
|
+
subject { time_frame.deviation_of(time) }
|
298
|
+
it { should eq 0.minutes }
|
299
|
+
end
|
300
|
+
end
|
301
|
+
context 'when self do not cover time' do
|
302
|
+
context 'and time is smaller than the left bound' do
|
303
|
+
let(:time) { time_frame.min - 42.hours - 42.minutes }
|
304
|
+
subject { time_frame.deviation_of(time) }
|
305
|
+
it { should eq(-42.hours - 42.minutes) }
|
306
|
+
end
|
307
|
+
context 'and time is greater than the right bound' do
|
308
|
+
let(:time) { time_frame.max + 42.hours + 42.minutes }
|
309
|
+
subject { time_frame.deviation_of(time) }
|
310
|
+
it { should eq 42.hours + 42.minutes }
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
context 'when providing a time_frame' do
|
315
|
+
describe 'when self overlaps other' do
|
316
|
+
context 'and its partly' do
|
317
|
+
let(:other) { time_frame.shift_by(time_frame.duration / 2) }
|
318
|
+
subject { time_frame.deviation_of(other) }
|
319
|
+
it { should eq 0.minutes }
|
320
|
+
end
|
321
|
+
context 'and time equals max' do
|
322
|
+
let(:other) { time_frame }
|
323
|
+
subject { time_frame.deviation_of(other) }
|
324
|
+
it { should eq 0.minutes }
|
325
|
+
end
|
326
|
+
context 'and other lies in the interior of self' do
|
327
|
+
let(:other) do
|
328
|
+
TimeFrame.new(min: time_frame.min + 1.hour, duration: 1.hour)
|
329
|
+
end
|
330
|
+
subject { time_frame.deviation_of(other) }
|
331
|
+
it { should eq 0.minutes }
|
332
|
+
end
|
333
|
+
end
|
334
|
+
context 'when self do not cover time' do
|
335
|
+
context 'and time is smaller than the left bound' do
|
336
|
+
let(:other) { time_frame.shift_by(-2.days - 42.seconds) }
|
337
|
+
subject { time_frame.deviation_of(other) }
|
338
|
+
it { should eq(-42.seconds) }
|
339
|
+
end
|
340
|
+
context 'and time is greater than the right bound' do
|
341
|
+
let(:other) { time_frame.shift_by(2.days + 42.seconds) }
|
342
|
+
subject { time_frame.deviation_of(other) }
|
343
|
+
it { should eq 42.seconds }
|
344
|
+
end
|
345
|
+
end
|
346
|
+
it 'fails when only argument is empty' do
|
347
|
+
expect(-> { time_frame.deviation_of(TimeFrame::EMPTY) })
|
348
|
+
.to raise_error ArgumentError
|
349
|
+
end
|
350
|
+
it 'fails when only self is empty' do
|
351
|
+
expect(-> { TimeFrame::EMPTY.deviation_of(time_frame) })
|
352
|
+
.to raise_error TypeError
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
describe '#empty?' do
|
358
|
+
it 'returns false when self is not contains at least one point' do
|
359
|
+
expect(TimeFrame.new(min: time, max: time)).not_to be_empty
|
360
|
+
end
|
361
|
+
|
362
|
+
it 'returns true when self is empty' do
|
363
|
+
expect(TimeFrame::EMPTY).to be_empty
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
describe '.union' do
|
368
|
+
|
369
|
+
context 'when given an empty array' do
|
370
|
+
subject { TimeFrame.union([]) }
|
371
|
+
it { should eq [] }
|
372
|
+
end
|
373
|
+
|
374
|
+
context 'when given a single time frame' do
|
375
|
+
let(:time_frame) { TimeFrame.new(min: time, duration: 1.hour) }
|
376
|
+
subject { TimeFrame.union([time_frame]) }
|
377
|
+
it { should eq [time_frame] }
|
378
|
+
end
|
379
|
+
|
380
|
+
context 'when getting single element it returns a dup' do
|
381
|
+
let(:time_frames) { [TimeFrame.new(min: time, duration: 1.hour)] }
|
382
|
+
subject { TimeFrame.union(time_frames) }
|
383
|
+
it { should_not equal time_frames }
|
384
|
+
end
|
385
|
+
|
386
|
+
context 'when given time frames' do
|
387
|
+
context 'in order' do
|
388
|
+
context 'and no sorted flag is provided' do
|
389
|
+
context 'that are overlapping' do
|
390
|
+
let(:time_frame1) { TimeFrame.new(min: time, duration: 2.hours) }
|
391
|
+
let(:time_frame2) { time_frame1.shift_by(1.hour) }
|
392
|
+
let(:expected) do
|
393
|
+
[TimeFrame.new(min: time_frame1.min, max: time_frame2.max)]
|
394
|
+
end
|
395
|
+
subject { TimeFrame.union([time_frame1, time_frame2]) }
|
396
|
+
it { should eq expected }
|
397
|
+
end
|
398
|
+
context 'that are disjoint' do
|
399
|
+
let(:time_frame1) { TimeFrame.new(min: time, duration: 2.hours) }
|
400
|
+
let(:time_frame2) { time_frame1.shift_by(3.hours) }
|
401
|
+
subject { TimeFrame.union([time_frame1, time_frame2]) }
|
402
|
+
it { should eq [time_frame1, time_frame2] }
|
403
|
+
end
|
404
|
+
context 'that intersect at their boundaries' do
|
405
|
+
let(:time_frame1) { TimeFrame.new(min: time, duration: + 2.hour) }
|
406
|
+
let(:time_frame2) { time_frame1.shift_by(time_frame1.duration) }
|
407
|
+
let(:expected) do
|
408
|
+
[TimeFrame.new(min: time_frame1.min, max: time_frame2.max)]
|
409
|
+
end
|
410
|
+
subject { TimeFrame.union([time_frame1, time_frame2]) }
|
411
|
+
it { should eq expected }
|
412
|
+
end
|
413
|
+
end
|
414
|
+
context 'and the sorted flag is provided' do
|
415
|
+
context 'that are overlapping' do
|
416
|
+
let(:time_frame1) { TimeFrame.new(min: time, duration: 2.hours) }
|
417
|
+
let(:time_frame2) { time_frame1.shift_by(1.hour) }
|
418
|
+
let(:expected) do
|
419
|
+
[TimeFrame.new(min: time_frame1.min, max: time_frame2.max)]
|
420
|
+
end
|
421
|
+
subject do
|
422
|
+
TimeFrame.union([time_frame1, time_frame2], sorted: true)
|
423
|
+
end
|
424
|
+
it { should eq expected }
|
425
|
+
end
|
426
|
+
context 'that are disjoint' do
|
427
|
+
let(:time_frame1) { TimeFrame.new(min: time, duration: 2.hours) }
|
428
|
+
let(:time_frame2) { time_frame1.shift_by(3.hours) }
|
429
|
+
subject do
|
430
|
+
TimeFrame.union([time_frame1, time_frame2], sorted: true)
|
431
|
+
end
|
432
|
+
it { should eq [time_frame1, time_frame2] }
|
433
|
+
end
|
434
|
+
context 'that intersect at their boundaries' do
|
435
|
+
let(:time_frame1) { TimeFrame.new(min: time, duration: + 2.hour) }
|
436
|
+
let(:time_frame2) { time_frame1.shift_by(time_frame1.duration) }
|
437
|
+
let(:expected) do
|
438
|
+
[TimeFrame.new(min: time_frame1.min, max: time_frame2.max)]
|
439
|
+
end
|
440
|
+
subject do
|
441
|
+
TimeFrame.union([time_frame1, time_frame2], sorted: true)
|
442
|
+
end
|
443
|
+
it { should eq expected }
|
444
|
+
end
|
445
|
+
end
|
446
|
+
end
|
447
|
+
context 'not in order' do
|
448
|
+
context 'that are overlapping' do
|
449
|
+
let(:time_frame1) { TimeFrame.new(min: time, duration: 2.hours) }
|
450
|
+
let(:time_frame2) { time_frame1.shift_by(1.hour) }
|
451
|
+
subject { TimeFrame.union([time_frame2, time_frame1]) }
|
452
|
+
it do
|
453
|
+
should eq [
|
454
|
+
TimeFrame.new(min: time_frame1.min, max: time_frame2.max)
|
455
|
+
]
|
456
|
+
end
|
457
|
+
end
|
458
|
+
context 'that are disjoint' do
|
459
|
+
let(:time_frame1) { TimeFrame.new(min: time, duration: 2.hours) }
|
460
|
+
let(:time_frame2) { time_frame1.shift_by(3.hours) }
|
461
|
+
subject { TimeFrame.union([time_frame2, time_frame1]) }
|
462
|
+
it { should eq [time_frame1, time_frame2] }
|
463
|
+
end
|
464
|
+
context 'that intersect at their boundaries' do
|
465
|
+
let(:time_frame1) { TimeFrame.new(min: time, duration: + 2.hour) }
|
466
|
+
let(:time_frame2) { time_frame1.shift_by(time_frame1.duration) }
|
467
|
+
subject { TimeFrame.union([time_frame2, time_frame1]) }
|
468
|
+
it do
|
469
|
+
should eq [
|
470
|
+
TimeFrame.new(min: time_frame1.min, max: time_frame2.max)
|
471
|
+
]
|
472
|
+
end
|
473
|
+
end
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
it 'ignores any empty time frames' do
|
478
|
+
expect(TimeFrame.union([TimeFrame::EMPTY, TimeFrame::EMPTY])).to eq []
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
describe '.intersection' do
|
483
|
+
it 'returns the intersection of all time frames' do
|
484
|
+
time_frame1 = TimeFrame.new(min: Time.zone.local(2012), duration: 3.days)
|
485
|
+
time_frame2 = time_frame1.shift_by(-1.day)
|
486
|
+
time_frame3 = time_frame1.shift_by(-2.days)
|
487
|
+
expect(TimeFrame.intersection([time_frame1, time_frame2, time_frame3]))
|
488
|
+
.to eq TimeFrame.new(min: Time.zone.local(2012), duration: 1.day)
|
489
|
+
end
|
490
|
+
it 'is empty if the intersection is empty' do
|
491
|
+
time_frame1 = TimeFrame.new(min: Time.zone.local(2012), duration: 1.days)
|
492
|
+
time_frame2 = time_frame1.shift_by(-2.day)
|
493
|
+
time_frame3 = time_frame1.shift_by(-4.days)
|
494
|
+
expect(
|
495
|
+
TimeFrame.intersection([time_frame1, time_frame2, time_frame3])
|
496
|
+
).to be_empty
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
describe '#overlaps?' do
|
501
|
+
let(:time_frame) { TimeFrame.new(min: time, duration: 3.hours) }
|
502
|
+
context 'when self is equal to other' do
|
503
|
+
let(:other) { time_frame.clone }
|
504
|
+
subject { time_frame.overlaps?(other) }
|
505
|
+
it { should be true }
|
506
|
+
end
|
507
|
+
context 'when self covers other' do
|
508
|
+
let(:other) do
|
509
|
+
TimeFrame.new(
|
510
|
+
min: time_frame.min + 1.hour, max: time_frame.max - 1.hour
|
511
|
+
)
|
512
|
+
end
|
513
|
+
subject { time_frame.overlaps?(other) }
|
514
|
+
it { should be true }
|
515
|
+
end
|
516
|
+
context 'when other covers self' do
|
517
|
+
let(:other) do
|
518
|
+
TimeFrame.new(
|
519
|
+
min: time_frame.min - 1.hour, max: time_frame.max + 1.hour
|
520
|
+
)
|
521
|
+
end
|
522
|
+
subject { time_frame.overlaps?(other) }
|
523
|
+
it { should be true }
|
524
|
+
end
|
525
|
+
context 'when self begins earlier than other' do
|
526
|
+
context 'and they are disjoint' do
|
527
|
+
let(:other) { time_frame.shift_by(-time_frame.duration - 1.hour) }
|
528
|
+
subject { time_frame.overlaps?(other) }
|
529
|
+
it { should be false }
|
530
|
+
end
|
531
|
+
context 'and they are overlapping' do
|
532
|
+
let(:other) { time_frame.shift_by(-1.hours) }
|
533
|
+
subject { time_frame.overlaps?(other) }
|
534
|
+
it { should be true }
|
535
|
+
end
|
536
|
+
context 'and they intersect at their boundaries' do
|
537
|
+
let(:other) { time_frame.shift_by(-time_frame.duration) }
|
538
|
+
subject { time_frame.overlaps?(other) }
|
539
|
+
it { should be false }
|
540
|
+
end
|
541
|
+
end
|
542
|
+
context 'when other begins earlier than self' do
|
543
|
+
context 'and they are disjoint' do
|
544
|
+
let(:other) { time_frame.shift_by(time_frame.duration + 1.hour) }
|
545
|
+
subject { time_frame.overlaps?(other) }
|
546
|
+
it { should be false }
|
547
|
+
end
|
548
|
+
context 'and they are overlapping' do
|
549
|
+
let(:other) { time_frame.shift_by(1.hours) }
|
550
|
+
subject { time_frame.overlaps?(other) }
|
551
|
+
it { should be true }
|
552
|
+
end
|
553
|
+
context 'and they intersect at their boundaries' do
|
554
|
+
let(:other) { time_frame.shift_by(time_frame.duration) }
|
555
|
+
subject { time_frame.overlaps?(other) }
|
556
|
+
it { should be false }
|
557
|
+
end
|
558
|
+
end
|
559
|
+
it 'returns false when self contains only one point' do
|
560
|
+
singleton_time_frame = TimeFrame.new(min: time, max: time)
|
561
|
+
time_frame = TimeFrame.new(min: time, duration: 1.hour)
|
562
|
+
expect(singleton_time_frame.overlaps?(time_frame)).to be false
|
563
|
+
expect(singleton_time_frame.overlaps?(singleton_time_frame)).to be false
|
564
|
+
expect(singleton_time_frame.overlaps?(TimeFrame::EMPTY)).to be false
|
565
|
+
end
|
566
|
+
it 'returns false when self is empty' do
|
567
|
+
singleton_time_frame = TimeFrame.new(min: time, max: time)
|
568
|
+
time_frame = TimeFrame.new(min: time, duration: 1.hour)
|
569
|
+
expect(TimeFrame::EMPTY.overlaps?(time_frame)).to be false
|
570
|
+
expect(TimeFrame::EMPTY.overlaps?(singleton_time_frame)).to be false
|
571
|
+
expect(TimeFrame::EMPTY.overlaps?(TimeFrame::EMPTY)).to be false
|
572
|
+
end
|
573
|
+
it 'returns false when other contains only one point' do
|
574
|
+
singleton_time_frame = TimeFrame.new(min: time, max: time)
|
575
|
+
time_frame = TimeFrame.new(min: time, duration: 1.hour)
|
576
|
+
expect(time_frame.overlaps?(singleton_time_frame)).to be false
|
577
|
+
expect(singleton_time_frame.overlaps?(singleton_time_frame)).to be false
|
578
|
+
expect(TimeFrame::EMPTY.overlaps?(singleton_time_frame)).to be false
|
579
|
+
end
|
580
|
+
it 'returns false when other is empty' do
|
581
|
+
singleton_time_frame = TimeFrame.new(min: time, max: time)
|
582
|
+
time_frame = TimeFrame.new(min: time, duration: 1.hour)
|
583
|
+
expect(time_frame.overlaps?(TimeFrame::EMPTY)).to be false
|
584
|
+
expect(singleton_time_frame.overlaps?(TimeFrame::EMPTY)).to be false
|
585
|
+
expect(TimeFrame::EMPTY.overlaps?(TimeFrame::EMPTY)).to be false
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
describe '#&' do
|
590
|
+
let(:time_frame) { TimeFrame.new(min: time, duration: 3.hours) }
|
591
|
+
context 'when self is equal to other' do
|
592
|
+
let(:other) { time_frame.clone }
|
593
|
+
subject { time_frame & other }
|
594
|
+
it { should eq time_frame }
|
595
|
+
end
|
596
|
+
context 'when self covers other' do
|
597
|
+
let(:other) do
|
598
|
+
TimeFrame.new(
|
599
|
+
min: time_frame.min + 1.hour, max: time_frame.max - 1.hour
|
600
|
+
)
|
601
|
+
end
|
602
|
+
subject { time_frame & other }
|
603
|
+
it { should eq other }
|
604
|
+
end
|
605
|
+
context 'when other covers self' do
|
606
|
+
let(:other) do
|
607
|
+
TimeFrame.new(
|
608
|
+
min: time_frame.min - 1.hour, max: time_frame.max + 1.hour
|
609
|
+
)
|
610
|
+
end
|
611
|
+
subject { time_frame & other }
|
612
|
+
it { should eq time_frame }
|
613
|
+
end
|
614
|
+
context 'when self begins earlier than other' do
|
615
|
+
context 'and they are disjoint' do
|
616
|
+
let(:other) { time_frame.shift_by(time_frame.duration + 1.hour) }
|
617
|
+
subject { time_frame & other }
|
618
|
+
it { should be_empty }
|
619
|
+
end
|
620
|
+
context 'and they are overlapping' do
|
621
|
+
let(:other) { time_frame.shift_by(1.hour) }
|
622
|
+
subject { time_frame & other }
|
623
|
+
it { should eq TimeFrame.new(min: other.min, max: time_frame.max) }
|
624
|
+
end
|
625
|
+
context 'and they intersect at their boundaries' do
|
626
|
+
let(:other) { time_frame.shift_by(time_frame.duration) }
|
627
|
+
subject { time_frame & other }
|
628
|
+
it { should eq TimeFrame.new(min: time_frame.max, max: time_frame.max) }
|
629
|
+
end
|
630
|
+
end
|
631
|
+
context 'when other begins earlier than self' do
|
632
|
+
context 'and they are disjoint' do
|
633
|
+
let(:other) { time_frame.shift_by(-time_frame.duration - 1.hour) }
|
634
|
+
subject { time_frame & other }
|
635
|
+
it { should be_empty }
|
636
|
+
end
|
637
|
+
context 'and they are overlapping' do
|
638
|
+
let(:other) { time_frame.shift_by(-1.hour) }
|
639
|
+
subject { time_frame & other }
|
640
|
+
it { should eq TimeFrame.new(min: time_frame.min, max: other.max) }
|
641
|
+
end
|
642
|
+
context 'and they intersect at their boundaries' do
|
643
|
+
let(:other) { time_frame.shift_by(-time_frame.duration) }
|
644
|
+
subject { time_frame & other }
|
645
|
+
it { should eq TimeFrame.new(min: time_frame.min, max: time_frame.min) }
|
646
|
+
end
|
647
|
+
end
|
648
|
+
it 'is empty time frame when self it empty' do
|
649
|
+
expect(TimeFrame::EMPTY & time_frame).to eq TimeFrame::EMPTY
|
650
|
+
expect(TimeFrame::EMPTY & TimeFrame::EMPTY).to eq TimeFrame::EMPTY
|
651
|
+
end
|
652
|
+
it 'is empty time frame when self it not empty and the argument is empty' do
|
653
|
+
expect(time_frame & TimeFrame::EMPTY).to eq TimeFrame::EMPTY
|
654
|
+
end
|
655
|
+
end
|
656
|
+
|
657
|
+
describe '#split_by_interval' do
|
658
|
+
context 'when time frame duration is divisible by interval' do
|
659
|
+
let(:time) { Time.new(2012, 1, 1) }
|
660
|
+
let(:interval) { 1.day }
|
661
|
+
let(:time_frame) do
|
662
|
+
TimeFrame.new(min: time, duration: 7.days)
|
663
|
+
end
|
664
|
+
subject do
|
665
|
+
time_frame.split_by_interval(interval)
|
666
|
+
end
|
667
|
+
|
668
|
+
describe '#size' do
|
669
|
+
subject { super().size }
|
670
|
+
it { should eq 7 }
|
671
|
+
end
|
672
|
+
(0..6).each do |day|
|
673
|
+
it "should have the right borders on day #{day}" do
|
674
|
+
expected = TimeFrame.new(min: time, duration: interval)
|
675
|
+
expect(subject[day]).to eq expected.shift_by(day.days)
|
676
|
+
end
|
677
|
+
end
|
678
|
+
end
|
679
|
+
|
680
|
+
context 'when time frame duration is not divisible by interval' do
|
681
|
+
let(:time) { Time.new(2012, 1, 1) }
|
682
|
+
let(:interval) { 1.day }
|
683
|
+
let(:time_frame) do
|
684
|
+
TimeFrame.new(min: time, duration: 7.days + 12.hours)
|
685
|
+
end
|
686
|
+
subject do
|
687
|
+
time_frame.split_by_interval(interval)
|
688
|
+
end
|
689
|
+
|
690
|
+
describe '#size' do
|
691
|
+
subject { super().size }
|
692
|
+
it { should eq 8 }
|
693
|
+
end
|
694
|
+
(0..6).each do |day|
|
695
|
+
it "should have the right borders on day #{day}" do
|
696
|
+
expected = TimeFrame.new(min: time, duration: interval)
|
697
|
+
expect(subject[day]).to eq expected.shift_by(day.days)
|
698
|
+
end
|
699
|
+
end
|
700
|
+
it 'should have a smaller time_frame at the end' do
|
701
|
+
expected = TimeFrame.new(min: time + 7.days, duration: 12.hours)
|
702
|
+
expect(subject[7]).to eq expected
|
703
|
+
end
|
704
|
+
end
|
705
|
+
|
706
|
+
it 'returns an empty array when self is empty' do
|
707
|
+
expect(TimeFrame::EMPTY.split_by_interval(1.day)).to eq []
|
708
|
+
end
|
709
|
+
end
|
710
|
+
|
711
|
+
describe '#shift_by' do
|
712
|
+
let(:min) { time }
|
713
|
+
let(:max) { time + 2.days }
|
714
|
+
let(:time_frame) { TimeFrame.new(min: min, max: max) }
|
715
|
+
context 'when shifting into the future' do
|
716
|
+
subject { time_frame.shift_by(1.day) }
|
717
|
+
|
718
|
+
describe '#min' do
|
719
|
+
subject { super().min }
|
720
|
+
it { should eq min + 1.day }
|
721
|
+
end
|
722
|
+
|
723
|
+
describe '#max' do
|
724
|
+
subject { super().max }
|
725
|
+
it { should eq max + 1.day }
|
726
|
+
end
|
727
|
+
it { should_not equal time_frame }
|
728
|
+
end
|
729
|
+
context 'when shifting into the past' do
|
730
|
+
subject { time_frame.shift_by(-1.day) }
|
731
|
+
|
732
|
+
describe '#min' do
|
733
|
+
subject { super().min }
|
734
|
+
it { should eq min - 1.day }
|
735
|
+
end
|
736
|
+
|
737
|
+
describe '#max' do
|
738
|
+
subject { super().max }
|
739
|
+
it { should eq max - 1.day }
|
740
|
+
end
|
741
|
+
it { should_not equal time_frame }
|
742
|
+
end
|
743
|
+
context 'when shifting by 0' do
|
744
|
+
subject { time_frame.shift_by(0) }
|
745
|
+
|
746
|
+
describe '#min' do
|
747
|
+
subject { super().min }
|
748
|
+
it { should eq min }
|
749
|
+
end
|
750
|
+
|
751
|
+
describe '#max' do
|
752
|
+
subject { super().max }
|
753
|
+
it { should eq max }
|
754
|
+
end
|
755
|
+
it { should_not equal time_frame }
|
756
|
+
end
|
757
|
+
context 'when shifting back and forth' do
|
758
|
+
subject { time_frame.shift_by(-1.day).shift_by(1.day) }
|
759
|
+
|
760
|
+
describe '#min' do
|
761
|
+
subject { super().min }
|
762
|
+
it { should eq min }
|
763
|
+
end
|
764
|
+
|
765
|
+
describe '#max' do
|
766
|
+
subject { super().max }
|
767
|
+
it { should eq max }
|
768
|
+
end
|
769
|
+
it { should_not equal time_frame }
|
770
|
+
end
|
771
|
+
it 'raises a TypeError when time frame is empty' do
|
772
|
+
expect { TimeFrame::EMPTY.shift_by(1.day) }.to raise_error TypeError
|
773
|
+
end
|
774
|
+
end
|
775
|
+
|
776
|
+
describe '#shift_to' do
|
777
|
+
|
778
|
+
let(:duration) { 1.day }
|
779
|
+
let(:min) { Time.zone.local(2012, 1, 2) }
|
780
|
+
let(:max) { min + duration }
|
781
|
+
let(:time_frame) { TimeFrame.new(min: min, max: max) }
|
782
|
+
|
783
|
+
context 'when shifting to a future time' do
|
784
|
+
let(:destination) { min + duration }
|
785
|
+
subject { time_frame.shift_to(destination) }
|
786
|
+
it { should_not equal time_frame }
|
787
|
+
|
788
|
+
describe '#min' do
|
789
|
+
subject { super().min }
|
790
|
+
it { should eq destination }
|
791
|
+
end
|
792
|
+
|
793
|
+
describe '#max' do
|
794
|
+
subject { super().max }
|
795
|
+
it { should eq destination + duration }
|
796
|
+
end
|
797
|
+
end
|
798
|
+
|
799
|
+
context 'when shifting to a past time' do
|
800
|
+
let(:destination) { min - duration }
|
801
|
+
subject { time_frame.shift_to(destination) }
|
802
|
+
it { should_not equal time_frame }
|
803
|
+
|
804
|
+
describe '#min' do
|
805
|
+
subject { super().min }
|
806
|
+
it { should eq destination }
|
807
|
+
end
|
808
|
+
|
809
|
+
describe '#max' do
|
810
|
+
subject { super().max }
|
811
|
+
it { should eq destination + duration }
|
812
|
+
end
|
813
|
+
end
|
814
|
+
|
815
|
+
context 'when shifting to same time' do
|
816
|
+
let(:destination) { min }
|
817
|
+
subject { time_frame.shift_to(destination) }
|
818
|
+
it { should_not equal time_frame }
|
819
|
+
|
820
|
+
describe '#min' do
|
821
|
+
subject { super().min }
|
822
|
+
it { should eq destination }
|
823
|
+
end
|
824
|
+
|
825
|
+
describe '#max' do
|
826
|
+
subject { super().max }
|
827
|
+
it { should eq destination + duration }
|
828
|
+
end
|
829
|
+
end
|
830
|
+
|
831
|
+
it 'raises a TypeError when time frame is empty' do
|
832
|
+
expect do
|
833
|
+
TimeFrame::EMPTY.shift_to(Time.zone.local(2012, 1, 2))
|
834
|
+
end.to raise_error TypeError
|
835
|
+
end
|
836
|
+
end
|
837
|
+
|
838
|
+
describe '#without' do
|
839
|
+
let(:time_frame) { TimeFrame.new(min: time, duration: 10.hours) }
|
840
|
+
context 'when the arguments do not intersect' do
|
841
|
+
context 'and do not touch the boundaries' do
|
842
|
+
let(:arg) do
|
843
|
+
shift = time_frame.duration + 1.hour
|
844
|
+
[
|
845
|
+
TimeFrame.new(min: time - 2.hours, duration: 1.hour),
|
846
|
+
TimeFrame.new(min: time + shift, duration: 1.hour)
|
847
|
+
]
|
848
|
+
end
|
849
|
+
subject { time_frame.without(*arg) }
|
850
|
+
it { should eq [time_frame] }
|
851
|
+
end
|
852
|
+
context 'and they touch boundaries' do
|
853
|
+
let(:arg) do
|
854
|
+
[
|
855
|
+
TimeFrame.new(min: time - 1.hour, duration: 1.hour),
|
856
|
+
TimeFrame.new(min: time + time_frame.duration, duration: 1.hour)
|
857
|
+
]
|
858
|
+
end
|
859
|
+
subject { time_frame.without(*arg) }
|
860
|
+
it { should eq [time_frame] }
|
861
|
+
end
|
862
|
+
end
|
863
|
+
context 'when the arguments intersect' do
|
864
|
+
context 'and the argument time_frames overlaps themself' do
|
865
|
+
let(:arg) do
|
866
|
+
[
|
867
|
+
TimeFrame.new(min: time + 1.hour, duration: 2.hours),
|
868
|
+
TimeFrame.new(min: time + 2.hours, duration: 2.hours)
|
869
|
+
]
|
870
|
+
end
|
871
|
+
let(:expected) do
|
872
|
+
[
|
873
|
+
TimeFrame.new(min: time_frame.min, duration: 1.hour),
|
874
|
+
TimeFrame.new(min: time + 4.hours, max: time_frame.max)
|
875
|
+
]
|
876
|
+
end
|
877
|
+
subject { time_frame.without(*arg) }
|
878
|
+
it { should eq expected }
|
879
|
+
end
|
880
|
+
context 'and they cover self' do
|
881
|
+
let(:arg) do
|
882
|
+
duration = 0.5 * time_frame.duration
|
883
|
+
[
|
884
|
+
TimeFrame.new(min: time, duration: duration),
|
885
|
+
TimeFrame.new(min: time + duration, duration: duration)
|
886
|
+
]
|
887
|
+
end
|
888
|
+
subject { time_frame.without(*arg) }
|
889
|
+
it { should eq [] }
|
890
|
+
end
|
891
|
+
context 'and they overlap at the boundaries' do
|
892
|
+
let(:arg) do
|
893
|
+
shift = time_frame.duration - 1.hour
|
894
|
+
[
|
895
|
+
TimeFrame.new(min: time - 1.hour, duration: 2.hour),
|
896
|
+
TimeFrame.new(min: time + shift, duration: 2.hour)
|
897
|
+
]
|
898
|
+
end
|
899
|
+
let(:expected) do
|
900
|
+
[
|
901
|
+
TimeFrame.new(min: time_frame.min + 1.hour,
|
902
|
+
max: time_frame.max - 1.hour)
|
903
|
+
]
|
904
|
+
end
|
905
|
+
subject { time_frame.without(*arg) }
|
906
|
+
it { should eq expected }
|
907
|
+
end
|
908
|
+
context 'and we have three time_frames in args overlaped by self' do
|
909
|
+
context 'which are sorted' do
|
910
|
+
let(:arg) do
|
911
|
+
[
|
912
|
+
TimeFrame.new(min: time + 1.hour, duration: 2.hour),
|
913
|
+
TimeFrame.new(min: time + 4.hours, duration: 2.hour),
|
914
|
+
TimeFrame.new(min: time + 7.hours, duration: 2.hour)
|
915
|
+
]
|
916
|
+
end
|
917
|
+
let(:expected) do
|
918
|
+
[
|
919
|
+
TimeFrame.new(min: time, max: time + 1.hour),
|
920
|
+
TimeFrame.new(min: time + 3.hours, max: time + 4.hour),
|
921
|
+
TimeFrame.new(min: time + 6.hours, max: time + 7.hours),
|
922
|
+
TimeFrame.new(min: time + 9.hours, max: time + 10.hours)
|
923
|
+
]
|
924
|
+
end
|
925
|
+
subject { time_frame.without(*arg) }
|
926
|
+
it { should eq expected }
|
927
|
+
end
|
928
|
+
context 'and they are unsorted' do
|
929
|
+
let(:arg) do
|
930
|
+
[
|
931
|
+
TimeFrame.new(min: time + 4.hours, duration: 2.hour),
|
932
|
+
TimeFrame.new(min: time + 1.hour, duration: 2.hour),
|
933
|
+
TimeFrame.new(min: time + 7.hours, duration: 2.hour)
|
934
|
+
]
|
935
|
+
end
|
936
|
+
let(:expected) do
|
937
|
+
[
|
938
|
+
TimeFrame.new(min: time, max: time + 1.hour),
|
939
|
+
TimeFrame.new(min: time + 3.hours, max: time + 4.hour),
|
940
|
+
TimeFrame.new(min: time + 6.hours, max: time + 7.hours),
|
941
|
+
TimeFrame.new(min: time + 9.hours, max: time + 10.hours)
|
942
|
+
]
|
943
|
+
end
|
944
|
+
subject { time_frame.without(*arg) }
|
945
|
+
it { should eq expected }
|
946
|
+
end
|
947
|
+
end
|
948
|
+
end
|
949
|
+
|
950
|
+
it 'returns self (as a singleton array) if there are no arguments' do
|
951
|
+
expect(time_frame.without).to eq [time_frame]
|
952
|
+
end
|
953
|
+
|
954
|
+
it 'returns an empty array if self is empty' do
|
955
|
+
expect(TimeFrame::EMPTY.without(time_frame)).to eq []
|
956
|
+
end
|
957
|
+
|
958
|
+
it 'ignores empty time ranges within the arguments' do
|
959
|
+
expect(time_frame.without(TimeFrame::EMPTY)).to eq [time_frame]
|
960
|
+
end
|
961
|
+
end
|
962
|
+
|
963
|
+
describe '.covering_time_frame_for' do
|
964
|
+
|
965
|
+
context 'for a single time frame' do
|
966
|
+
let(:time_frame) { TimeFrame.new(min: time, duration: 1.hour) }
|
967
|
+
subject { TimeFrame.covering_time_frame_for([time_frame]) }
|
968
|
+
it { should eq time_frame }
|
969
|
+
end
|
970
|
+
|
971
|
+
context 'for multiple time frames' do
|
972
|
+
let(:time_frame1) { TimeFrame.new(min: time, duration: 2.hours) }
|
973
|
+
let(:time_frame2) { time_frame1.shift_by(-1.hour) }
|
974
|
+
let(:time_frame3) { time_frame1.shift_by(3.hours) }
|
975
|
+
subject do
|
976
|
+
TimeFrame.covering_time_frame_for(
|
977
|
+
[time_frame1, time_frame2, time_frame3]
|
978
|
+
)
|
979
|
+
end
|
980
|
+
|
981
|
+
describe '#min' do
|
982
|
+
subject { super().min }
|
983
|
+
it { should eq time_frame2.min }
|
984
|
+
end
|
985
|
+
|
986
|
+
describe '#max' do
|
987
|
+
subject { super().max }
|
988
|
+
it { should eq time_frame3.max }
|
989
|
+
end
|
990
|
+
end
|
991
|
+
|
992
|
+
it 'returns the empty time frame if the array is empty' do
|
993
|
+
expect(TimeFrame.covering_time_frame_for([])).to eq TimeFrame::EMPTY
|
994
|
+
end
|
995
|
+
|
996
|
+
it 'ignores empty time frames' do
|
997
|
+
time_frame = TimeFrame.new(min: time, duration: 2.hours)
|
998
|
+
expect(
|
999
|
+
TimeFrame.covering_time_frame_for([time_frame, TimeFrame::EMPTY])
|
1000
|
+
).to eq time_frame
|
1001
|
+
end
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
describe '.each_overlap' do
|
1005
|
+
|
1006
|
+
# Visualization of example input:
|
1007
|
+
#
|
1008
|
+
# array1: |---|-------| |-------|-----------|
|
1009
|
+
# array2: |-----------| |---| |---| |---|
|
1010
|
+
#
|
1011
|
+
# 0 1 2 3 4 5 6 7 8 9 10 11
|
1012
|
+
|
1013
|
+
let(:array1) do
|
1014
|
+
[
|
1015
|
+
TimeFrame.new(min: time, max: time + 1.hour),
|
1016
|
+
TimeFrame.new(min: time + 1.hour, max: time + 3.hours),
|
1017
|
+
TimeFrame.new(min: time + 4.hours, max: time + 6.hours),
|
1018
|
+
TimeFrame.new(min: time + 6.hours, max: time + 9.hours)
|
1019
|
+
]
|
1020
|
+
end
|
1021
|
+
|
1022
|
+
let(:array2) do
|
1023
|
+
[
|
1024
|
+
TimeFrame.new(min: time + 2.hours, max: time + 5.hour),
|
1025
|
+
TimeFrame.new(min: time + 6.hour, max: time + 7.hours),
|
1026
|
+
TimeFrame.new(min: time + 8.hours, max: time + 9.hours),
|
1027
|
+
TimeFrame.new(min: time + 10.hours, max: time + 11.hours),
|
1028
|
+
TimeFrame::EMPTY
|
1029
|
+
]
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
it 'yields the block for each overlap' do
|
1033
|
+
overlaps = []
|
1034
|
+
TimeFrame.each_overlap(array1, array2) { |a, b| overlaps << [a, b] }
|
1035
|
+
expect(overlaps).to eq [
|
1036
|
+
[array1[1], array2[0]],
|
1037
|
+
[array1[2], array2[0]],
|
1038
|
+
[array1[3], array2[1]],
|
1039
|
+
[array1[3], array2[2]]
|
1040
|
+
]
|
1041
|
+
end
|
1042
|
+
|
1043
|
+
it 'still works when switching arguments' do
|
1044
|
+
overlaps = []
|
1045
|
+
TimeFrame.each_overlap(array2, array1) { |a, b| overlaps << [a, b] }
|
1046
|
+
expect(overlaps).to eq [
|
1047
|
+
[array2[0], array1[1]],
|
1048
|
+
[array2[0], array1[2]],
|
1049
|
+
[array2[1], array1[3]],
|
1050
|
+
[array2[2], array1[3]]
|
1051
|
+
]
|
1052
|
+
end
|
1053
|
+
|
1054
|
+
it 'works if first array is empty' do
|
1055
|
+
overlaps = []
|
1056
|
+
TimeFrame.each_overlap([], array2) { |a, b| overlaps << [a, b] }
|
1057
|
+
expect(overlaps).to be_empty
|
1058
|
+
end
|
1059
|
+
|
1060
|
+
it 'works if second array is empty' do
|
1061
|
+
overlaps = []
|
1062
|
+
TimeFrame.each_overlap(array1, []) { |a, b| overlaps << [a, b] }
|
1063
|
+
expect(overlaps).to be_empty
|
1064
|
+
end
|
1065
|
+
end
|
1066
|
+
|
1067
|
+
describe '#inspect' do
|
1068
|
+
it 'works for a TimeFrame with same min and max' do
|
1069
|
+
time = Time.now
|
1070
|
+
expected = "#{time}..#{time}"
|
1071
|
+
tr = TimeFrame.new(min: time, max: time)
|
1072
|
+
actual = tr.inspect
|
1073
|
+
expect(actual).to eq expected
|
1074
|
+
end
|
1075
|
+
|
1076
|
+
it 'works for a TimeFrame created with min and max' do
|
1077
|
+
min = Time.now
|
1078
|
+
max = min + 10.minutes
|
1079
|
+
expected = "#{min}..#{max}"
|
1080
|
+
tr = TimeFrame.new(min: min, max: max)
|
1081
|
+
actual = tr.inspect
|
1082
|
+
expect(actual).to eq expected
|
1083
|
+
end
|
1084
|
+
|
1085
|
+
it 'works for a TimeFrame created with min and duration' do
|
1086
|
+
min = Time.now
|
1087
|
+
max = min + 10.minutes
|
1088
|
+
expected = "#{min}..#{max}"
|
1089
|
+
tr = TimeFrame.new(min: min, duration: 10.minutes)
|
1090
|
+
actual = tr.inspect
|
1091
|
+
expect(actual).to eq expected
|
1092
|
+
end
|
1093
|
+
|
1094
|
+
it 'is overridden for empty time frames' do
|
1095
|
+
expect(TimeFrame::EMPTY.inspect).to eq 'EMPTY'
|
1096
|
+
end
|
1097
|
+
end
|
1098
|
+
end
|