working_hours 1.1.2 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 67a0f2eb365a59918de39db2f11c29793a458f07
4
- data.tar.gz: 003cae888ac4274d8fa53fffee04c8bddb83b3b8
2
+ SHA256:
3
+ metadata.gz: 26d036d577be17378683ab793c1b7331a0f6609fc3c6faf11f46681dd9a3a88b
4
+ data.tar.gz: f037fc32ac6249d4d506a31a75bb69b7fbd5de35022ac39b332a521bae6d9e88
5
5
  SHA512:
6
- metadata.gz: 1917dd8e540d5b79d19f85c4c29ee10951f42f37a86451eacbaa9e03522db3f76b93051358d87404bb74df3ead6c2940c029f7b80e713af861a3517e562f692a
7
- data.tar.gz: 78aaf458c8bf63b49f9a94cf9134a9f6f95ae8293075d799acca986ef5b6da4417fae2315f1450d404884384864f67fc8ae51482d5f75fc02727df76a43d5dbf
6
+ metadata.gz: 1fa880f28c9d500c1964942ef13c70e5695d0bb5368a24e2bf711ff735de2d96790986f9cc5656e98fe29c623ae560a1bd4930e48f0275e7aa49e00eb6f67f0d
7
+ data.tar.gz: 4c719df9ec920d581fa304c4f9e55d6a97a2484a4f456b62ee5ed89a734f23f7a8c990e57cfce1c3484cd14878c120704b512bd9455aea348cb6933ce127cbba
data/.gitignore CHANGED
@@ -6,6 +6,7 @@
6
6
  .ruby-gemset
7
7
  .ruby-version
8
8
  Gemfile.lock
9
+ gemfiles/*.lock
9
10
  InstalledFiles
10
11
  _yardoc
11
12
  coverage
data/.travis.yml CHANGED
@@ -1,20 +1,27 @@
1
1
  language: ruby
2
+ dist: bionic
2
3
  rvm:
3
- - 2.0.0
4
- - 2.1.6
5
- - 2.2.3
6
- - ruby-head
7
- - jruby-1.7.19
8
- - jruby-head
9
- env: JRUBY_OPTS=--2.0
4
+ - 2.4.10
5
+ - 2.5.8
6
+ - 2.6.6
7
+ - 2.7.2
8
+ - 3.0.0
9
+ - jruby-9.2.14.0
10
10
  gemfile:
11
- - gemfiles/Gemfile.activesupport-3.2.x
12
- - gemfiles/Gemfile.activesupport-4.0.x
13
- - gemfiles/Gemfile.activesupport-4.1.x
14
- - gemfiles/Gemfile.activesupport-4.2.x
15
- - gemfiles/Gemfile.activesupport-edge
16
- matrix:
11
+ - gemfiles/Gemfile.activesupport-4.x
12
+ - gemfiles/Gemfile.activesupport-5.x
13
+ - gemfiles/Gemfile.activesupport-6.x
14
+ jobs:
15
+ exclude:
16
+ - rvm: 2.4.10
17
+ gemfile: gemfiles/Gemfile.activesupport-6.x
18
+ - rvm: 2.7.2
19
+ gemfile: gemfiles/Gemfile.activesupport-4.x
20
+ - rvm: 3.0.0
21
+ gemfile: gemfiles/Gemfile.activesupport-4.x
22
+ include:
23
+ - rvm: ruby-head
24
+ gemfile: gemfiles/Gemfile.activesupport-edge
17
25
  allow_failures:
18
26
  - gemfile: gemfiles/Gemfile.activesupport-edge
19
- - rvm: ruby-head
20
- - rvm: jruby-head
27
+ fast_finish: true
data/CHANGELOG.md CHANGED
@@ -1,6 +1,32 @@
1
1
  # Unreleased
2
2
 
3
- [Compare master with v1.1.2](https://github.com/intrepidd/working_hours/compare/v1.1.2...master)
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)
9
+
10
+ # v1.3.0
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)
12
+ * Increase code safety by always initializing an empty hash for each day of the week in the precompiled config (inspired by [#35](https://github.com/Intrepidd/working_hours/pull/35)
13
+
14
+ # v1.2.0
15
+ * Drop support for ruby 2.0, 2.1, 2.2 and 2.3
16
+ * Drop support for jruby 1.7 and 9.0
17
+ * Drop support for ActiveSupport 3.x
18
+ * Add support for jruby 9.2
19
+ * Add support for ruby 2.5, 2.6 and 2.7
20
+ * Add support for ActiveSupport 5.x and 6.x
21
+ * Fix day computations when origin is a holiday or a non worked day - [#39](https://github.com/Intrepidd/working_hours/pull/39)
22
+
23
+
24
+ # v1.1.4
25
+ * Fix thread safety - [#36](https://github.com/Intrepidd/working_hours/pull/36)
26
+
27
+ # v1.1.3
28
+ * Fixed warnings with Ruby 2.4.0+ - [#32](https://github.com/Intrepidd/working_hours/pull/32)
29
+ * Fix install bug with jruby 1.7.20
4
30
 
5
31
  # v1.1.2
6
32
  * Fixed an issue of float imprecision causing infinite loop - [#27](https://github.com/Intrepidd/working_hours/pull/27)
data/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # WorkingHours
2
2
 
3
- [![Build Status](https://travis-ci.org/Intrepidd/working_hours.svg?branch=master)](https://travis-ci.org/Intrepidd/working_hours)
3
+ [![Build Status](https://travis-ci.com/Intrepidd/working_hours.svg?branch=master)](https://travis-ci.com/Intrepidd/working_hours)
4
4
 
5
5
  A modern ruby gem allowing to do time calculation with working hours.
6
6
 
7
7
  Compatible and tested with:
8
- - Ruby `2.0`, `2.1`, JRuby `1.7` ( with ruby 2 syntax support enabled )
9
- - ActiveSupport `3.2.x`, `4.0.x` and `4.1.x`
8
+ - Ruby `2.4`, `2.5`, `2.6`, `2.7`, `3.0`, JRuby `9.2`
9
+ - ActiveSupport `4.x`, `5.x`, `6.x`
10
10
 
11
11
  ## Installation
12
12
 
@@ -2,4 +2,4 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec :path => '..'
4
4
 
5
- gem 'activesupport', '~> 3.2'
5
+ gem 'activesupport', '~> 5.2'
@@ -2,4 +2,4 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec :path => '..'
4
4
 
5
- gem 'activesupport', '~> 4.0'
5
+ gem 'activesupport', '~> 6.1'
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec :path => '..'
4
+
5
+ gem 'activesupport', github: 'rails/rails', branch: 'main'
data/lib/working_hours.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  require "working_hours/module"
2
- require "working_hours/core_ext/fixnum"
3
- require "working_hours/core_ext/date_and_time"
2
+ require "working_hours/core_ext/integer"
3
+ require "working_hours/core_ext/date_and_time"
@@ -5,8 +5,12 @@ module WorkingHours
5
5
  module Computation
6
6
 
7
7
  def add_days origin, days, config: nil
8
+ return origin if days.zero?
9
+
8
10
  config ||= wh_config
9
11
  time = in_config_zone(origin, config: config)
12
+ time += (days <=> 0).day until working_day?(time, config: config)
13
+
10
14
  while days > 0
11
15
  time += 1.day
12
16
  days -= 1 if working_day?(time, config: config)
@@ -30,7 +34,7 @@ module WorkingHours
30
34
 
31
35
  def add_seconds origin, seconds, config: nil
32
36
  config ||= wh_config
33
- time = in_config_zone(origin, config: config).round
37
+ time = in_config_zone(origin, config: config)
34
38
  while seconds > 0
35
39
  # roll to next business period
36
40
  time = advance_to_working_time(time, config: config)
@@ -68,7 +72,7 @@ module WorkingHours
68
72
 
69
73
  def advance_to_working_time time, config: nil
70
74
  config ||= wh_config
71
- time = in_config_zone(time, config: config).round
75
+ time = in_config_zone(time, config: config)
72
76
  loop do
73
77
  # skip holidays and weekends
74
78
  while not working_day?(time, config: config)
@@ -76,9 +80,9 @@ module WorkingHours
76
80
  end
77
81
  # find first working range after time
78
82
  time_in_day = time.seconds_since_midnight
79
- (config[:working_hours][time.wday] || {}).each do |from, to|
83
+ config[:working_hours][time.wday].each do |from, to|
80
84
  return time if time_in_day >= from and time_in_day < to
81
- return time + (from - time_in_day) if from >= time_in_day
85
+ return time.beginning_of_day + from if from >= time_in_day
82
86
  end
83
87
  # if none is found, go to next day and loop
84
88
  time = (time + 1.day).beginning_of_day
@@ -87,7 +91,7 @@ module WorkingHours
87
91
 
88
92
  def advance_to_closing_time time, config: nil
89
93
  config ||= wh_config
90
- time = in_config_zone(time, config: config).round
94
+ time = in_config_zone(time, config: config)
91
95
  loop do
92
96
  # skip holidays and weekends
93
97
  while not working_day?(time, config: config)
@@ -96,9 +100,8 @@ module WorkingHours
96
100
  # find next working range after time
97
101
  time_in_day = time.seconds_since_midnight
98
102
  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
103
+ config[:working_hours][time.wday].each do |from, to|
104
+ return time + to if time_in_day < to
102
105
  end
103
106
  # if none is found, go to next day and loop
104
107
  time = time + 1.day
@@ -119,7 +122,7 @@ module WorkingHours
119
122
 
120
123
  def return_to_exact_working_time time, config: nil
121
124
  config ||= wh_config
122
- time = in_config_zone(time, config: config).round
125
+ time = in_config_zone(time, config: config)
123
126
  loop do
124
127
  # skip holidays and weekends
125
128
  while not working_day?(time, config: config)
@@ -127,7 +130,7 @@ module WorkingHours
127
130
  end
128
131
  # find last working range before time
129
132
  time_in_day = time.seconds_since_midnight
130
- (config[:working_hours][time.wday] || {}).reverse_each do |from, to|
133
+ config[:working_hours][time.wday].reverse_each do |from, to|
131
134
  # round is used to suppress miliseconds hack from `end_of_day`
132
135
  return time if time_in_day > from and time_in_day <= to
133
136
  return (time - (time_in_day - to)) if to <= time_in_day
@@ -176,23 +179,28 @@ module WorkingHours
176
179
  -working_time_between(to, from, config: config)
177
180
  else
178
181
  from = advance_to_working_time(in_config_zone(from, config: config))
179
- to = in_config_zone(to, config: config).round
182
+ to = in_config_zone(to, config: config)
180
183
  distance = 0
181
184
  while from < to
185
+ from_was = from
182
186
  # look at working ranges
183
187
  time_in_day = from.seconds_since_midnight
184
188
  config[:working_hours][from.wday].each do |begins, ends|
185
189
  if time_in_day >= begins and time_in_day < ends
186
- # take all we can
187
- take = [ends - time_in_day, to - from].min
188
- # advance time
189
- from += take
190
- # increase counter
191
- distance += take
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
192
199
  end
193
200
  end
194
201
  # roll to next business period
195
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
196
204
  end
197
205
  distance.round # round up to supress miliseconds introduced by 24:00 hack
198
206
  end
@@ -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
 
@@ -44,9 +45,8 @@ module WorkingHours
44
45
  validate_working_hours! config[:working_hours]
45
46
  validate_holidays! config[:holidays]
46
47
  validate_time_zone! config[:time_zone]
47
- compiled = {working_hours: []}
48
+ compiled = { working_hours: Array.new(7) { Hash.new } }
48
49
  working_hours.each do |day, hours|
49
- compiled[:working_hours][DAYS_OF_WEEK.index(day)] = {}
50
50
  hours.each do |start, finish|
51
51
  compiled[:working_hours][DAYS_OF_WEEK.index(day)][compile_time(start)] = compile_time(finish)
52
52
  end
@@ -89,7 +89,7 @@ module WorkingHours
89
89
  private
90
90
 
91
91
  def config
92
- Thread.current[:working_hours] ||= global_config
92
+ Thread.current[:working_hours] ||= global_config.dup
93
93
  end
94
94
 
95
95
  def global_config
@@ -116,7 +116,7 @@ module WorkingHours
116
116
  sec = time[TIME_FORMAT,3].to_i
117
117
  time = hour * 3600 + min * 60 + sec
118
118
  # Converts 24:00 to 23:59:59.999999
119
- return 86399.999999 if time == 86400
119
+ return MIDNIGHT if time == 86400
120
120
  time
121
121
  end
122
122
 
@@ -2,7 +2,7 @@ require "working_hours/duration_proxy"
2
2
 
3
3
  module WorkingHours
4
4
  module CoreExt
5
- module Fixnum
5
+ module Integer
6
6
 
7
7
  def working
8
8
  WorkingHours::DurationProxy.new(self)
@@ -12,4 +12,4 @@ module WorkingHours
12
12
  end
13
13
  end
14
14
 
15
- Fixnum.send(:include, WorkingHours::CoreExt::Fixnum)
15
+ Integer.send(:include, WorkingHours::CoreExt::Integer)
@@ -1,3 +1,3 @@
1
1
  module WorkingHours
2
- VERSION = "1.1.2"
2
+ VERSION = "1.3.1"
3
3
  end
@@ -32,12 +32,26 @@ describe WorkingHours::Computation do
32
32
  expect(add_days(time, 1)).to eq(Date.new(2014, 4, 9)) # Wednesday
33
33
  end
34
34
 
35
+ it 'skips non worked days when origin is not worked' do
36
+ time = Date.new(2014, 4, 8) # Tuesday
37
+ WorkingHours::Config.working_hours = {mon: {'09:00' => '17:00'}, wed: {'09:00' => '17:00'}, thu: {'09:00' => '17:00'}, sun: {'09:00' => '17:00'}}
38
+ expect(add_days(time, 1)).to eq(Date.new(2014, 4, 10)) # Thursday
39
+ expect(add_days(time, -1)).to eq(Date.new(2014, 4, 6)) # Sunday
40
+ end
41
+
35
42
  it 'skips holidays' do
36
43
  time = Date.new(2014, 4, 7) # Monday
37
44
  WorkingHours::Config.holidays = [Date.new(2014, 4, 8)] # Tuesday
38
45
  expect(add_days(time, 1)).to eq(Date.new(2014, 4, 9)) # Wednesday
39
46
  end
40
47
 
48
+ it 'skips holidays when origin is holiday' do
49
+ time = Date.new(2014, 4, 9) # Wednesday
50
+ WorkingHours::Config.holidays = [time] # Wednesday
51
+ expect(add_days(time, 1)).to eq(Date.new(2014, 4, 11)) # Friday
52
+ expect(add_days(time, -1)).to eq(Date.new(2014, 4, 7)) # Monday
53
+ end
54
+
41
55
  it 'skips holidays and non worked days' do
42
56
  time = Date.new(2014, 4, 7) # Monday
43
57
  WorkingHours::Config.holidays = [Date.new(2014, 4, 9)] # Wednesday
@@ -45,6 +59,12 @@ describe WorkingHours::Computation do
45
59
  expect(add_days(time, 3)).to eq(Date.new(2014, 4, 21))
46
60
  end
47
61
 
62
+ it 'returns the original value when adding 0 days' do
63
+ time = Date.new(2014, 4, 7)
64
+ WorkingHours::Config.holidays = [time]
65
+ expect(add_days(time, 0)).to eq(time)
66
+ end
67
+
48
68
  it 'accepts time given from any time zone' do
49
69
  time = Time.utc(1991, 11, 14, 21, 0, 0) # Thursday 21 pm UTC
50
70
  WorkingHours::Config.time_zone = 'Tokyo' # But we are at tokyo, so it's already Friday 6 am
@@ -109,6 +129,13 @@ describe WorkingHours::Computation do
109
129
  time = Time.utc(2014, 4, 8, 0, 0, 30) # Tuesday
110
130
  expect(add_seconds(time, -60)).to eq(Time.utc(2014, 4, 7, 23, 59, 00))
111
131
  end
132
+
133
+ it 'honors miliseconds in the base time and increment (but return rounded result)' do
134
+ # Rounding the base time or increments before the end would yield a wrong result
135
+ time = Time.utc(1991, 11, 15, 16, 59, 42.25) # +250ms
136
+ expect(add_seconds(time, 120.4)).to eq(Time.utc(1991, 11, 18, 9, 1, 43))
137
+ end
138
+
112
139
  end
113
140
 
114
141
  describe '#advance_to_working_time' do
@@ -146,6 +173,10 @@ describe WorkingHours::Computation do
146
173
  WorkingHours::Config.time_zone = 'Tokyo'
147
174
  expect(advance_to_working_time(Time.new(2014, 4, 7, 0, 0, 0)).zone).to eq('JST')
148
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
149
180
  end
150
181
 
151
182
  describe '#advance_to_closing_time' do
@@ -213,7 +244,7 @@ describe WorkingHours::Computation do
213
244
  end
214
245
 
215
246
  let(:monday_morning) { Time.utc(2014, 4, 7, 0) }
216
- let(:monday_closing) { Time.utc(2014, 4, 7) + 86399.999999 }
247
+ let(:monday_closing) { Time.utc(2014, 4, 7) + WorkingHours::Config::MIDNIGHT }
217
248
  let(:tuesday_closing) { Time.utc(2014, 4, 8, 17) }
218
249
  let(:sunday) { Time.utc(2014, 4, 6, 17) }
219
250
 
@@ -228,6 +259,11 @@ describe WorkingHours::Computation do
228
259
  it 'moves over midnight' do
229
260
  expect(advance_to_closing_time(sunday)).to eq(monday_closing)
230
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
231
267
  end
232
268
 
233
269
  it 'works with any input timezone (converts to config)' do
@@ -244,6 +280,10 @@ describe WorkingHours::Computation do
244
280
  WorkingHours::Config.time_zone = 'Tokyo'
245
281
  expect(advance_to_closing_time(Time.new(2014, 4, 7, 0, 0, 0)).zone).to eq('JST')
246
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
247
287
  end
248
288
 
249
289
  describe '#next_working_time' do
@@ -516,5 +556,41 @@ describe WorkingHours::Computation do
516
556
  Time.new(2014, 4, 7, 15, 0, 0, "-04:00"), # Monday 7pm in UTC
517
557
  )).to eq(7.hours)
518
558
  end
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
+
581
+ # generates two times with +0ms, +250ms, +500ms, +750ms and +1s
582
+ # then for each combination compare the result with a ruby diff
583
+ context 'with precise miliseconds timings' do
584
+ reference = Time.utc(2014, 4, 7, 10)
585
+ 0.step(1.0, 0.25) do |offset1|
586
+ 0.step(1.0, 0.25) do |offset2|
587
+ from = reference + offset1
588
+ to = reference + offset2
589
+ it "returns expected value (#{(to - from).round}) for #{offset1} — #{offset2} interval" do
590
+ expect(working_time_between(from, to)).to eq((to - from).round)
591
+ end
592
+ end
593
+ end
594
+ end
519
595
  end
520
596
  end
@@ -5,25 +5,58 @@ describe WorkingHours::Config do
5
5
  describe '.working_hours' do
6
6
 
7
7
  let(:config) { WorkingHours::Config.working_hours }
8
+ let(:config2) { { :mon => { '08:00' => '14:00' } } }
9
+ let(:config3) { { :tue => { '10:00' => '16:00' } } }
8
10
 
9
11
  it 'has a default config' do
10
12
  expect(config).to be_kind_of(Hash)
11
13
  end
12
14
 
13
15
  it 'is thread safe' do
16
+ expect(WorkingHours::Config.working_hours).to eq(config)
17
+
18
+ thread = Thread.new do
19
+ WorkingHours::Config.working_hours = config2
20
+ expect(WorkingHours::Config.working_hours).to eq(config2)
21
+ Thread.stop
22
+ expect(WorkingHours::Config.working_hours).to eq(config2)
23
+ end
24
+
25
+ expect {
26
+ sleep 0.1 # let the thread begin its execution
27
+ }.not_to change { WorkingHours::Config.working_hours }.from(config)
28
+
14
29
  expect {
15
- Thread.new {
16
- WorkingHours::Config.working_hours = {:mon => {'08:00' => '14:00'}}
17
- }.join
18
- }.not_to change { WorkingHours::Config.working_hours }
30
+ WorkingHours::Config.working_hours = config3
31
+ }.to change { WorkingHours::Config.working_hours }.from(config).to(config3)
32
+
33
+ expect {
34
+ thread.run
35
+ thread.join
36
+ }.not_to change { WorkingHours::Config.working_hours }.from(config3)
19
37
  end
20
38
 
21
39
  it 'is fiber safe' do
40
+ expect(WorkingHours::Config.working_hours).to eq(config)
41
+
42
+ fiber = Fiber.new do
43
+ WorkingHours::Config.working_hours = config2
44
+ expect(WorkingHours::Config.working_hours).to eq(config2)
45
+ Fiber.yield
46
+ expect(WorkingHours::Config.working_hours).to eq(config2)
47
+ end
48
+
49
+ expect {
50
+ fiber.resume
51
+ }.not_to change { WorkingHours::Config.working_hours }.from(config)
52
+
53
+ expect {
54
+ WorkingHours::Config.working_hours = config3
55
+ }.to change { WorkingHours::Config.working_hours }.from(config).to(config3)
56
+
22
57
  expect {
23
- Fiber.new {
24
- WorkingHours::Config.working_hours = {:mon => {'08:00' => '14:00'}}
25
- }.resume
26
- }.not_to change { WorkingHours::Config.working_hours }
58
+ fiber.resume
59
+ }.not_to change { WorkingHours::Config.working_hours }.from(config3)
27
60
  end
28
61
 
29
62
  it 'is initialized from last known global config' do
@@ -233,16 +266,23 @@ describe WorkingHours::Config do
233
266
 
234
267
  it 'computes an optimized version' do
235
268
  expect(subject).to eq({
236
- :working_hours => [nil, {32400=>61200}, {32400=>61200}, {32400=>61200}, {32400=>61200}, {32400=>61200}],
269
+ :working_hours => [{}, {32400=>61200}, {32400=>61200}, {32400=>61200}, {32400=>61200}, {32400=>61200}, {}],
237
270
  :holidays => Set.new([]),
238
271
  :time_zone => ActiveSupport::TimeZone['UTC']
239
272
  })
240
273
  end
241
274
 
275
+ it 'includes default values for each days so computation does not fail' do
276
+ WorkingHours::Config.working_hours = {:mon => {'08:00' => '14:00'}}
277
+ expect(subject[:working_hours]).to eq([{}, {28800=>50400}, {}, {}, {}, {}, {}])
278
+ expect(WorkingHours.working_time_between(Time.utc(2014, 4, 14, 0), Time.utc(2014, 4, 21, 0))).to eq(3600*6)
279
+ expect(WorkingHours.add_seconds(Time.utc(2014, 4, 14, 0), 3600*7)).to eq(Time.utc(2014, 4, 21, 9))
280
+ end
281
+
242
282
  it 'supports seconds' do
243
283
  WorkingHours::Config.working_hours = {:mon => {'20:32:59' => '22:59:59'}}
244
284
  expect(subject).to eq({
245
- :working_hours => [nil, {73979 => 82799}],
285
+ :working_hours => [{}, {73979 => 82799}, {}, {}, {}, {}, {}],
246
286
  :holidays => Set.new([]),
247
287
  :time_zone => ActiveSupport::TimeZone['UTC']
248
288
  })
@@ -251,7 +291,7 @@ describe WorkingHours::Config do
251
291
  it 'supports 24:00 (converts to 23:59:59.999999)' do
252
292
  WorkingHours::Config.working_hours = {:mon => {'20:00' => '24:00'}}
253
293
  expect(subject).to eq({
254
- :working_hours => [nil, {72000 => 86399.999999}],
294
+ :working_hours => [{}, {72000 => 86399.999999}, {}, {}, {}, {}, {}],
255
295
  :holidays => Set.new([]),
256
296
  :time_zone => ActiveSupport::TimeZone['UTC']
257
297
  })
@@ -263,9 +303,9 @@ describe WorkingHours::Config do
263
303
  }.to change {
264
304
  WorkingHours::Config.precompiled[:working_hours]
265
305
  }.from(
266
- [nil, {32400=>61200}, {32400=>61200}, {32400=>61200}, {32400=>61200}, {32400=>61200}]
306
+ [{}, {32400=>61200}, {32400=>61200}, {32400=>61200}, {32400=>61200}, {32400=>61200}, {}]
267
307
  ).to(
268
- [nil, {28800=>50400}]
308
+ [{}, {28800=>50400}, {}, {}, {}, {}, {}]
269
309
  )
270
310
  end
271
311
 
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe WorkingHours::CoreExt::Fixnum do
3
+ describe WorkingHours::CoreExt::Integer do
4
4
 
5
5
  describe '#working' do
6
6
  it 'returns a DurationProxy' do
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
  spec.add_dependency 'activesupport', '>= 3.2'
22
22
  spec.add_dependency 'tzinfo'
23
23
 
24
- spec.add_development_dependency 'bundler', '~> 1.5'
24
+ spec.add_development_dependency 'bundler', '>= 1.5'
25
25
  spec.add_development_dependency 'rake'
26
26
  spec.add_development_dependency 'rspec', '~> 3.2'
27
27
  spec.add_development_dependency 'timecop'
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.2
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: 2015-12-06 00:00:00.000000000 Z
12
+ date: 2021-02-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -43,14 +43,14 @@ dependencies:
43
43
  name: bundler
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
- - - "~>"
46
+ - - ">="
47
47
  - !ruby/object:Gem::Version
48
48
  version: '1.5'
49
49
  type: :development
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
- - - "~>"
53
+ - - ">="
54
54
  - !ruby/object:Gem::Version
55
55
  version: '1.5'
56
56
  - !ruby/object:Gem::Dependency
@@ -111,16 +111,15 @@ files:
111
111
  - LICENSE.txt
112
112
  - README.md
113
113
  - Rakefile
114
- - gemfiles/Gemfile.activesupport-3.2.x
115
- - gemfiles/Gemfile.activesupport-4.0.x
116
- - gemfiles/Gemfile.activesupport-4.1.x
117
- - gemfiles/Gemfile.activesupport-4.2.x
118
- - gemfiles/Gemfile.activesupport-head
114
+ - gemfiles/Gemfile.activesupport-4.x
115
+ - gemfiles/Gemfile.activesupport-5.x
116
+ - gemfiles/Gemfile.activesupport-6.x
117
+ - gemfiles/Gemfile.activesupport-edge
119
118
  - lib/working_hours.rb
120
119
  - lib/working_hours/computation.rb
121
120
  - lib/working_hours/config.rb
122
121
  - lib/working_hours/core_ext/date_and_time.rb
123
- - lib/working_hours/core_ext/fixnum.rb
122
+ - lib/working_hours/core_ext/integer.rb
124
123
  - lib/working_hours/duration.rb
125
124
  - lib/working_hours/duration_proxy.rb
126
125
  - lib/working_hours/module.rb
@@ -129,7 +128,7 @@ files:
129
128
  - spec/working_hours/computation_spec.rb
130
129
  - spec/working_hours/config_spec.rb
131
130
  - spec/working_hours/core_ext/date_and_time_spec.rb
132
- - spec/working_hours/core_ext/fixnum_spec.rb
131
+ - spec/working_hours/core_ext/integer_spec.rb
133
132
  - spec/working_hours/duration_proxy_spec.rb
134
133
  - spec/working_hours/duration_spec.rb
135
134
  - spec/working_hours_spec.rb
@@ -153,8 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
152
  - !ruby/object:Gem::Version
154
153
  version: '0'
155
154
  requirements: []
156
- rubyforge_project:
157
- rubygems_version: 2.4.5.1
155
+ rubygems_version: 3.1.2
158
156
  signing_key:
159
157
  specification_version: 4
160
158
  summary: time calculation with working hours
@@ -163,8 +161,7 @@ test_files:
163
161
  - spec/working_hours/computation_spec.rb
164
162
  - spec/working_hours/config_spec.rb
165
163
  - spec/working_hours/core_ext/date_and_time_spec.rb
166
- - spec/working_hours/core_ext/fixnum_spec.rb
164
+ - spec/working_hours/core_ext/integer_spec.rb
167
165
  - spec/working_hours/duration_proxy_spec.rb
168
166
  - spec/working_hours/duration_spec.rb
169
167
  - spec/working_hours_spec.rb
170
- has_rdoc:
@@ -1,5 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec :path => '..'
4
-
5
- gem 'activesupport', '~> 4.1'
@@ -1,5 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec :path => '..'
4
-
5
- gem 'activesupport', github: 'rails/rails'