biz 1.7.0 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +30 -5
- data/lib/biz.rb +2 -0
- data/lib/biz/calculation.rb +2 -0
- data/lib/biz/calculation/active.rb +15 -4
- data/lib/biz/calculation/duration_within.rb +2 -0
- data/lib/biz/calculation/for_duration.rb +4 -4
- data/lib/biz/calculation/on_break.rb +3 -1
- data/lib/biz/calculation/on_holiday.rb +3 -1
- data/lib/biz/configuration.rb +27 -10
- data/lib/biz/core_ext.rb +2 -0
- data/lib/biz/core_ext/date.rb +2 -0
- data/lib/biz/core_ext/integer.rb +2 -0
- data/lib/biz/core_ext/time.rb +2 -0
- data/lib/biz/date.rb +2 -0
- data/lib/biz/dates.rb +2 -0
- data/lib/biz/day_of_week.rb +4 -0
- data/lib/biz/day_time.rb +9 -7
- data/lib/biz/duration.rb +9 -5
- data/lib/biz/error.rb +2 -0
- data/lib/biz/holiday.rb +11 -7
- data/lib/biz/interval.rb +4 -0
- data/lib/biz/periods.rb +3 -0
- data/lib/biz/periods/abstract.rb +22 -14
- data/lib/biz/periods/after.rb +14 -8
- data/lib/biz/periods/before.rb +14 -8
- data/lib/biz/periods/linear.rb +41 -0
- data/lib/biz/periods/proxy.rb +3 -1
- data/lib/biz/schedule.rb +3 -0
- data/lib/biz/time.rb +3 -1
- data/lib/biz/time_segment.rb +7 -1
- data/lib/biz/timeline.rb +2 -0
- data/lib/biz/timeline/abstract.rb +3 -1
- data/lib/biz/timeline/backward.rb +2 -0
- data/lib/biz/timeline/forward.rb +2 -0
- data/lib/biz/timeline/proxy.rb +3 -1
- data/lib/biz/validation.rb +4 -2
- data/lib/biz/version.rb +3 -1
- data/lib/biz/week.rb +8 -4
- data/lib/biz/week_time.rb +2 -0
- data/lib/biz/week_time/abstract.rb +8 -4
- data/lib/biz/week_time/end.rb +2 -0
- data/lib/biz/week_time/start.rb +2 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a7b79c92f3c6e05644da996c8c90a6cf7785a7dadd8bbb78c550c79f632091c5
|
4
|
+
data.tar.gz: 896b6de71bc1c79a919cb7c9086caf5f22cfbd8ecf0a8274489e7afd3812ebb7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8bbd2b8b8a835d68d4249a2545abdd43b82ba44decdc3cc9045ff70445e908a5fa72f4ce2ee9abb8aab341d30b1c9a191bf780a69045cc70361c5f810de478b8
|
7
|
+
data.tar.gz: 0caa36b7677937f5d7b1fc92d58bc92310cbf7996976cca7a22afd1be976d9d62d78337d783d73aabf0baa2d161b1c3c77fa00fb2ae80822cb53e7f0c0810d26
|
data/README.md
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
![biz](http://d26a57ydsghvgx.cloudfront.net/www/public/assets/images/bizlogo.png)
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/biz.svg)](http://badge.fury.io/rb/biz)
|
4
|
-
[![Build Status](https://travis-ci.
|
4
|
+
[![Build Status](https://travis-ci.com/zendesk/biz.svg?branch=master)](https://travis-ci.com/zendesk/biz)
|
5
5
|
[![Code Climate](https://codeclimate.com/github/zendesk/biz/badges/gpa.svg)](https://codeclimate.com/github/zendesk/biz)
|
6
6
|
[![Test Coverage](https://codeclimate.com/github/zendesk/biz/badges/coverage.svg)](https://codeclimate.com/github/zendesk/biz)
|
7
|
-
[![Dependency Status](https://gemnasium.com/zendesk/biz.svg)](https://gemnasium.com/zendesk/biz)
|
8
7
|
|
9
8
|
Time calculations using business hours.
|
10
9
|
|
@@ -16,6 +15,7 @@ Time calculations using business hours.
|
|
16
15
|
- Intervals spanning the entire day.
|
17
16
|
- Holidays.
|
18
17
|
- Breaks (time-segment holidays).
|
18
|
+
- Shifts (date-based intervals).
|
19
19
|
* Second-level calculation precision.
|
20
20
|
* Seamless Daylight Saving Time handling.
|
21
21
|
* Schedule intersection.
|
@@ -52,6 +52,11 @@ Biz.configure do |config|
|
|
52
52
|
sat: {'10:00' => '14:00'}
|
53
53
|
}
|
54
54
|
|
55
|
+
config.shifts = {
|
56
|
+
Date.new(2006, 1, 1) => {'09:00' => '12:00'},
|
57
|
+
Date.new(2006, 1, 7) => {'08:00' => '10:00', '12:00' => '14:00'}
|
58
|
+
}
|
59
|
+
|
55
60
|
config.breaks = {
|
56
61
|
Date.new(2006, 1, 2) => {'10:00' => '11:30'},
|
57
62
|
Date.new(2006, 1, 3) => {'14:15' => '14:30', '15:40' => '15:50'}
|
@@ -63,6 +68,10 @@ Biz.configure do |config|
|
|
63
68
|
end
|
64
69
|
```
|
65
70
|
|
71
|
+
Shifts act as exceptions to the hours configured for a particular date; that is,
|
72
|
+
if a date is configured with both hours-based intervals and shifts, the shifts
|
73
|
+
are in force and the intervals are disregarded.
|
74
|
+
|
66
75
|
Periods occurring on holidays are disregarded. Similarly, any segment of a
|
67
76
|
period that overlaps with a break is treated as inactive.
|
68
77
|
|
@@ -194,6 +203,11 @@ schedule_1 = Biz::Schedule.new do |config|
|
|
194
203
|
sat: {'11:00' => '14:30'}
|
195
204
|
}
|
196
205
|
|
206
|
+
config.shifts = {
|
207
|
+
Date.new(2016, 7, 1) => {'10:00' => '13:00', '15:00' => '16:00'},
|
208
|
+
Date.new(2016, 7, 2) => {'14:00' => '17:00'}
|
209
|
+
}
|
210
|
+
|
197
211
|
config.breaks = {
|
198
212
|
Date.new(2016, 6, 2) => {'09:00' => '10:30', '16:00' => '16:30'},
|
199
213
|
Date.new(2016, 6, 3) => {'12:15' => '12:45'}
|
@@ -213,6 +227,11 @@ schedule_2 = Biz::Schedule.new do |config|
|
|
213
227
|
thu: {'11:00' => '12:00', '13:00' => '14:00'}
|
214
228
|
}
|
215
229
|
|
230
|
+
config.shifts = {
|
231
|
+
Date.new(2016, 7, 1) => {'15:30' => '16:30'},
|
232
|
+
Date.new(2016, 7, 5) => {'14:00' => '18:00'}
|
233
|
+
}
|
234
|
+
|
216
235
|
config.breaks = {
|
217
236
|
Date.new(2016, 6, 3) => {'13:30' => '14:00'},
|
218
237
|
Date.new(2016, 6, 4) => {'11:00' => '12:00'}
|
@@ -231,8 +250,9 @@ schedule_1 & schedule_2
|
|
231
250
|
```
|
232
251
|
|
233
252
|
The resulting schedule will be a combination of the two schedules: an
|
234
|
-
intersection of the intervals, a union of the breaks and holidays,
|
235
|
-
zone of the first schedule.
|
253
|
+
intersection of the intervals, a union of the breaks and holidays,
|
254
|
+
and the time zone of the first schedule. Any configured shifts will be
|
255
|
+
disregarded.
|
236
256
|
|
237
257
|
For the above example, the resulting schedule would be equivalent to one with
|
238
258
|
the following configuration:
|
@@ -246,6 +266,11 @@ Biz::Schedule.new do |config|
|
|
246
266
|
thu: {'11:00' => '12:00', '13:00' => '14:00'}
|
247
267
|
}
|
248
268
|
|
269
|
+
config.shifts = {
|
270
|
+
Date.new(2016, 7, 1) => {'15:30' => '16:00'},
|
271
|
+
Date.new(2016, 7, 5) => {'14:00' => '16:00'}
|
272
|
+
}
|
273
|
+
|
249
274
|
config.breaks = {
|
250
275
|
Date.new(2016, 6, 2) => {'09:00' => '10:30', '16:00' => '16:30'},
|
251
276
|
Date.new(2016, 6, 3) => {'12:15' => '12:45', '13:30' => '14:00'},
|
@@ -316,7 +341,7 @@ To open a console with the gem and sample schedule loaded:
|
|
316
341
|
|
317
342
|
## Copyright and license
|
318
343
|
|
319
|
-
Copyright 2015-
|
344
|
+
Copyright 2015-18 Zendesk
|
320
345
|
|
321
346
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
322
347
|
this gem except in compliance with the License.
|
data/lib/biz.rb
CHANGED
data/lib/biz/calculation.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Biz
|
2
4
|
module Calculation
|
3
5
|
class Active
|
@@ -8,16 +10,25 @@ module Biz
|
|
8
10
|
end
|
9
11
|
|
10
12
|
def result
|
11
|
-
|
12
|
-
|
13
|
-
|
13
|
+
return in_hours? && active? if schedule.shifts.none?
|
14
|
+
|
15
|
+
schedule.periods.after(time).first.contains?(time)
|
14
16
|
end
|
15
17
|
|
16
|
-
|
18
|
+
private
|
17
19
|
|
18
20
|
attr_reader :schedule,
|
19
21
|
:time
|
20
22
|
|
23
|
+
def in_hours?
|
24
|
+
schedule.intervals.any? { |interval| interval.contains?(time) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def active?
|
28
|
+
schedule.holidays.none? { |holiday| holiday.contains?(time) } &&
|
29
|
+
schedule.breaks.none? { |brake| brake.contains?(time) }
|
30
|
+
end
|
31
|
+
|
21
32
|
end
|
22
33
|
end
|
23
34
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Biz
|
2
4
|
module Calculation
|
3
5
|
class ForDuration
|
@@ -86,16 +88,14 @@ module Biz
|
|
86
88
|
@schedule = schedule
|
87
89
|
@scalar = Integer(scalar)
|
88
90
|
|
89
|
-
fail ArgumentError, 'negative scalar' if @scalar
|
91
|
+
fail ArgumentError, 'negative scalar' if @scalar.negative?
|
90
92
|
end
|
91
93
|
|
92
|
-
|
94
|
+
private
|
93
95
|
|
94
96
|
attr_reader :schedule,
|
95
97
|
:scalar
|
96
98
|
|
97
|
-
private
|
98
|
-
|
99
99
|
def unit
|
100
100
|
self.class.unit
|
101
101
|
end
|
data/lib/biz/configuration.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Biz
|
2
4
|
class Configuration
|
3
5
|
|
@@ -21,11 +23,25 @@ module Biz
|
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
26
|
+
def shifts
|
27
|
+
@shifts ||= begin
|
28
|
+
raw
|
29
|
+
.shifts
|
30
|
+
.flat_map { |date, hours|
|
31
|
+
hours.map { |timestamps| date_period(date, timestamps) }
|
32
|
+
}
|
33
|
+
.sort
|
34
|
+
.freeze
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
24
38
|
def breaks
|
25
39
|
@breaks ||= begin
|
26
40
|
raw
|
27
41
|
.breaks
|
28
|
-
.flat_map { |date, hours|
|
42
|
+
.flat_map { |date, hours|
|
43
|
+
hours.map { |timestamps| date_period(date, timestamps) }
|
44
|
+
}
|
29
45
|
.sort
|
30
46
|
.freeze
|
31
47
|
end
|
@@ -69,6 +85,7 @@ module Biz
|
|
69
85
|
def to_proc
|
70
86
|
proc do |config|
|
71
87
|
config.hours = raw.hours
|
88
|
+
config.shifts = raw.shifts
|
72
89
|
config.breaks = raw.breaks
|
73
90
|
config.holidays = raw.holidays
|
74
91
|
config.time_zone = raw.time_zone
|
@@ -95,13 +112,11 @@ module Biz
|
|
95
112
|
}
|
96
113
|
end
|
97
114
|
|
98
|
-
def
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
)
|
104
|
-
}
|
115
|
+
def date_period(date, timestamps)
|
116
|
+
TimeSegment.new(
|
117
|
+
time.on_date(date, DayTime.from_timestamp(timestamps.first)),
|
118
|
+
time.on_date(date, DayTime.from_timestamp(timestamps.last))
|
119
|
+
)
|
105
120
|
end
|
106
121
|
|
107
122
|
def intersected_intervals(other)
|
@@ -121,7 +136,7 @@ module Biz
|
|
121
136
|
end
|
122
137
|
end
|
123
138
|
|
124
|
-
Raw = Struct.new(:hours, :breaks, :holidays, :time_zone) do
|
139
|
+
Raw = Struct.new(:hours, :shifts, :breaks, :holidays, :time_zone) do
|
125
140
|
module Default
|
126
141
|
HOURS = {
|
127
142
|
mon: {'09:00' => '17:00'},
|
@@ -131,15 +146,17 @@ module Biz
|
|
131
146
|
fri: {'09:00' => '17:00'}
|
132
147
|
}.freeze
|
133
148
|
|
149
|
+
SHIFTS = [].freeze
|
134
150
|
BREAKS = [].freeze
|
135
151
|
HOLIDAYS = [].freeze
|
136
|
-
TIME_ZONE = 'Etc/UTC'
|
152
|
+
TIME_ZONE = 'Etc/UTC'
|
137
153
|
end
|
138
154
|
|
139
155
|
def initialize(*)
|
140
156
|
super
|
141
157
|
|
142
158
|
self.hours ||= Default::HOURS
|
159
|
+
self.shifts ||= Default::SHIFTS
|
143
160
|
self.breaks ||= Default::BREAKS
|
144
161
|
self.holidays ||= Default::HOLIDAYS
|
145
162
|
self.time_zone ||= Default::TIME_ZONE
|
data/lib/biz/core_ext.rb
CHANGED
data/lib/biz/core_ext/date.rb
CHANGED
data/lib/biz/core_ext/integer.rb
CHANGED
data/lib/biz/core_ext/time.rb
CHANGED
data/lib/biz/date.rb
CHANGED
data/lib/biz/dates.rb
CHANGED
data/lib/biz/day_of_week.rb
CHANGED
data/lib/biz/day_time.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Biz
|
2
4
|
class DayTime
|
3
5
|
|
4
6
|
VALID_SECONDS = (0..Time.day_seconds).freeze
|
5
7
|
|
6
8
|
module Timestamp
|
7
|
-
FORMAT = '%02d:%02d'
|
9
|
+
FORMAT = '%02d:%02d'
|
8
10
|
PATTERN = /\A(?<hour>\d{2}):(?<minute>\d{2})(:?(?<second>\d{2}))?\Z/
|
9
11
|
end
|
10
12
|
|
@@ -80,18 +82,18 @@ module Biz
|
|
80
82
|
format(Timestamp::FORMAT, hour, minute)
|
81
83
|
end
|
82
84
|
|
83
|
-
def <=>(other)
|
84
|
-
return unless other.is_a?(self.class)
|
85
|
-
|
86
|
-
day_second <=> other.day_second
|
87
|
-
end
|
88
|
-
|
89
85
|
private
|
90
86
|
|
91
87
|
def valid_second?
|
92
88
|
VALID_SECONDS.cover?(day_second)
|
93
89
|
end
|
94
90
|
|
91
|
+
def <=>(other)
|
92
|
+
return unless other.is_a?(self.class)
|
93
|
+
|
94
|
+
day_second <=> other.day_second
|
95
|
+
end
|
96
|
+
|
95
97
|
MIDNIGHT = from_hour(0)
|
96
98
|
ENDNIGHT = from_hour(24)
|
97
99
|
|
data/lib/biz/duration.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Biz
|
2
4
|
class Duration
|
3
5
|
|
@@ -50,22 +52,24 @@ module Biz
|
|
50
52
|
end
|
51
53
|
|
52
54
|
def positive?
|
53
|
-
seconds
|
55
|
+
seconds.positive?
|
54
56
|
end
|
55
57
|
|
56
58
|
def abs
|
57
59
|
self.class.new(seconds.abs)
|
58
60
|
end
|
59
61
|
|
62
|
+
protected
|
63
|
+
|
64
|
+
attr_reader :seconds
|
65
|
+
|
66
|
+
private
|
67
|
+
|
60
68
|
def <=>(other)
|
61
69
|
return unless other.is_a?(self.class)
|
62
70
|
|
63
71
|
seconds <=> other.seconds
|
64
72
|
end
|
65
73
|
|
66
|
-
protected
|
67
|
-
|
68
|
-
attr_reader :seconds
|
69
|
-
|
70
74
|
end
|
71
75
|
end
|
data/lib/biz/error.rb
CHANGED
data/lib/biz/holiday.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Biz
|
2
4
|
class Holiday
|
3
5
|
|
@@ -13,7 +15,7 @@ module Biz
|
|
13
15
|
delegate contains?: :to_time_segment
|
14
16
|
|
15
17
|
def to_time_segment
|
16
|
-
@
|
18
|
+
@to_time_segment ||= begin
|
17
19
|
TimeSegment.new(
|
18
20
|
Time.new(time_zone).on_date(date, DayTime.midnight),
|
19
21
|
Time.new(time_zone).on_date(date, DayTime.endnight)
|
@@ -21,12 +23,6 @@ module Biz
|
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
24
|
-
def <=>(other)
|
25
|
-
return unless other.is_a?(self.class)
|
26
|
-
|
27
|
-
[date, time_zone] <=> [other.date, other.time_zone]
|
28
|
-
end
|
29
|
-
|
30
26
|
protected
|
31
27
|
|
32
28
|
attr_reader :date,
|
@@ -38,5 +34,13 @@ module Biz
|
|
38
34
|
date
|
39
35
|
end
|
40
36
|
|
37
|
+
private
|
38
|
+
|
39
|
+
def <=>(other)
|
40
|
+
return unless other.is_a?(self.class)
|
41
|
+
|
42
|
+
[date, time_zone] <=> [other.date, other.time_zone]
|
43
|
+
end
|
44
|
+
|
41
45
|
end
|
42
46
|
end
|
data/lib/biz/interval.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Biz
|
2
4
|
class Interval
|
3
5
|
|
@@ -54,6 +56,8 @@ module Biz
|
|
54
56
|
self.class.new(lower_bound, [lower_bound, upper_bound].max, time_zone)
|
55
57
|
end
|
56
58
|
|
59
|
+
private
|
60
|
+
|
57
61
|
def <=>(other)
|
58
62
|
return unless other.is_a?(self.class)
|
59
63
|
|
data/lib/biz/periods.rb
CHANGED
data/lib/biz/periods/abstract.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Biz
|
2
4
|
module Periods
|
3
5
|
class Abstract < SimpleDelegator
|
@@ -13,17 +15,17 @@ module Biz
|
|
13
15
|
Timeline::Proxy.new(self)
|
14
16
|
end
|
15
17
|
|
16
|
-
|
18
|
+
private
|
17
19
|
|
18
20
|
attr_reader :schedule,
|
19
|
-
:origin
|
20
|
-
|
21
|
-
|
21
|
+
:origin,
|
22
|
+
:boundary,
|
23
|
+
:intervals,
|
24
|
+
:shifts
|
22
25
|
|
23
26
|
def periods
|
24
|
-
|
27
|
+
Linear.new(week_periods, shifts, selector)
|
25
28
|
.lazy
|
26
|
-
.flat_map { |week| business_periods(week) }
|
27
29
|
.select { |period| relevant?(period) }
|
28
30
|
.map { |period| period & boundary }
|
29
31
|
.flat_map { |period| active_periods(period) }
|
@@ -31,20 +33,26 @@ module Biz
|
|
31
33
|
.reject(&:empty?)
|
32
34
|
end
|
33
35
|
|
34
|
-
def
|
35
|
-
|
36
|
+
def week_periods
|
37
|
+
weeks
|
38
|
+
.lazy
|
39
|
+
.flat_map { |week|
|
40
|
+
intervals.map { |interval| interval.to_time_segment(week) }
|
41
|
+
}
|
36
42
|
end
|
37
43
|
|
38
44
|
def active_periods(period)
|
39
|
-
schedule
|
40
|
-
|
41
|
-
|
45
|
+
schedule
|
46
|
+
.breaks
|
47
|
+
.reduce([period]) { |periods, break_period|
|
48
|
+
periods.flat_map { |active_period| active_period / break_period }
|
49
|
+
}
|
42
50
|
end
|
43
51
|
|
44
52
|
def on_holiday?(period)
|
45
|
-
schedule
|
46
|
-
|
47
|
-
|
53
|
+
schedule
|
54
|
+
.holidays
|
55
|
+
.any? { |holiday| holiday.contains?(period.start_time) }
|
48
56
|
end
|
49
57
|
|
50
58
|
end
|
data/lib/biz/periods/after.rb
CHANGED
@@ -1,13 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Biz
|
2
4
|
module Periods
|
3
5
|
class After < Abstract
|
4
6
|
|
7
|
+
def initialize(schedule, origin)
|
8
|
+
@boundary = TimeSegment.after(origin)
|
9
|
+
@intervals = schedule.intervals
|
10
|
+
@shifts = schedule.shifts
|
11
|
+
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
5
15
|
def timeline
|
6
16
|
super.forward
|
7
17
|
end
|
8
18
|
|
9
19
|
private
|
10
20
|
|
21
|
+
def selector
|
22
|
+
:min_by
|
23
|
+
end
|
24
|
+
|
11
25
|
def weeks
|
12
26
|
Range.new(
|
13
27
|
Week.since_epoch(schedule.in_zone.local(origin)),
|
@@ -19,14 +33,6 @@ module Biz
|
|
19
33
|
origin < period.end_time
|
20
34
|
end
|
21
35
|
|
22
|
-
def boundary
|
23
|
-
@boundary ||= TimeSegment.after(origin)
|
24
|
-
end
|
25
|
-
|
26
|
-
def intervals
|
27
|
-
@intervals ||= schedule.intervals
|
28
|
-
end
|
29
|
-
|
30
36
|
end
|
31
37
|
end
|
32
38
|
end
|
data/lib/biz/periods/before.rb
CHANGED
@@ -1,13 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Biz
|
2
4
|
module Periods
|
3
5
|
class Before < Abstract
|
4
6
|
|
7
|
+
def initialize(schedule, origin)
|
8
|
+
@boundary = TimeSegment.before(origin)
|
9
|
+
@intervals = schedule.intervals.reverse
|
10
|
+
@shifts = schedule.shifts.reverse
|
11
|
+
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
5
15
|
def timeline
|
6
16
|
super.backward
|
7
17
|
end
|
8
18
|
|
9
19
|
private
|
10
20
|
|
21
|
+
def selector
|
22
|
+
:max_by
|
23
|
+
end
|
24
|
+
|
11
25
|
def weeks
|
12
26
|
Week
|
13
27
|
.since_epoch(schedule.in_zone.local(origin))
|
@@ -22,14 +36,6 @@ module Biz
|
|
22
36
|
super.reverse
|
23
37
|
end
|
24
38
|
|
25
|
-
def boundary
|
26
|
-
@boundary ||= TimeSegment.before(origin)
|
27
|
-
end
|
28
|
-
|
29
|
-
def intervals
|
30
|
-
@intervals ||= schedule.intervals.reverse
|
31
|
-
end
|
32
|
-
|
33
39
|
end
|
34
40
|
end
|
35
41
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Biz
|
4
|
+
module Periods
|
5
|
+
class Linear < SimpleDelegator
|
6
|
+
|
7
|
+
def initialize(periods, shifts, selector)
|
8
|
+
@periods = periods.to_enum
|
9
|
+
@shifts = shifts.to_enum
|
10
|
+
@selector = selector
|
11
|
+
@sequences = [@periods, @shifts]
|
12
|
+
|
13
|
+
super(linear_periods)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
attr_reader :periods,
|
19
|
+
:shifts,
|
20
|
+
:selector,
|
21
|
+
:sequences
|
22
|
+
|
23
|
+
def linear_periods
|
24
|
+
Enumerator.new do |yielder|
|
25
|
+
loop do
|
26
|
+
periods.next and next if periods.peek.date == shifts.peek.date
|
27
|
+
|
28
|
+
yielder << begin
|
29
|
+
sequences
|
30
|
+
.public_send(selector) { |sequence| sequence.peek.date }
|
31
|
+
.next
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
loop do yielder << periods.next end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/biz/periods/proxy.rb
CHANGED
data/lib/biz/schedule.rb
CHANGED
data/lib/biz/time.rb
CHANGED
data/lib/biz/time_segment.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Biz
|
2
4
|
class TimeSegment
|
3
5
|
|
@@ -14,10 +16,12 @@ module Biz
|
|
14
16
|
def initialize(start_time, end_time)
|
15
17
|
@start_time = start_time
|
16
18
|
@end_time = end_time
|
19
|
+
@date = start_time.to_date
|
17
20
|
end
|
18
21
|
|
19
22
|
attr_reader :start_time,
|
20
|
-
:end_time
|
23
|
+
:end_time,
|
24
|
+
:date
|
21
25
|
|
22
26
|
def duration
|
23
27
|
Duration.new(end_time - start_time)
|
@@ -49,6 +53,8 @@ module Biz
|
|
49
53
|
].reject(&:empty?).map { |potential| self & potential }
|
50
54
|
end
|
51
55
|
|
56
|
+
private
|
57
|
+
|
52
58
|
def <=>(other)
|
53
59
|
return unless other.is_a?(self.class)
|
54
60
|
|
data/lib/biz/timeline.rb
CHANGED
data/lib/biz/timeline/forward.rb
CHANGED
data/lib/biz/timeline/proxy.rb
CHANGED
data/lib/biz/validation.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Biz
|
2
4
|
class Validation
|
3
5
|
|
@@ -15,7 +17,7 @@ module Biz
|
|
15
17
|
self
|
16
18
|
end
|
17
19
|
|
18
|
-
|
20
|
+
private
|
19
21
|
|
20
22
|
attr_reader :raw
|
21
23
|
|
@@ -30,7 +32,7 @@ module Biz
|
|
30
32
|
fail Error::Configuration, message unless condition.call(raw)
|
31
33
|
end
|
32
34
|
|
33
|
-
|
35
|
+
private
|
34
36
|
|
35
37
|
attr_reader :message,
|
36
38
|
:condition
|
data/lib/biz/version.rb
CHANGED
data/lib/biz/week.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Biz
|
2
4
|
class Week
|
3
5
|
|
@@ -41,15 +43,17 @@ module Biz
|
|
41
43
|
self.class.new(week + other.week)
|
42
44
|
end
|
43
45
|
|
46
|
+
protected
|
47
|
+
|
48
|
+
attr_reader :week
|
49
|
+
|
50
|
+
private
|
51
|
+
|
44
52
|
def <=>(other)
|
45
53
|
return unless other.is_a?(self.class)
|
46
54
|
|
47
55
|
week <=> other.week
|
48
56
|
end
|
49
57
|
|
50
|
-
protected
|
51
|
-
|
52
|
-
attr_reader :week
|
53
|
-
|
54
58
|
end
|
55
59
|
end
|
data/lib/biz/week_time.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Biz
|
2
4
|
module WeekTime
|
3
5
|
class Abstract
|
@@ -33,16 +35,18 @@ module Biz
|
|
33
35
|
timestamp
|
34
36
|
] => :day_time
|
35
37
|
|
38
|
+
protected
|
39
|
+
|
40
|
+
attr_reader :week_minute
|
41
|
+
|
42
|
+
private
|
43
|
+
|
36
44
|
def <=>(other)
|
37
45
|
return unless other.is_a?(WeekTime::Abstract)
|
38
46
|
|
39
47
|
week_minute <=> other.week_minute
|
40
48
|
end
|
41
49
|
|
42
|
-
protected
|
43
|
-
|
44
|
-
attr_reader :week_minute
|
45
|
-
|
46
50
|
end
|
47
51
|
end
|
48
52
|
end
|
data/lib/biz/week_time/end.rb
CHANGED
data/lib/biz/week_time/start.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: biz
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Craig Little
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2018-09-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: clavius
|
@@ -72,6 +72,7 @@ files:
|
|
72
72
|
- lib/biz/periods/abstract.rb
|
73
73
|
- lib/biz/periods/after.rb
|
74
74
|
- lib/biz/periods/before.rb
|
75
|
+
- lib/biz/periods/linear.rb
|
75
76
|
- lib/biz/periods/proxy.rb
|
76
77
|
- lib/biz/schedule.rb
|
77
78
|
- lib/biz/time.rb
|
@@ -100,7 +101,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
100
101
|
requirements:
|
101
102
|
- - ">="
|
102
103
|
- !ruby/object:Gem::Version
|
103
|
-
version: '2.
|
104
|
+
version: '2.3'
|
104
105
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
106
|
requirements:
|
106
107
|
- - ">="
|
@@ -108,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
108
109
|
version: '0'
|
109
110
|
requirements: []
|
110
111
|
rubyforge_project:
|
111
|
-
rubygems_version: 2.6
|
112
|
+
rubygems_version: 2.7.6
|
112
113
|
signing_key:
|
113
114
|
specification_version: 4
|
114
115
|
summary: Business hours calculations
|