working_hours 1.3.1 → 1.3.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/CHANGELOG.md +3 -0
- data/lib/working_hours/computation.rb +22 -7
- data/lib/working_hours/version.rb +1 -1
- data/spec/working_hours/computation_spec.rb +114 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 102b64fd640e32b4164e32d0323d3575bfd720064d02045ff65f7e320b7dd305
|
4
|
+
data.tar.gz: dea4493b517eda97438e35f3857da9b456ead0e992b6278b33d108f1c38ef7ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 88e9102b1a8111aab7b0c5017c93f2eda8c99821499dfe9aa91e89572925b090a95840059a91de0e1114340ae566973a4d67844ebabee4a8f93dd6f11ae94786
|
7
|
+
data.tar.gz: 5dcdc183274c968c8cf61880870642ec1045092f9f579fb0f14c9ad1e45ffd232e37aa544c0a1c2f4c58dca4a676d1c6856fdd7cf95171e8702201ec44b550bc
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
[Compare master with v1.3.1](https://github.com/intrepidd/working_hours/compare/v1.3.1...master)
|
4
4
|
|
5
|
+
# v1.3.2
|
6
|
+
* Improve support for time shifts - [#46](https://github.com/Intrepidd/working_hours/pull/46)
|
7
|
+
|
5
8
|
# v1.3.1
|
6
9
|
* Improve computation accuracy in `advance_to_working_time` and `working_time_between` by using more exact (integer-based) time operations instead of floating point numbers - [#44](https://github.com/Intrepidd/working_hours/pull/44)
|
7
10
|
* Raise an exception when we detect an infinite loops in `advance_to_working_time` to improve resilience and make debugging easier - [#44](https://github.com/Intrepidd/working_hours/pull/44)
|
@@ -82,7 +82,7 @@ module WorkingHours
|
|
82
82
|
time_in_day = time.seconds_since_midnight
|
83
83
|
config[:working_hours][time.wday].each do |from, to|
|
84
84
|
return time if time_in_day >= from and time_in_day < to
|
85
|
-
return time
|
85
|
+
return move_time_of_day(time, from) if from >= time_in_day
|
86
86
|
end
|
87
87
|
# if none is found, go to next day and loop
|
88
88
|
time = (time + 1.day).beginning_of_day
|
@@ -99,12 +99,11 @@ module WorkingHours
|
|
99
99
|
end
|
100
100
|
# find next working range after time
|
101
101
|
time_in_day = time.seconds_since_midnight
|
102
|
-
time = time.beginning_of_day
|
103
102
|
config[:working_hours][time.wday].each do |from, to|
|
104
|
-
return time
|
103
|
+
return move_time_of_day(time, to) if time_in_day < to
|
105
104
|
end
|
106
105
|
# if none is found, go to next day and loop
|
107
|
-
time = time + 1.day
|
106
|
+
time = (time + 1.day).beginning_of_day
|
108
107
|
end
|
109
108
|
end
|
110
109
|
|
@@ -131,9 +130,8 @@ module WorkingHours
|
|
131
130
|
# find last working range before time
|
132
131
|
time_in_day = time.seconds_since_midnight
|
133
132
|
config[:working_hours][time.wday].reverse_each do |from, to|
|
134
|
-
# round is used to suppress miliseconds hack from `end_of_day`
|
135
133
|
return time if time_in_day > from and time_in_day <= to
|
136
|
-
return (time
|
134
|
+
return move_time_of_day(time, to) if to <= time_in_day
|
137
135
|
end
|
138
136
|
# if none is found, go to previous day and loop
|
139
137
|
time = (time - 1.day).end_of_day
|
@@ -190,7 +188,7 @@ module WorkingHours
|
|
190
188
|
if (to - from) > (ends - time_in_day)
|
191
189
|
# take all the range and continue
|
192
190
|
distance += (ends - time_in_day)
|
193
|
-
from = from
|
191
|
+
from = move_time_of_day(from, ends)
|
194
192
|
else
|
195
193
|
# take only what's needed and stop
|
196
194
|
distance += (to - from)
|
@@ -208,6 +206,23 @@ module WorkingHours
|
|
208
206
|
|
209
207
|
private
|
210
208
|
|
209
|
+
# Changes the time of the day to match given time (in seconds since midnight)
|
210
|
+
# preserving nanosecond prevision (rational number) and honoring time shifts
|
211
|
+
#
|
212
|
+
# This replaces the previous implementation which was:
|
213
|
+
# time.beginning_of_day + seconds
|
214
|
+
# (because this one would shift hours during time shifts days)
|
215
|
+
def move_time_of_day time, seconds
|
216
|
+
# return time.beginning_of_day + seconds
|
217
|
+
hour = (seconds / 3600).to_i
|
218
|
+
seconds %= 3600
|
219
|
+
minutes = (seconds / 60).to_i
|
220
|
+
seconds %= 60
|
221
|
+
# sec/usec separation is required for ActiveSupport <= 5.1
|
222
|
+
usec = ((seconds % 1) * 10**6)
|
223
|
+
time.change(hour: hour, min: minutes, sec: seconds.to_i, usec: usec)
|
224
|
+
end
|
225
|
+
|
211
226
|
def wh_config
|
212
227
|
WorkingHours::Config.precompiled
|
213
228
|
end
|
@@ -177,6 +177,32 @@ describe WorkingHours::Computation do
|
|
177
177
|
it 'do not leak nanoseconds when advancing' do
|
178
178
|
expect(advance_to_working_time(Time.utc(2014, 4, 7, 5, 0, 0, 123456.789))).to eq(Time.utc(2014, 4, 7, 9, 0, 0, 0))
|
179
179
|
end
|
180
|
+
|
181
|
+
it 'returns correct hour during positive time shifts' do
|
182
|
+
WorkingHours::Config.working_hours = {sun: {'09:00' => '17:00'}}
|
183
|
+
WorkingHours::Config.time_zone = 'Paris'
|
184
|
+
from = Time.new(2020, 3, 29, 0, 0, 0, "+01:00")
|
185
|
+
expect(from.utc_offset).to eq(3600)
|
186
|
+
res = advance_to_working_time(from)
|
187
|
+
expect(res).to eq(Time.new(2020, 3, 29, 9, 0, 0, "+02:00"))
|
188
|
+
expect(res.utc_offset).to eq(7200)
|
189
|
+
# starting from wrong time-zone
|
190
|
+
expect(advance_to_working_time(Time.new(2020, 3, 29, 5, 0, 0, "+01:00"))).to eq(Time.new(2020, 3, 29, 9, 0, 0, "+02:00"))
|
191
|
+
expect(advance_to_working_time(Time.new(2020, 3, 29, 1, 0, 0, "+02:00"))).to eq(Time.new(2020, 3, 29, 9, 0, 0, "+02:00"))
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'returns correct hour during negative time shifts' do
|
195
|
+
WorkingHours::Config.working_hours = {sun: {'09:00' => '17:00'}}
|
196
|
+
WorkingHours::Config.time_zone = 'Paris'
|
197
|
+
from = Time.new(2020, 10, 25, 0, 0, 0, "+02:00")
|
198
|
+
expect(from.utc_offset).to eq(7200)
|
199
|
+
res = advance_to_working_time(from)
|
200
|
+
expect(res).to eq(Time.new(2020, 10, 25, 9, 0, 0, "+01:00"))
|
201
|
+
expect(res.utc_offset).to eq(3600)
|
202
|
+
# starting from wrong time-zone
|
203
|
+
expect(advance_to_working_time(Time.new(2020, 10, 25, 4, 0, 0, "+02:00"))).to eq(Time.new(2020, 10, 25, 9, 0, 0, "+01:00"))
|
204
|
+
expect(advance_to_working_time(Time.new(2020, 10, 25, 1, 0, 0, "+01:00"))).to eq(Time.new(2020, 10, 25, 9, 0, 0, "+01:00"))
|
205
|
+
end
|
180
206
|
end
|
181
207
|
|
182
208
|
describe '#advance_to_closing_time' do
|
@@ -284,6 +310,32 @@ describe WorkingHours::Computation do
|
|
284
310
|
it 'do not leak nanoseconds when advancing' do
|
285
311
|
expect(advance_to_closing_time(Time.utc(2014, 4, 7, 5, 0, 0, 123456.789))).to eq(Time.utc(2014, 4, 7, 17, 0, 0, 0))
|
286
312
|
end
|
313
|
+
|
314
|
+
it 'returns correct hour during positive time shifts' do
|
315
|
+
WorkingHours::Config.working_hours = {sun: {'09:00' => '17:00'}}
|
316
|
+
WorkingHours::Config.time_zone = 'Paris'
|
317
|
+
from = Time.new(2020, 3, 29, 0, 0, 0, "+01:00")
|
318
|
+
expect(from.utc_offset).to eq(3600)
|
319
|
+
res = advance_to_closing_time(from)
|
320
|
+
expect(res).to eq(Time.new(2020, 3, 29, 17, 0, 0, "+02:00"))
|
321
|
+
expect(res.utc_offset).to eq(7200)
|
322
|
+
# starting from wrong time-zone
|
323
|
+
expect(advance_to_closing_time(Time.new(2020, 3, 29, 5, 0, 0, "+01:00"))).to eq(Time.new(2020, 3, 29, 17, 0, 0, "+02:00"))
|
324
|
+
expect(advance_to_closing_time(Time.new(2020, 3, 29, 1, 0, 0, "+02:00"))).to eq(Time.new(2020, 3, 29, 17, 0, 0, "+02:00"))
|
325
|
+
end
|
326
|
+
|
327
|
+
it 'returns correct hour during negative time shifts' do
|
328
|
+
WorkingHours::Config.working_hours = {sun: {'09:00' => '17:00'}}
|
329
|
+
WorkingHours::Config.time_zone = 'Paris'
|
330
|
+
from = Time.new(2020, 10, 25, 0, 0, 0, "+02:00")
|
331
|
+
expect(from.utc_offset).to eq(7200)
|
332
|
+
res = advance_to_closing_time(from)
|
333
|
+
expect(res).to eq(Time.new(2020, 10, 25, 17, 0, 0, "+01:00"))
|
334
|
+
expect(res.utc_offset).to eq(3600)
|
335
|
+
# starting from wrong time-zone
|
336
|
+
expect(advance_to_closing_time(Time.new(2020, 10, 25, 4, 0, 0, "+02:00"))).to eq(Time.new(2020, 10, 25, 17, 0, 0, "+01:00"))
|
337
|
+
expect(advance_to_closing_time(Time.new(2020, 10, 25, 1, 0, 0, "+01:00"))).to eq(Time.new(2020, 10, 25, 17, 0, 0, "+01:00"))
|
338
|
+
end
|
287
339
|
end
|
288
340
|
|
289
341
|
describe '#next_working_time' do
|
@@ -390,6 +442,32 @@ describe WorkingHours::Computation do
|
|
390
442
|
WorkingHours::Config.time_zone = 'Tokyo'
|
391
443
|
expect(return_to_working_time(Time.new(2014, 4, 7, 1, 0, 0)).zone).to eq('JST')
|
392
444
|
end
|
445
|
+
|
446
|
+
it 'returns correct hour during positive time shifts' do
|
447
|
+
WorkingHours::Config.working_hours = {sun: {'00:00' => '01:00'}}
|
448
|
+
WorkingHours::Config.time_zone = 'Paris'
|
449
|
+
from = Time.new(2020, 3, 29, 9, 0, 0, "+02:00")
|
450
|
+
expect(from.utc_offset).to eq(7200)
|
451
|
+
res = return_to_working_time(from)
|
452
|
+
expect(res).to eq(Time.new(2020, 3, 29, 1, 0, 0, "+01:00"))
|
453
|
+
expect(res.utc_offset).to eq(3600)
|
454
|
+
# starting from wrong time-zone
|
455
|
+
expect(return_to_working_time(Time.new(2020, 3, 29, 2, 0, 0, "+02:00"))).to eq(Time.new(2020, 3, 29, 1, 0, 0, "+01:00"))
|
456
|
+
expect(return_to_working_time(Time.new(2020, 3, 29, 9, 0, 0, "+01:00"))).to eq(Time.new(2020, 3, 29, 1, 0, 0, "+01:00"))
|
457
|
+
end
|
458
|
+
|
459
|
+
it 'returns correct hour during negative time shifts' do
|
460
|
+
WorkingHours::Config.working_hours = {sun: {'00:00' => '01:00'}}
|
461
|
+
WorkingHours::Config.time_zone = 'Paris'
|
462
|
+
from = Time.new(2020, 10, 25, 9, 0, 0, "+01:00")
|
463
|
+
expect(from.utc_offset).to eq(3600)
|
464
|
+
res = return_to_working_time(from)
|
465
|
+
expect(res).to eq(Time.new(2020, 10, 25, 1, 0, 0, "+02:00"))
|
466
|
+
expect(res.utc_offset).to eq(7200)
|
467
|
+
# starting from wrong time-zone
|
468
|
+
expect(return_to_working_time(Time.new(2020, 10, 25, 9, 0, 0, "+02:00"))).to eq(Time.new(2020, 10, 25, 1, 0, 0, "+02:00"))
|
469
|
+
expect(return_to_working_time(Time.new(2020, 10, 25, 1, 0, 0, "+01:00"))).to eq(Time.new(2020, 10, 25, 1, 0, 0, "+02:00"))
|
470
|
+
end
|
393
471
|
end
|
394
472
|
|
395
473
|
describe '#working_day?' do
|
@@ -567,6 +645,42 @@ describe WorkingHours::Computation do
|
|
567
645
|
)).to eq(6.hours)
|
568
646
|
end
|
569
647
|
|
648
|
+
it 'works across positive time shifts' do
|
649
|
+
WorkingHours::Config.working_hours = {sun: {'08:00' => '21:00'}}
|
650
|
+
WorkingHours::Config.time_zone = 'Paris'
|
651
|
+
expect(working_time_between(
|
652
|
+
Time.utc(2020, 3, 29, 1, 0),
|
653
|
+
Time.utc(2020, 3, 30, 0, 0),
|
654
|
+
)).to eq(13.hours)
|
655
|
+
end
|
656
|
+
|
657
|
+
it 'works across negative time shifts' do
|
658
|
+
WorkingHours::Config.working_hours = {sun: {'08:00' => '21:00'}}
|
659
|
+
WorkingHours::Config.time_zone = 'Paris'
|
660
|
+
expect(working_time_between(
|
661
|
+
Time.utc(2019, 10, 27, 1, 0),
|
662
|
+
Time.utc(2019, 10, 28, 0, 0),
|
663
|
+
)).to eq(13.hours)
|
664
|
+
end
|
665
|
+
|
666
|
+
it 'works across time shifts + midnight' do
|
667
|
+
WorkingHours::Config.working_hours = {sun: {'00:00' => '24:00'}}
|
668
|
+
WorkingHours::Config.time_zone = 'Paris'
|
669
|
+
expect(working_time_between(
|
670
|
+
Time.utc(2020, 10, 24, 22, 0),
|
671
|
+
Time.utc(2020, 10, 25, 23, 0),
|
672
|
+
)).to eq(24.hours)
|
673
|
+
end
|
674
|
+
|
675
|
+
it 'works across multiple time shifts' do
|
676
|
+
WorkingHours::Config.working_hours = {sun: {'08:00' => '21:00'}}
|
677
|
+
WorkingHours::Config.time_zone = 'Paris'
|
678
|
+
expect(working_time_between(
|
679
|
+
Time.utc(2002, 10, 27, 6, 0),
|
680
|
+
Time.utc(2021, 10, 30, 0, 0),
|
681
|
+
)).to eq(12896.hours)
|
682
|
+
end
|
683
|
+
|
570
684
|
it 'do not cause infinite loop if the time is not advancing properly' do
|
571
685
|
# simulate some computation/precision error
|
572
686
|
expect(self).to receive(:advance_to_working_time).twice do |time|
|
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.3.
|
4
|
+
version: 1.3.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: 2021-02-
|
12
|
+
date: 2021-02-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|