working_hours 1.1.1 → 1.1.2

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
2
  SHA1:
3
- metadata.gz: fdc5713f20da1316a29bf18573095ac9e92ccb6c
4
- data.tar.gz: bf54ee89ba3f855fcdc0d791965cf23fd5c29d3c
3
+ metadata.gz: 67a0f2eb365a59918de39db2f11c29793a458f07
4
+ data.tar.gz: 003cae888ac4274d8fa53fffee04c8bddb83b3b8
5
5
  SHA512:
6
- metadata.gz: 99cd033ae02a2c9c25fbced88e1c265a891210c7a232e12bb32191fd3c9bcea652cf3f35bd6c50c3215277b190d3af56e73fbde20cff1bb6cdfb24e2a205e08f
7
- data.tar.gz: 50f2ca92979404a58af287abd33dd7af125ad9366a43160804bbbf09ce2b7648d474c5bf598e583fef4c7394e28965c31a91cc26a3ebba35d71e12cba0cdff95
6
+ metadata.gz: 1917dd8e540d5b79d19f85c4c29ee10951f42f37a86451eacbaa9e03522db3f76b93051358d87404bb74df3ead6c2940c029f7b80e713af861a3517e562f692a
7
+ data.tar.gz: 78aaf458c8bf63b49f9a94cf9134a9f6f95ae8293075d799acca986ef5b6da4417fae2315f1450d404884384864f67fc8ae51482d5f75fc02727df76a43d5dbf
data/.gitignore CHANGED
@@ -3,6 +3,8 @@
3
3
  .bundle
4
4
  .config
5
5
  .yardoc
6
+ .ruby-gemset
7
+ .ruby-version
6
8
  Gemfile.lock
7
9
  InstalledFiles
8
10
  _yardoc
data/.travis.yml CHANGED
@@ -2,7 +2,7 @@ language: ruby
2
2
  rvm:
3
3
  - 2.0.0
4
4
  - 2.1.6
5
- - 2.2.2
5
+ - 2.2.3
6
6
  - ruby-head
7
7
  - jruby-1.7.19
8
8
  - jruby-head
data/CHANGELOG.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # Unreleased
2
2
 
3
- [Compare master with v1.1.1](https://github.com/intrepidd/working_hours/compare/v1.1.1...master)
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.round
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.round
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
@@ -1,3 +1,3 @@
1
1
  module WorkingHours
2
- VERSION = "1.1.1"
2
+ VERSION = "1.1.2"
3
3
  end
@@ -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.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-08-18 00:00:00.000000000 Z
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.2.3
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: