working_hours 1.3.0 → 1.3.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 +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +7 -8
- data/CHANGELOG.md +6 -1
- data/gemfiles/Gemfile.activesupport-6.x +1 -1
- data/gemfiles/Gemfile.activesupport-edge +1 -1
- data/lib/working_hours/computation.rb +13 -9
- data/lib/working_hours/config.rb +2 -1
- data/lib/working_hours/version.rb +1 -1
- data/spec/working_hours/computation_spec.rb +35 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 26d036d577be17378683ab793c1b7331a0f6609fc3c6faf11f46681dd9a3a88b
|
4
|
+
data.tar.gz: f037fc32ac6249d4d506a31a75bb69b7fbd5de35022ac39b332a521bae6d9e88
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1fa880f28c9d500c1964942ef13c70e5695d0bb5368a24e2bf711ff735de2d96790986f9cc5656e98fe29c623ae560a1bd4930e48f0275e7aa49e00eb6f67f0d
|
7
|
+
data.tar.gz: 4c719df9ec920d581fa304c4f9e55d6a97a2484a4f456b62ee5ed89a734f23f7a8c990e57cfce1c3484cd14878c120704b512bd9455aea348cb6933ce127cbba
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
language: ruby
|
2
|
+
dist: bionic
|
2
3
|
rvm:
|
3
|
-
- 2.4.
|
4
|
-
- 2.5.
|
5
|
-
- 2.6.
|
6
|
-
- 2.7.
|
4
|
+
- 2.4.10
|
5
|
+
- 2.5.8
|
6
|
+
- 2.6.6
|
7
|
+
- 2.7.2
|
7
8
|
- 3.0.0
|
8
9
|
- jruby-9.2.14.0
|
9
10
|
gemfile:
|
@@ -12,17 +13,15 @@ gemfile:
|
|
12
13
|
- gemfiles/Gemfile.activesupport-6.x
|
13
14
|
jobs:
|
14
15
|
exclude:
|
15
|
-
- rvm: 2.4.
|
16
|
+
- rvm: 2.4.10
|
16
17
|
gemfile: gemfiles/Gemfile.activesupport-6.x
|
17
|
-
- rvm: 2.7.
|
18
|
+
- rvm: 2.7.2
|
18
19
|
gemfile: gemfiles/Gemfile.activesupport-4.x
|
19
20
|
- rvm: 3.0.0
|
20
21
|
gemfile: gemfiles/Gemfile.activesupport-4.x
|
21
22
|
include:
|
22
23
|
- rvm: ruby-head
|
23
24
|
gemfile: gemfiles/Gemfile.activesupport-edge
|
24
|
-
- rvm: jruby-head
|
25
|
-
gemfile: gemfiles/Gemfile.activesupport-edge
|
26
25
|
allow_failures:
|
27
26
|
- gemfile: gemfiles/Gemfile.activesupport-edge
|
28
27
|
fast_finish: true
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
-
[Compare master with v1.3.
|
3
|
+
[Compare master with v1.3.1](https://github.com/intrepidd/working_hours/compare/v1.3.1...master)
|
4
|
+
|
5
|
+
# v1.3.1
|
6
|
+
* 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
|
+
* 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)
|
8
|
+
* Use a Rational number for the midnight value to avoid leaking sub-nanoseconds residue because of floating point accuracy - [#44](https://github.com/Intrepidd/working_hours/pull/44)
|
4
9
|
|
5
10
|
# v1.3.0
|
6
11
|
* Improve supports for fractional seconds in input times by only rounding results at the end - [#42](https://github.com/Intrepidd/working_hours/issues/42) [#43](https://github.com/Intrepidd/working_hours/pull/43)
|
@@ -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 time.beginning_of_day + 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
|
@@ -101,8 +101,7 @@ module WorkingHours
|
|
101
101
|
time_in_day = time.seconds_since_midnight
|
102
102
|
time = time.beginning_of_day
|
103
103
|
config[:working_hours][time.wday].each do |from, to|
|
104
|
-
return time + to if time_in_day
|
105
|
-
return time + to if from >= time_in_day
|
104
|
+
return time + to if time_in_day < to
|
106
105
|
end
|
107
106
|
# if none is found, go to next day and loop
|
108
107
|
time = time + 1.day
|
@@ -183,20 +182,25 @@ module WorkingHours
|
|
183
182
|
to = in_config_zone(to, config: config)
|
184
183
|
distance = 0
|
185
184
|
while from < to
|
185
|
+
from_was = from
|
186
186
|
# look at working ranges
|
187
187
|
time_in_day = from.seconds_since_midnight
|
188
188
|
config[:working_hours][from.wday].each do |begins, ends|
|
189
189
|
if time_in_day >= begins and time_in_day < ends
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
190
|
+
if (to - from) > (ends - time_in_day)
|
191
|
+
# take all the range and continue
|
192
|
+
distance += (ends - time_in_day)
|
193
|
+
from = from.beginning_of_day + ends
|
194
|
+
else
|
195
|
+
# take only what's needed and stop
|
196
|
+
distance += (to - from)
|
197
|
+
from = to
|
198
|
+
end
|
196
199
|
end
|
197
200
|
end
|
198
201
|
# roll to next business period
|
199
202
|
from = advance_to_working_time(from, config: config)
|
203
|
+
raise "Invalid loop detected in working_time_between (from=#{from.iso8601(12)}, to=#{to.iso8601(12)}, distance=#{distance}, config=#{config}), please open an issue ;)" unless from > from_was
|
200
204
|
end
|
201
205
|
distance.round # round up to supress miliseconds introduced by 24:00 hack
|
202
206
|
end
|
data/lib/working_hours/config.rb
CHANGED
@@ -7,6 +7,7 @@ module WorkingHours
|
|
7
7
|
|
8
8
|
TIME_FORMAT = /\A([0-2][0-9])\:([0-5][0-9])(?:\:([0-5][0-9]))?\z/
|
9
9
|
DAYS_OF_WEEK = [:sun, :mon, :tue, :wed, :thu, :fri, :sat]
|
10
|
+
MIDNIGHT = Rational('86399.999999')
|
10
11
|
|
11
12
|
class << self
|
12
13
|
|
@@ -115,7 +116,7 @@ module WorkingHours
|
|
115
116
|
sec = time[TIME_FORMAT,3].to_i
|
116
117
|
time = hour * 3600 + min * 60 + sec
|
117
118
|
# Converts 24:00 to 23:59:59.999999
|
118
|
-
return
|
119
|
+
return MIDNIGHT if time == 86400
|
119
120
|
time
|
120
121
|
end
|
121
122
|
|
@@ -173,6 +173,10 @@ describe WorkingHours::Computation do
|
|
173
173
|
WorkingHours::Config.time_zone = 'Tokyo'
|
174
174
|
expect(advance_to_working_time(Time.new(2014, 4, 7, 0, 0, 0)).zone).to eq('JST')
|
175
175
|
end
|
176
|
+
|
177
|
+
it 'do not leak nanoseconds when advancing' do
|
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
|
+
end
|
176
180
|
end
|
177
181
|
|
178
182
|
describe '#advance_to_closing_time' do
|
@@ -240,7 +244,7 @@ describe WorkingHours::Computation do
|
|
240
244
|
end
|
241
245
|
|
242
246
|
let(:monday_morning) { Time.utc(2014, 4, 7, 0) }
|
243
|
-
let(:monday_closing) { Time.utc(2014, 4, 7) +
|
247
|
+
let(:monday_closing) { Time.utc(2014, 4, 7) + WorkingHours::Config::MIDNIGHT }
|
244
248
|
let(:tuesday_closing) { Time.utc(2014, 4, 8, 17) }
|
245
249
|
let(:sunday) { Time.utc(2014, 4, 6, 17) }
|
246
250
|
|
@@ -255,6 +259,11 @@ describe WorkingHours::Computation do
|
|
255
259
|
it 'moves over midnight' do
|
256
260
|
expect(advance_to_closing_time(sunday)).to eq(monday_closing)
|
257
261
|
end
|
262
|
+
|
263
|
+
it 'give precise computation with nothing other than miliseconds' do
|
264
|
+
pending "iso8601 is not precise enough on AS < 4" if ActiveSupport::VERSION::MAJOR <= 4
|
265
|
+
expect(advance_to_closing_time(monday_morning).iso8601(25)).to eq("2014-04-07T23:59:59.9999990000000000000000000Z")
|
266
|
+
end
|
258
267
|
end
|
259
268
|
|
260
269
|
it 'works with any input timezone (converts to config)' do
|
@@ -271,6 +280,10 @@ describe WorkingHours::Computation do
|
|
271
280
|
WorkingHours::Config.time_zone = 'Tokyo'
|
272
281
|
expect(advance_to_closing_time(Time.new(2014, 4, 7, 0, 0, 0)).zone).to eq('JST')
|
273
282
|
end
|
283
|
+
|
284
|
+
it 'do not leak nanoseconds when advancing' do
|
285
|
+
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
|
+
end
|
274
287
|
end
|
275
288
|
|
276
289
|
describe '#next_working_time' do
|
@@ -544,6 +557,27 @@ describe WorkingHours::Computation do
|
|
544
557
|
)).to eq(7.hours)
|
545
558
|
end
|
546
559
|
|
560
|
+
it 'uses precise computation to avoid useless loops' do
|
561
|
+
# +200 usec on each time, using floating point would cause
|
562
|
+
# precision issues and require several iterations
|
563
|
+
expect(self).to receive(:advance_to_working_time).twice.and_call_original
|
564
|
+
expect(working_time_between(
|
565
|
+
Time.utc(2014, 4, 7, 5, 0, 0, 200),
|
566
|
+
Time.utc(2014, 4, 7, 15, 0, 0, 200),
|
567
|
+
)).to eq(6.hours)
|
568
|
+
end
|
569
|
+
|
570
|
+
it 'do not cause infinite loop if the time is not advancing properly' do
|
571
|
+
# simulate some computation/precision error
|
572
|
+
expect(self).to receive(:advance_to_working_time).twice do |time|
|
573
|
+
time.change(hour: 9) - 0.0001
|
574
|
+
end
|
575
|
+
expect { working_time_between(
|
576
|
+
Time.utc(2014, 4, 7, 5, 0, 0),
|
577
|
+
Time.utc(2014, 4, 7, 15, 0, 0),
|
578
|
+
) }.to raise_error(RuntimeError, /Invalid loop detected in working_time_between \(from=2014-04-07T08:59:59.999/)
|
579
|
+
end
|
580
|
+
|
547
581
|
# generates two times with +0ms, +250ms, +500ms, +750ms and +1s
|
548
582
|
# then for each combination compare the result with a ruby diff
|
549
583
|
context 'with precise miliseconds timings' 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.3.
|
4
|
+
version: 1.3.1
|
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-
|
12
|
+
date: 2021-02-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -152,7 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
152
152
|
- !ruby/object:Gem::Version
|
153
153
|
version: '0'
|
154
154
|
requirements: []
|
155
|
-
rubygems_version: 3.
|
155
|
+
rubygems_version: 3.1.2
|
156
156
|
signing_key:
|
157
157
|
specification_version: 4
|
158
158
|
summary: time calculation with working hours
|