workpattern 0.4.0 → 0.5.0

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.
@@ -1,32 +1,27 @@
1
1
  module Workpattern
2
-
3
2
  # Mixins expected to be used in more than one class
4
3
  #
5
4
  # @since 0.2.0
6
5
  #
7
- module Utility
6
+ # @private
7
+ module Base
8
+ # Holds local timezone info
9
+ @@tz = nil
8
10
 
9
- # Returns the supplied <tt>DateTime</tt> at the very start of the day.
10
- #
11
- # @param [DateTime] adate is the <tt>DateTime</tt> to be changed
12
- # @return [DateTime]
13
- #
14
- # @todo Consider mixin for DateTime class
11
+ # Converts a date like object into utc
15
12
  #
16
- def midnight_before(adate)
17
- return adate -(HOUR * adate.hour) - (MINUTE * adate.min)
13
+ def to_utc(date)
14
+ date.to_time.utc
18
15
  end
19
-
20
- # Returns the supplied <tt>DateTime</tt> at the very start of the next day.
21
- #
22
- # @param [DateTime] adate is the <tt>DateTime</tt> to be changed
23
- # @return [DateTime]
24
- #
25
- # @todo Consider mixin for DateTime class
16
+ # Converts a date like object into local time
26
17
  #
27
- def midnight_after(adate)
28
- return midnight_before(adate.next_day)
18
+ def to_local(date)
19
+ date.to_time.getgm
20
+ end
21
+
22
+ # Retrieves the local timezone
23
+ def timezone
24
+ @@tz || @@tz = TZInfo::Timezone.get(Time.now.zone)
29
25
  end
30
-
31
26
  end
32
- end
27
+ end
@@ -1,3 +1,3 @@
1
1
  module Workpattern
2
- VERSION = '0.4.0'
3
- end
2
+ VERSION = '0.5.0'.freeze
3
+ end
@@ -1,65 +1,64 @@
1
1
  module Workpattern
2
-
2
+ # The representation of a week might not be obvious so I am writing about it
3
+ # here. It will also help me if I ever need to come back to this in the
4
+ # future.
5
+ #
6
+ # Each day is represented by a binary number where a 1 represents a working
7
+ # minute and a 0 represents a resting minute.
8
+ #
9
+ # @private
3
10
  class Week
4
-
5
11
  attr_accessor :values, :hours_per_day, :start, :finish, :week_total, :total
6
12
 
7
- def initialize(start,finish,type=1,hours_per_day=24)
13
+ def initialize(start, finish, type = 1, hours_per_day = 24)
8
14
  @hours_per_day = hours_per_day
9
- @start=DateTime.new(start.year,start.month,start.day)
10
- @finish=DateTime.new(finish.year,finish.month,finish.day)
15
+ @start = Time.gm(start.year, start.month, start.day)
16
+ @finish = Time.gm(finish.year, finish.month, finish.day)
11
17
  @values = Array.new(6)
12
- 0.upto(6) do |i|
18
+ 0.upto(6) do |i|
13
19
  @values[i] = working_day * type
14
20
  end
15
21
  end
16
22
 
17
- def <=>(other_week)
18
- if self.start < other_week.start
19
- return -1
20
- elsif self.start == other_week.start
21
- return 0
22
- else
23
- return 1
24
- end
23
+ def <=>(other)
24
+ return -1 if start < other.start
25
+ return 0 if start == other.start
26
+ 1
25
27
  end
26
-
28
+
27
29
  def week_total
28
- span_in_days > 6 ? full_week_total_minutes : part_week_total_minutes
29
- end
30
+ elapsed_days > 6 ? full_week_total_minutes : part_week_total_minutes
31
+ end
30
32
 
31
33
  def total
32
- total_days = span_in_days
33
- return week_total if total_days < 8
34
- sum = sum_of_minutes_in_day_range(self.start.wday, 6)
35
- total_days -= (7-self.start.wday)
36
- sum += sum_of_minutes_in_day_range(0,self.finish.wday)
37
- total_days-=(self.finish.wday+1)
38
- sum += week_total * total_days / 7
39
- return sum
34
+ elapsed_days < 8 ? week_total : range_total
40
35
  end
41
36
 
42
- def workpattern(days,from_time,to_time,type)
43
- DAYNAMES[days].each do |day|
44
- type==1 ? work_on_day(day,from_time,to_time) : rest_on_day(day,from_time,to_time)
37
+ def workpattern(days, from_time, to_time, type)
38
+ DAYNAMES[days].each do |day|
39
+ if type == 1
40
+ work_on_day(day, from_time, to_time)
41
+ else
42
+ rest_on_day(day, from_time, to_time)
43
+ end
45
44
  end
46
45
  end
47
46
 
48
- def duplicate()
49
- duplicate_week=Week.new(self.start,self.finish)
50
- 0.upto(6).each do |i| duplicate_week.values[i] = @values[i] end
51
- return duplicate_week
47
+ def duplicate
48
+ duplicate_week = Week.new(start, finish)
49
+ 0.upto(6).each { |i| duplicate_week.values[i] = @values[i] }
50
+ duplicate_week
52
51
  end
53
52
 
54
- def calc(start_date,duration, midnight=false)
55
- return start_date,duration,false if duration==0
56
- return add(start_date,duration) if duration > 0
57
- return subtract(self.start,duration, midnight) if (self.total==0) && (duration <0)
58
- return subtract(start_date,duration, midnight) if duration <0
53
+ def calc(start_date, duration, midnight = false)
54
+ return start_date, duration, false if duration == 0
55
+ return add(start_date, duration) if duration > 0
56
+ return subtract(start, duration, midnight) if total == 0 && duration < 0
57
+ subtract(start_date, duration, midnight)
59
58
  end
60
59
 
61
- def working?(date)
62
- return true if bit_pos_time(date) & @values[date.wday] > 0
60
+ def working?(time)
61
+ return true if bit_pos(time.hour, time.min) & @values[time.wday] > 0
63
62
  false
64
63
  end
65
64
 
@@ -67,43 +66,137 @@ module Workpattern
67
66
  !working?(date)
68
67
  end
69
68
 
70
- def diff(start_date,finish_date)
71
- start_date,finish_date=finish_date,start_date if ((start_date <=> finish_date))==1
72
-
73
- if (start_date.jd==finish_date.jd)
74
- duration, start_date=diff_in_same_day(start_date, finish_date)
75
- elsif (finish_date.jd<=self.finish.jd)
76
- duration, start_date=diff_in_same_weekpattern(start_date,finish_date)
77
- else
78
- duration, start_date=diff_beyond_weekpattern(start_date,finish_date)
79
- end
80
- return duration, start_date
69
+ def diff(start_d, finish_d)
70
+ start_d, finish_d = finish_d, start_d if ((start_d <=> finish_d)) == 1
81
71
 
72
+ return diff_in_same_day(start_d, finish_d) if jd(start_d) == jd(finish_d)
73
+ return diff_in_same_weekpattern(start_d, finish_d) if jd(finish_d) <= jd(finish)
74
+ diff_beyond_weekpattern(start_d, finish_d)
82
75
  end
83
76
 
84
- private
77
+ private
78
+
79
+ def working_minutes_in(day)
80
+ day.to_s(2).count('1')
81
+ end
85
82
 
86
- def span_in_days
87
- (self.finish-self.start).to_i + 1
83
+ def elapsed_days
84
+ (finish - start).to_i / 86_400 + 1
88
85
  end
89
86
 
90
87
  def full_week_total_minutes
91
- sum_of_minutes_in_day_range 0, 6
88
+ minutes_in_day_range 0, 6
92
89
  end
93
-
90
+
94
91
  def part_week_total_minutes
92
+ start.wday <= finish.wday ? no_rollover_minutes : rollover_minutes
93
+ end
95
94
 
96
- if self.start.wday <= self.finish.wday
97
- total = sum_of_minutes_in_day_range(self.start.wday, self.finish.wday)
95
+ def no_rollover_minutes
96
+ minutes_in_day_range(start.wday, finish.wday)
97
+ end
98
+
99
+ def rollover_minutes
100
+ minutes_to_first_saturday + minutes_to_finish_day
101
+ end
102
+
103
+ def range_total
104
+ total_days = elapsed_days
105
+
106
+ sum = minutes_to_first_saturday
107
+ total_days -= (7 - start.wday)
108
+
109
+ sum += minutes_to_finish_day
110
+ total_days -= (finish.wday + 1)
111
+
112
+ sum += week_total * total_days / 7
113
+ sum
114
+ end
115
+
116
+ def minutes_to_first_saturday
117
+ minutes_in_day_range(start.wday, 6)
118
+ end
119
+
120
+ def minutes_to_finish_day
121
+ minutes_in_day_range(0, finish.wday)
122
+ end
123
+
124
+ def minutes_in_day_range(first, last)
125
+ @values[first..last].inject(0) { |a, e| a + working_minutes_in(e) }
126
+ end
127
+
128
+ def add(initial_date, duration)
129
+ running_date, duration = add_to_end_of_day(initial_date, duration)
130
+
131
+ running_date, duration = add_to_finish_day running_date, duration
132
+ running_date, duration = add_full_weeks running_date, duration
133
+ running_date, duration = add_remaining_days running_date, duration
134
+ [running_date, duration, false]
135
+ end
136
+
137
+ def add_to_end_of_day(initial_date, duration)
138
+ available_minutes_in_day = minutes_to_end_of_day(initial_date)
139
+
140
+ if available_minutes_in_day < duration
141
+ duration -= available_minutes_in_day
142
+ initial_date = start_of_next_day(initial_date)
143
+ elsif available_minutes_in_day == duration
144
+ duration -= available_minutes_in_day
145
+ initial_date = end_of_this_day(initial_date)
98
146
  else
99
- total = sum_of_minutes_in_day_range(self.start.wday, 6)
100
- total += sum_of_minutes_in_day_range(0, self.finish.wday)
147
+ initial_date = consume_minutes(initial_date, duration)
148
+ duration = 0
149
+ end
150
+ [initial_date, duration]
151
+ end
152
+
153
+ def add_to_finish_day(date, duration)
154
+ while (duration != 0) && (date.wday != next_day(finish).wday) && (jd(date) <= jd(finish))
155
+ date, duration = add_to_end_of_day(date, duration)
156
+ end
157
+
158
+ [date, duration]
159
+ end
160
+
161
+ def add_full_weeks(date, duration)
162
+ while (duration != 0) && (duration >= week_total) && ((jd(date) + (6 * 86_400)) <= jd(finish))
163
+ duration -= week_total
164
+ date += (7 * 86_400)
165
+ end
166
+
167
+ [date, duration]
168
+ end
169
+
170
+ def add_remaining_days(date, duration)
171
+ while (duration != 0) && (jd(date) <= jd(finish))
172
+ date, duration = add_to_end_of_day(date, duration)
101
173
  end
102
- return total
174
+ [date, duration]
103
175
  end
176
+
177
+ def add_to_finish_day(date, duration)
178
+ while ( duration != 0) && (date.wday != next_day(self.finish).wday) && (jd(date) <= jd(self.finish))
179
+ date, duration = add_to_end_of_day(date,duration)
180
+ end
104
181
 
105
- def sum_of_minutes_in_day_range(first,last)
106
- @values[first..last].inject(0) {|sum,item| sum + item.to_s(2).count('1')}
182
+ return date, duration
183
+ end
184
+
185
+ def add_full_weeks(date, duration)
186
+
187
+ while (duration != 0) && (duration >= self.week_total) && ((jd(date) + (6*86400)) <= jd(self.finish))
188
+ duration -= self.week_total
189
+ date += (7*86400)
190
+ end
191
+
192
+ return date, duration
193
+ end
194
+
195
+ def add_remaining_days(date, duration)
196
+ while (duration != 0) && (jd(date) <= jd(self.finish))
197
+ date, duration = add_to_end_of_day(date,duration)
198
+ end
199
+ return date, duration
107
200
  end
108
201
 
109
202
  def work_on_day(day,from_time,to_time)
@@ -117,161 +210,128 @@ module Workpattern
117
210
  end
118
211
 
119
212
  def time_mask(from_time, to_time)
120
- bit_pos_above_time(to_time) - bit_pos_time(from_time)
121
- end
122
-
123
- def bit_pos_above_time(time)
124
- bit_pos(time.hour, time.min+1)
213
+ bit_pos(to_time.hour, to_time.min + 1) - bit_pos(from_time.hour, from_time.min)
125
214
  end
126
215
 
127
216
  def bit_pos(hour,minute)
128
217
  2**( (hour * 60) + minute )
129
218
  end
130
219
 
131
- def bit_pos_time(time)
132
- bit_pos(time.hour,time.min)
220
+ def work_on_day(day, from_time, to_time)
221
+ values[day] = values[day] | time_mask(from_time, to_time)
133
222
  end
134
223
 
135
- def add(initial_date,duration)
136
-
137
- initial_date, duration = add_to_end_of_day(initial_date,duration)
138
-
139
- while ( duration != 0) && (initial_date.wday != self.finish.next_day.wday) && (initial_date.jd <= self.finish.jd)
140
- initial_date, duration = add_to_end_of_day(initial_date,duration)
141
- end
142
-
143
- while (duration != 0) && (duration >= self.week_total) && ((initial_date.jd + 6) <= self.finish.jd)
144
- duration -= self.week_total
145
- initial_date += 7
146
- end
147
-
148
- while (duration != 0) && (initial_date.jd <= self.finish.jd)
149
- initial_date, duration = add_to_end_of_day(initial_date,duration)
150
- end
151
- return initial_date, duration, false
152
-
224
+ def rest_on_day(day, from_time, to_time)
225
+ mask_of_ones = time_mask(from_time, to_time)
226
+ mask = mask_of_ones ^ working_day & working_day
227
+ values[day] = values[day] & mask
153
228
  end
154
229
 
155
- def add_to_end_of_day(initial_date, duration)
156
- available_minutes_in_day = minutes_to_end_of_day(initial_date)
230
+ def time_mask(from_time, to_time)
231
+ bit_pos(to_time.hour, to_time.min + 1) - bit_pos(from_time.hour, from_time.min)
232
+ end
157
233
 
158
- if available_minutes_in_day < duration
159
- duration -= available_minutes_in_day
160
- initial_date = start_of_next_day(initial_date)
161
- elsif available_minutes_in_day == duration
162
- duration -= available_minutes_in_day
163
- initial_date = end_of_this_day(initial_date)
164
- else
165
- initial_date = consume_minutes(initial_date,duration)
166
- duration=0
167
- end
168
- return initial_date, duration
234
+ def bit_pos(hour, minute)
235
+ 2**((hour * 60) + minute)
169
236
  end
170
237
 
171
- def minutes_to_end_of_day(date)
172
- pattern_to_end_of_day(date).to_s(2).count('1')
238
+ def minutes_to_end_of_day(date)
239
+ working_minutes_in pattern_to_end_of_day(date)
173
240
  end
174
241
 
175
- def pattern_to_end_of_day(date)
242
+ def pattern_to_end_of_day(date)
176
243
  mask = mask_to_end_of_day(date)
177
- (self.values[date.wday] & mask)
244
+ (values[date.wday] & mask)
178
245
  end
179
246
 
180
- def mask_to_end_of_day(date)
181
- bit_pos(self.hours_per_day,0) - bit_pos(date.hour, date.min)
247
+ def mask_to_end_of_day(date)
248
+ bit_pos(hours_per_day, 0) - bit_pos(date.hour, date.min)
182
249
  end
183
250
 
184
251
  def working_day
185
- 2**(60*self.hours_per_day)-1
252
+ 2**(60 * hours_per_day) - 1
186
253
  end
187
254
 
188
255
  def start_of_next_day(date)
189
- date.next_day - (HOUR * date.hour) - (MINUTE * date.minute)
256
+ next_day(date) - (HOUR * date.hour) - (MINUTE * date.min)
190
257
  end
191
258
 
192
259
  def start_of_previous_day(date)
193
- start_of_next_day(date).prev_day.prev_day
260
+ prev_day(prev_day(start_of_next_day(date)))
194
261
  end
195
262
 
196
263
  def start_of_today(date)
197
- start_of_next_day(date.prev_day)
264
+ start_of_next_day(prev_day(date))
198
265
  end
199
266
 
200
- def end_of_this_day(date)
267
+ def end_of_this_day(date)
201
268
  position = pattern_to_end_of_day(date).to_s(2).size
202
- return adjust_date(date,position)
269
+ adjust_date(date, position)
203
270
  end
204
271
 
205
- def adjust_date(date,adjustment)
272
+ def adjust_date(date, adjustment)
206
273
  date - (HOUR * date.hour) - (MINUTE * date.min) + (MINUTE * adjustment)
207
274
  end
208
275
 
209
- def diff_minutes_to_end_of_day(start_date)
210
- mask = ((2**(60*self.hours_per_day + 1)) - (2**(start_date.hour*60 + start_date.min))).to_i
211
- return (self.values[start.wday] & mask).to_s(2).count('1')
212
- end
213
-
214
276
  def mask_to_start_of_day(date)
215
- bit_pos(date.hour, date.min) - bit_pos(0,0)
277
+ bit_pos(date.hour, date.min) - bit_pos(0, 0)
216
278
  end
217
-
279
+
218
280
  def pattern_to_start_of_day(date)
219
281
  mask = mask_to_start_of_day(date)
220
- (self.values[date.wday] & mask)
282
+ (values[date.wday] & mask)
221
283
  end
222
284
 
223
285
  def minutes_to_start_of_day(date)
224
- pattern_to_start_of_day(date).to_s(2).count('1')
286
+ working_minutes_in pattern_to_start_of_day(date)
225
287
  end
226
288
 
227
- def consume_minutes(date,duration)
228
-
229
- minutes=pattern_to_end_of_day(date).to_s(2).reverse! if duration > 0
230
- minutes=pattern_to_start_of_day(date).to_s(2) if duration < 0
289
+ def consume_minutes(date, duration)
290
+ minutes = pattern_to_end_of_day(date).to_s(2).reverse! if duration > 0
291
+ minutes = pattern_to_start_of_day(date).to_s(2) if duration < 0
231
292
 
232
- top=minutes.size
233
- bottom=1
293
+ top = minutes.size
294
+ bottom = 1
234
295
  mark = top / 2
235
296
 
236
- while minutes[0,mark].count('1') != duration.abs
297
+ while minutes[0, mark].count('1') != duration.abs
237
298
  last_mark = mark
238
- if minutes[0,mark].count('1') < duration.abs
299
+ if minutes[0, mark].count('1') < duration.abs
239
300
 
240
301
  bottom = mark
241
- mark = (top-mark) / 2 + mark
302
+ mark = (top - mark) / 2 + mark
242
303
  mark = top if last_mark == mark
243
304
 
244
305
  else
245
306
 
246
307
  top = mark
247
- mark = (mark-bottom) / 2 + bottom
248
- mark = bottom if last_mark = mark
308
+ mark = (mark - bottom) / 2 + bottom
309
+ mark = bottom if last_mark == mark
249
310
 
250
- end
311
+ end
251
312
  end
252
313
 
253
314
  mark = minutes_addition_adjustment(minutes, mark) if duration > 0
254
- mark = minutes_subtraction_adjustment(minutes,mark) if duration < 0
315
+ mark = minutes_subtraction_adjustment(minutes, mark) if duration < 0
255
316
 
256
317
  return adjust_date(date, mark) if duration > 0
257
318
  return start_of_today(date) + (MINUTE * mark) if duration < 0
258
-
259
319
  end
260
-
261
- def minutes_subtraction_adjustment(minutes,mark)
262
- i = mark - 1
263
-
264
- while minutes[i]=='0'
265
- i-=1
266
- end
267
-
320
+
321
+ def minutes_subtraction_adjustment(minutes, mark)
322
+ i = mark - 1
323
+
324
+ while minutes[i] == '0'
325
+ i -= 1
326
+ end
327
+
268
328
  minutes.size - (i + 1)
269
329
  end
270
330
 
271
- def minutes_addition_adjustment(minutes,mark)
272
- minutes=minutes[0,mark]
331
+ def minutes_addition_adjustment(minutes, mark)
332
+ minutes = minutes[0, mark]
273
333
 
274
- while minutes[minutes.size-1]=='0'
334
+ while minutes[minutes.size - 1] == '0'
275
335
  minutes.chop!
276
336
  end
277
337
 
@@ -279,8 +339,7 @@ module Workpattern
279
339
  end
280
340
 
281
341
  def subtract_to_start_of_day(initial_date, duration, midnight)
282
-
283
- initial_date,duration, midnight = handle_midnight(initial_date, duration) if midnight
342
+ initial_date, duration, midnight = handle_midnight(initial_date, duration) if midnight
284
343
  available_minutes_in_day = minutes_to_start_of_day(initial_date)
285
344
 
286
345
  if duration != 0
@@ -289,93 +348,111 @@ module Workpattern
289
348
  initial_date = start_of_previous_day(initial_date)
290
349
  midnight = true
291
350
  else
292
- initial_date = consume_minutes(initial_date,duration)
351
+ initial_date = consume_minutes(initial_date, duration)
293
352
  duration = 0
294
353
  midnight = false
295
354
  end
296
355
  end
297
- return initial_date, duration, midnight
356
+ [initial_date, duration, midnight]
298
357
  end
299
358
 
300
-
301
- def handle_midnight(initial_date,duration)
359
+ def handle_midnight(initial_date, duration)
302
360
  if working?(start_of_next_day(initial_date) - MINUTE)
303
361
  duration += 1
304
362
  end
305
-
363
+
306
364
  initial_date -= (HOUR * initial_date.hour)
307
365
  initial_date -= (MINUTE * initial_date.min)
308
- initial_date = initial_date.next_day - MINUTE
366
+ initial_date = next_day(initial_date) - MINUTE
309
367
 
310
- return initial_date, duration, false
368
+ [initial_date, duration, false]
311
369
  end
312
370
 
313
-
314
371
  def subtract(initial_date, duration, midnight)
315
- initial_date,duration, midnight = handle_midnight(initial_date, duration) if midnight
372
+ initial_date, duration, midnight = handle_midnight(initial_date, duration) if midnight
316
373
 
317
374
  initial_date, duration, midnight = subtract_to_start_of_day(initial_date, duration, midnight)
318
375
 
319
- while ( duration != 0) && (initial_date.wday != self.start.prev_day.wday) && (initial_date.jd >= self.start.jd)
320
- initial_date, duration, midnight = subtract_to_start_of_day(initial_date,duration, midnight)
376
+ while (duration != 0) && (initial_date.wday != prev_day(start.wday)) && (jd(initial_date) >= jd(start))
377
+ initial_date, duration, midnight = subtract_to_start_of_day(initial_date, duration, midnight)
321
378
  end
322
379
 
323
- while (duration != 0) && (duration >= self.week_total) && ((initial_date.jd - 6) >= self.start.jd)
324
- duration += self.week_total
380
+ while (duration != 0) && (duration >= week_total) && ((jd(initial_date) - (6 * 86_400)) >= jd(start))
381
+ duration += week_total
325
382
  initial_date -= 7
326
383
  end
327
384
 
328
- while (duration != 0) && (initial_date.jd >= self.start.jd)
329
- initial_date, duration, midnight = subtract_to_start_of_day(initial_date,duration, midnight)
385
+ while (duration != 0) && (jd(initial_date) >= jd(start))
386
+ initial_date, duration, midnight = subtract_to_start_of_day(initial_date,
387
+ duration,
388
+ midnight)
330
389
  end
331
390
 
332
- return initial_date, duration, midnight
333
-
391
+ [initial_date, duration, midnight]
334
392
  end
335
393
 
336
394
  def diff_in_same_weekpattern(start_date, finish_date)
337
395
  duration, start_date = diff_to_tomorrow(start_date)
338
- while true
339
- break if (start_date.wday == (self.finish.wday + 1))
340
- break if (start_date.jd == self.finish.jd)
341
- break if (start_date.jd == finish_date.jd)
396
+ loop do
397
+ break if start_date.wday == (finish.wday + 1)
398
+ break if jd(start_date) == jd(finish)
399
+ break if jd(start_date) == jd(finish_date)
342
400
  duration += minutes_to_end_of_day(start_date)
343
401
  start_date = start_of_next_day(start_date)
344
- end
402
+ end
345
403
 
346
- while true
347
- break if ((start_date + 7) > finish_date)
348
- break if ((start_date + 6).jd > self.finish.jd)
404
+ loop do
405
+ break if (start_date + (7 * 86_400)) > finish_date
406
+ break if jd(start_date + (6 * 86_400)) > jd(finish)
349
407
  duration += week_total
350
- start_date += 7
408
+ start_date += (7 * 86_400)
351
409
  end
352
410
 
353
- while true
354
- break if (start_date.jd >= self.finish.jd)
355
- break if (start_date.jd >= finish_date.jd)
411
+ loop do
412
+ break if jd(start_date) >= jd(finish)
413
+ break if jd(start_date) >= jd(finish_date)
356
414
  duration += minutes_to_end_of_day(start_date)
357
415
  start_date = start_of_next_day(start_date)
358
- end
359
-
360
- interim_duration, start_date = diff_in_same_day(start_date, finish_date) if (start_date < self.finish)
416
+ end
417
+
418
+ if start_date < finish
419
+ interim_duration, start_date = diff_in_same_day(start_date, finish_date)
420
+ end
361
421
  duration += interim_duration unless interim_duration.nil?
362
- return duration, start_date
422
+ [duration, start_date]
363
423
  end
364
-
365
- def diff_beyond_weekpattern(start_date,finish_date)
424
+
425
+ def diff_beyond_weekpattern(start_date, finish_date)
366
426
  duration, start_date = diff_in_same_weekpattern(start_date, finish_date)
367
- return duration, start_date
427
+ [duration, start_date]
368
428
  end
369
429
 
370
430
  def diff_to_tomorrow(start_date)
371
- mask = bit_pos(self.hours_per_day, 0) - bit_pos(start_date.hour, start_date.min)
372
- return (self.values[start_date.wday] & mask).to_s(2).count('1'), start_of_next_day(start_date)
431
+ finish_bit_pos = bit_pos(hours_per_day, 0)
432
+ start_bit_pos = bit_pos(start_date.hour, start_date.min)
433
+ mask = finish_bit_pos - start_bit_pos
434
+ minutes = working_minutes_in(values[start_date.wday] & mask)
435
+ [minutes, start_of_next_day(start_date)]
373
436
  end
374
437
 
375
438
  def diff_in_same_day(start_date, finish_date)
376
- mask = bit_pos(finish_date.hour, finish_date.min) - bit_pos(start_date.hour, start_date.min)
377
- return (self.values[start_date.wday] & mask).to_s(2).count('1'), finish_date
439
+ finish_bit_pos = bit_pos(finish_date.hour, finish_date.min)
440
+ start_bit_pos = bit_pos(start_date.hour, start_date.min)
441
+ mask = finish_bit_pos - start_bit_pos
442
+ minutes = working_minutes_in(values[start_date.wday] & mask)
443
+ [minutes, finish_date]
444
+ end
445
+
446
+ def next_day(time)
447
+ time + 86_400
378
448
  end
379
449
 
450
+ def prev_day(time)
451
+ time - 86_400
452
+ end
453
+
454
+ def jd(time)
455
+ Time.gm(time.year, time.month, time.day)
456
+ end
380
457
  end
381
458
  end