workpattern 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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