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