working_hours 1.1.1 → 1.1.2
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 +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +1 -1
- data/CHANGELOG.md +7 -1
- data/README.md +10 -0
- data/lib/working_hours/computation.rb +27 -2
- data/lib/working_hours/version.rb +1 -1
- data/spec/working_hours/computation_spec.rb +167 -0
- data/spec/working_hours/duration_spec.rb +4 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 67a0f2eb365a59918de39db2f11c29793a458f07
|
4
|
+
data.tar.gz: 003cae888ac4274d8fa53fffee04c8bddb83b3b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1917dd8e540d5b79d19f85c4c29ee10951f42f37a86451eacbaa9e03522db3f76b93051358d87404bb74df3ead6c2940c029f7b80e713af861a3517e562f692a
|
7
|
+
data.tar.gz: 78aaf458c8bf63b49f9a94cf9134a9f6f95ae8293075d799acca986ef5b6da4417fae2315f1450d404884384864f67fc8ae51482d5f75fc02727df76a43d5dbf
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
-
[Compare master with v1.1.
|
3
|
+
[Compare master with v1.1.2](https://github.com/intrepidd/working_hours/compare/v1.1.2...master)
|
4
|
+
|
5
|
+
# v1.1.2
|
6
|
+
* Fixed an issue of float imprecision causing infinite loop - [#27](https://github.com/Intrepidd/working_hours/pull/27)
|
7
|
+
* Added #next_working_time and #advance_to_closing_time - [#23](https://github.com/Intrepidd/working_hours/pull/23)
|
8
|
+
|
9
|
+
_06/12/2015_
|
4
10
|
|
5
11
|
# v1.1.1
|
6
12
|
* Fix infinite loop happening when rewinding seconds and crossing through midgnight
|
data/README.md
CHANGED
@@ -57,6 +57,16 @@ Time.utc(2014, 8, 4, 7, 16).in_working_hours? # => false
|
|
57
57
|
# Advance to next working time
|
58
58
|
WorkingHours.advance_to_working_time(Time.utc(2014, 8, 4, 7, 16)) # => Mon, 04 Aug 2014 09:00:00 UTC +00:00
|
59
59
|
|
60
|
+
# Advance to next closing time
|
61
|
+
WorkingHours.advance_to_closing_time(Time.utc(2014, 8, 4, 7, 16)) # => Mon, 04 Aug 2014 17:00:00 UTC +00:00
|
62
|
+
WorkingHours.advance_to_closing_time(Time.utc(2014, 8, 4, 10, 16)) # => Mon, 04 Aug 2014 17:00:00 UTC +00:00
|
63
|
+
WorkingHours.advance_to_closing_time(Time.utc(2014, 8, 4, 18, 16)) # => Tue, 05 Aug 2014 17:00:00 UTC +00:00
|
64
|
+
|
65
|
+
# Next working time
|
66
|
+
sunday = Time.utc(2014, 8, 3)
|
67
|
+
monday = WorkingHours.next_working_time(sunday) # => Mon, 04 Aug 2014 09:00:00 UTC +00:00
|
68
|
+
tuesday = WorkingHours.next_working_time(monday) # => Tue, 05 Aug 2014 09:00:00 UTC +00:00
|
69
|
+
|
60
70
|
# Return to previous working time
|
61
71
|
WorkingHours.return_to_working_time(Time.utc(2014, 8, 4, 7, 16)) # => Fri, 01 Aug 2014 17:00:00 UTC +00:00
|
62
72
|
```
|
@@ -39,7 +39,7 @@ module WorkingHours
|
|
39
39
|
config[:working_hours][time.wday].each do |from, to|
|
40
40
|
if time_in_day >= from and time_in_day < to
|
41
41
|
# take all we can
|
42
|
-
take = [to - time_in_day, seconds].min
|
42
|
+
take = [to - time_in_day, seconds].min
|
43
43
|
# advance time
|
44
44
|
time += take
|
45
45
|
# decrease seconds
|
@@ -55,7 +55,7 @@ module WorkingHours
|
|
55
55
|
config[:working_hours][time.wday].reverse_each do |from, to|
|
56
56
|
if time_in_day > from and time_in_day <= to
|
57
57
|
# take all we can
|
58
|
-
take = [time_in_day - from, -seconds].min
|
58
|
+
take = [time_in_day - from, -seconds].min
|
59
59
|
# advance time
|
60
60
|
time -= take
|
61
61
|
# decrease seconds
|
@@ -85,6 +85,31 @@ module WorkingHours
|
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
|
+
def advance_to_closing_time time, config: nil
|
89
|
+
config ||= wh_config
|
90
|
+
time = in_config_zone(time, config: config).round
|
91
|
+
loop do
|
92
|
+
# skip holidays and weekends
|
93
|
+
while not working_day?(time, config: config)
|
94
|
+
time = (time + 1.day).beginning_of_day
|
95
|
+
end
|
96
|
+
# find next working range after time
|
97
|
+
time_in_day = time.seconds_since_midnight
|
98
|
+
time = time.beginning_of_day
|
99
|
+
(config[:working_hours][time.wday] || {}).each do |from, to|
|
100
|
+
return time + to if time_in_day >= from and time_in_day < to
|
101
|
+
return time + to if from >= time_in_day
|
102
|
+
end
|
103
|
+
# if none is found, go to next day and loop
|
104
|
+
time = time + 1.day
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def next_working_time(time, config: nil)
|
109
|
+
time = advance_to_closing_time(time, config: config) if in_working_hours?(time, config: config)
|
110
|
+
advance_to_working_time(time, config: config)
|
111
|
+
end
|
112
|
+
|
88
113
|
def return_to_working_time(time, config: nil)
|
89
114
|
# return_to_exact_working_time may return values with a high number of milliseconds,
|
90
115
|
# this is necessary for the end of day hack, here we return a rounded value for the
|
@@ -148,6 +148,173 @@ describe WorkingHours::Computation do
|
|
148
148
|
end
|
149
149
|
end
|
150
150
|
|
151
|
+
describe '#advance_to_closing_time' do
|
152
|
+
|
153
|
+
it 'jumps non-working day' do
|
154
|
+
WorkingHours::Config.holidays = [Date.new(2014, 5, 1)]
|
155
|
+
holiday = Time.utc(2014, 5, 1, 12, 0)
|
156
|
+
friday_closing = Time.utc(2014, 5, 2, 17, 0)
|
157
|
+
sunday = Time.utc(2014, 6, 1, 12, 0)
|
158
|
+
monday_closing = Time.utc(2014, 6, 2, 17, 0)
|
159
|
+
expect(advance_to_closing_time(holiday)).to eq(friday_closing)
|
160
|
+
expect(advance_to_closing_time(sunday)).to eq(monday_closing)
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'moves to the closing time during working hours' do
|
164
|
+
in_open_time = Time.utc(2014, 4, 7, 12, 0)
|
165
|
+
closing_time = Time.utc(2014, 4, 7, 17, 0)
|
166
|
+
expect(advance_to_closing_time(in_open_time)).to eq(closing_time)
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'jumps outside working hours' do
|
170
|
+
monday_before_opening = Time.utc(2014, 4, 7, 8, 59)
|
171
|
+
monday_closing = Time.utc(2014, 4, 7, 17, 0)
|
172
|
+
tuesday_closing = Time.utc(2014, 4, 8, 17, 0)
|
173
|
+
expect(advance_to_closing_time(monday_before_opening)).to eq(monday_closing)
|
174
|
+
expect(advance_to_closing_time(monday_closing)).to eq(tuesday_closing)
|
175
|
+
end
|
176
|
+
|
177
|
+
context 'moving between timespans' do
|
178
|
+
before do
|
179
|
+
WorkingHours::Config.working_hours = {
|
180
|
+
mon: {'07:00' => '12:00', '13:00' => '18:00'},
|
181
|
+
tue: {'09:00' => '17:00'},
|
182
|
+
wed: {'09:00' => '17:00'},
|
183
|
+
thu: {'09:00' => '17:00'},
|
184
|
+
fri: {'09:00' => '17:00'}
|
185
|
+
}
|
186
|
+
end
|
187
|
+
|
188
|
+
let(:monday_morning) { Time.utc(2014, 4, 7, 10) }
|
189
|
+
let(:morning_closing) { Time.utc(2014, 4, 7, 12) }
|
190
|
+
let(:afternoon_closing) { Time.utc(2014, 4, 7, 18) }
|
191
|
+
let(:monday_break) { Time.utc(2014, 4, 7, 12) }
|
192
|
+
let(:tuesday_closing) { Time.utc(2014, 4, 8, 17) }
|
193
|
+
|
194
|
+
it 'moves from morning to end of morning slot' do
|
195
|
+
expect(advance_to_closing_time(monday_morning)).to eq(morning_closing)
|
196
|
+
end
|
197
|
+
|
198
|
+
it 'moves from break time to end of afternoon slot' do
|
199
|
+
expect(advance_to_closing_time(monday_break)).to eq(afternoon_closing)
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'moves from afternoon closing slot to next day' do
|
203
|
+
expect(advance_to_closing_time(afternoon_closing)).to eq(tuesday_closing)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
context 'supporting midnight' do
|
208
|
+
before do
|
209
|
+
WorkingHours::Config.working_hours = {
|
210
|
+
mon: {'00:00' => '24:00'},
|
211
|
+
tue: {'09:00' => '17:00'}
|
212
|
+
}
|
213
|
+
end
|
214
|
+
|
215
|
+
let(:monday_morning) { Time.utc(2014, 4, 7, 0) }
|
216
|
+
let(:monday_closing) { Time.utc(2014, 4, 7) + 86399.999999 }
|
217
|
+
let(:tuesday_closing) { Time.utc(2014, 4, 8, 17) }
|
218
|
+
let(:sunday) { Time.utc(2014, 4, 6, 17) }
|
219
|
+
|
220
|
+
it 'moves from morning to midnight' do
|
221
|
+
expect(advance_to_closing_time(monday_morning)).to eq(monday_closing)
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'moves from midnight to end of next slot' do
|
225
|
+
expect(advance_to_closing_time(monday_closing)).to eq(tuesday_closing)
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'moves over midnight' do
|
229
|
+
expect(advance_to_closing_time(sunday)).to eq(monday_closing)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'works with any input timezone (converts to config)' do
|
234
|
+
# Monday 0 am (-09:00) is 9am in UTC time, working time!
|
235
|
+
monday_morning = Time.new(2014, 4, 7, 0, 0, 0 , "-09:00")
|
236
|
+
monday_closing = Time.new(2014, 4, 7, 12, 0, 0 , "-05:00")
|
237
|
+
monday_night = Time.new(2014, 4, 7, 22, 0, 0, "+02:00")
|
238
|
+
tuesday_evening = Time.utc(2014, 4, 8, 17)
|
239
|
+
expect(advance_to_closing_time(monday_morning)).to eq(monday_closing)
|
240
|
+
expect(advance_to_closing_time(monday_night)).to eq(tuesday_evening)
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'returns time in config zone' do
|
244
|
+
WorkingHours::Config.time_zone = 'Tokyo'
|
245
|
+
expect(advance_to_closing_time(Time.new(2014, 4, 7, 0, 0, 0)).zone).to eq('JST')
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
describe '#next_working_time' do
|
250
|
+
|
251
|
+
it 'jumps non-working day' do
|
252
|
+
WorkingHours::Config.holidays = [Date.new(2014, 5, 1)]
|
253
|
+
holiday = Time.utc(2014, 5, 1, 12, 0)
|
254
|
+
sunday = Time.utc(2014, 6, 1, 12, 0)
|
255
|
+
expect(next_working_time(holiday)).to eq(Time.utc(2014, 5, 2, 9, 0))
|
256
|
+
expect(next_working_time(sunday)).to eq(Time.utc(2014, 6, 2, 9, 0))
|
257
|
+
end
|
258
|
+
|
259
|
+
it 'moves to the following timespan during working hours' do
|
260
|
+
monday = Time.utc(2014, 4, 7, 12, 0)
|
261
|
+
tuesday = Time.utc(2014, 4, 8, 9, 0)
|
262
|
+
expect(next_working_time(monday)).to eq(tuesday)
|
263
|
+
end
|
264
|
+
|
265
|
+
it 'jumps outside working hours' do
|
266
|
+
monday_before_opening = Time.utc(2014, 4, 7, 8, 59)
|
267
|
+
monday_opening = Time.utc(2014, 4, 7, 9, 0)
|
268
|
+
monday_closing = Time.utc(2014, 4, 7, 17, 0)
|
269
|
+
tuesday_opening = Time.utc(2014, 4, 8, 9, 0)
|
270
|
+
expect(next_working_time(monday_before_opening)).to eq(monday_opening)
|
271
|
+
expect(next_working_time(monday_closing)).to eq(tuesday_opening)
|
272
|
+
end
|
273
|
+
|
274
|
+
context 'move between timespans' do
|
275
|
+
before do
|
276
|
+
WorkingHours::Config.working_hours = {
|
277
|
+
mon: {'07:00' => '12:00', '13:00' => '18:00'},
|
278
|
+
tue: {'09:00' => '17:00'},
|
279
|
+
wed: {'09:00' => '17:00'},
|
280
|
+
thu: {'09:00' => '17:00'},
|
281
|
+
fri: {'09:00' => '17:00'}
|
282
|
+
}
|
283
|
+
end
|
284
|
+
|
285
|
+
let(:monday_morning) { Time.utc(2014, 4, 7, 10) }
|
286
|
+
let(:monday_afternoon) { Time.utc(2014, 4, 7, 13) }
|
287
|
+
let(:monday_break) { Time.utc(2014, 4, 7, 12) }
|
288
|
+
let(:tuesday_morning) { Time.utc(2014, 4, 8, 9) }
|
289
|
+
|
290
|
+
it 'moves from morning to afternoon slot' do
|
291
|
+
expect(next_working_time(monday_morning)).to eq(monday_afternoon)
|
292
|
+
end
|
293
|
+
|
294
|
+
it 'moves from break time to afternoon slot' do
|
295
|
+
expect(next_working_time(monday_break)).to eq(monday_afternoon)
|
296
|
+
end
|
297
|
+
|
298
|
+
it 'moves from afternoon slot to next day' do
|
299
|
+
expect(next_working_time(monday_afternoon)).to eq(tuesday_morning)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'works with any input timezone (converts to config)' do
|
304
|
+
# Monday 0 am (-09:00) is 9am in UTC time, working time!
|
305
|
+
monday_morning = Time.new(2014, 4, 7, 0, 0, 0 , "-09:00")
|
306
|
+
monday_night = Time.new(2014, 4, 7, 22, 0, 0, "+02:00")
|
307
|
+
tuesday_morning = Time.utc(2014, 4, 8, 9)
|
308
|
+
expect(next_working_time(monday_morning)).to eq(tuesday_morning)
|
309
|
+
expect(next_working_time(monday_night)).to eq(tuesday_morning)
|
310
|
+
end
|
311
|
+
|
312
|
+
it 'returns time in config zone' do
|
313
|
+
WorkingHours::Config.time_zone = 'Tokyo'
|
314
|
+
expect(next_working_time(Time.new(2014, 4, 7, 0, 0, 0)).zone).to eq('JST')
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
151
318
|
describe '#return_to_working_time' do
|
152
319
|
it 'jumps non-working day' do
|
153
320
|
WorkingHours::Config.holidays = [Date.new(2014, 5, 1)]
|
@@ -62,6 +62,10 @@ describe WorkingHours::Duration do
|
|
62
62
|
WorkingHours::Config.time_zone = 'Tokyo'
|
63
63
|
expect(7.working.days.from_now.zone).to eq('JST')
|
64
64
|
end
|
65
|
+
|
66
|
+
it 'should not hang with fractional hours' do
|
67
|
+
WorkingHours::Duration.new(4.1, :hours).since(Time.utc(1991, 11, 15, 21))
|
68
|
+
end
|
65
69
|
end
|
66
70
|
|
67
71
|
describe '#until' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: working_hours
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adrien Jarthon
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-12-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -154,7 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
154
154
|
version: '0'
|
155
155
|
requirements: []
|
156
156
|
rubyforge_project:
|
157
|
-
rubygems_version: 2.
|
157
|
+
rubygems_version: 2.4.5.1
|
158
158
|
signing_key:
|
159
159
|
specification_version: 4
|
160
160
|
summary: time calculation with working hours
|
@@ -167,3 +167,4 @@ test_files:
|
|
167
167
|
- spec/working_hours/duration_proxy_spec.rb
|
168
168
|
- spec/working_hours/duration_spec.rb
|
169
169
|
- spec/working_hours_spec.rb
|
170
|
+
has_rdoc:
|