workpattern 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,245 @@
1
+ module Workpattern
2
+
3
+ class Day
4
+
5
+ attr_accessor :pattern, :hours_per_day, :first_working_minute, :last_working_minute
6
+
7
+ def initialize(hours_per_day = HOURS_IN_DAY, type = WORK_TYPE)
8
+ @hours_per_day = hours_per_day
9
+ @pattern = initial_day(type)
10
+ set_first_and_last_minutes
11
+ end
12
+
13
+ def set_resting(start_time, finish_time)
14
+ mask = resting_mask(start_time, finish_time)
15
+ @pattern = @pattern & mask
16
+ set_first_and_last_minutes
17
+ end
18
+
19
+ def set_working(from_time, to_time)
20
+ @pattern = @pattern | working_mask(from_time, to_time)
21
+ set_first_and_last_minutes
22
+ end
23
+
24
+ def working_minutes(from_time = FIRST_TIME_IN_DAY, to_time = LAST_TIME_IN_DAY)
25
+ section = @pattern & working_mask(from_time, to_time)
26
+ section.to_s(2).count('1')
27
+ end
28
+
29
+ def working?(hour, minute)
30
+ mask = (2**((hour * 60) + minute))
31
+ result = mask & @pattern
32
+ mask == result
33
+ end
34
+
35
+ def resting?(hour, minute)
36
+ !working?(hour,minute)
37
+ end
38
+
39
+ def calc(a_date, a_duration)
40
+ if a_duration == 0
41
+ return a_date, a_duration, SAME_DAY
42
+ else
43
+ return a_duration > 0 ? add(a_date, a_duration) : subtract(a_date, a_duration)
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def add(a_date, a_duration)
50
+ minutes_left = working_minutes(a_date)
51
+ if a_duration > minutes_left
52
+ return [a_date, a_duration - minutes_left, NEXT_DAY]
53
+ elsif a_duration < minutes_left
54
+ return add_minutes(a_date, a_duration)
55
+ else
56
+ if working?(LAST_TIME_IN_DAY.hour, LAST_TIME_IN_DAY.min)
57
+ return [a_date, 0, NEXT_DAY]
58
+ else
59
+ return_date = Time.gm(a_date.year, a_date.month, a_date.day, @last_working_minute.hour, @last_working_minute.min) + 60
60
+ return [ return_date, 0, SAME_DAY]
61
+ end
62
+ end
63
+ end
64
+
65
+ def add_minutes(a_date, a_duration)
66
+ elapsed_date = a_date + (a_duration * 60) - 60
67
+
68
+ if working_minutes(a_date, elapsed_date) == a_duration
69
+ return [elapsed_date += 60, 0, SAME_DAY]
70
+ else
71
+ begin
72
+ elapsed_date += 60
73
+ end while working_minutes(a_date, elapsed_date) != a_duration
74
+ return [elapsed_date += 60, 0, SAME_DAY]
75
+ end
76
+ end
77
+
78
+ def subtract(a_date, a_duration)
79
+ minutes_left = working_minutes(FIRST_TIME_IN_DAY,a_date - 60)
80
+ abs_duration = a_duration.abs
81
+ if abs_duration > minutes_left
82
+ return [a_date, a_duration + minutes_left, PREVIOUS_DAY]
83
+ elsif abs_duration < minutes_left
84
+ return subtract_minutes(a_date, abs_duration)
85
+ else
86
+ return [Time.gm(a_date.year,a_date.month,a_date.day,@first_working_minute.hour,@first_working_minute.min), 0, SAME_DAY]
87
+ end
88
+ end
89
+
90
+ def subtract_minutes(a_date, abs_duration)
91
+ elapsed_date = a_date - (abs_duration * 60)
92
+ if working_minutes(elapsed_date, a_date - 60) == abs_duration
93
+ return [elapsed_date, 0, SAME_DAY]
94
+ else
95
+ a_date -= 60
96
+ begin
97
+ elapsed_date -= 60
98
+ end while working_minutes(elapsed_date, a_date) != abs_duration
99
+ return [elapsed_date, 0, SAME_DAY]
100
+ end
101
+ end
102
+
103
+ def working_day
104
+ 2**((60 * @hours_per_day) +1) - 1
105
+ end
106
+
107
+ def initial_day(type = WORK_TYPE)
108
+
109
+ pattern = 2**((60 * @hours_per_day) + 1)
110
+
111
+ if type == WORK_TYPE
112
+ pattern = pattern - 1
113
+ end
114
+
115
+ pattern
116
+ end
117
+
118
+ def working_mask(start_time, finish_time)
119
+
120
+ start = minutes_in_time(start_time)
121
+ finish = minutes_in_time(finish_time)
122
+
123
+ mask = initial_day
124
+
125
+ mask = mask - ((2**start) - 1)
126
+ mask & ((2**(finish + 1)) -1)
127
+ end
128
+
129
+ def resting_mask(start_time, finish_time)
130
+
131
+ start = minutes_in_time(start_time)
132
+ finish_clock = Clock.new(finish_time.hour, finish_time.min + 1)
133
+
134
+ mask = initial_day(REST_TYPE)
135
+ if minutes_in_time(finish_time) != LAST_TIME_IN_DAY.minutes
136
+ mask = mask | working_mask(finish_clock,LAST_TIME_IN_DAY)
137
+ end
138
+ mask | ((2**start) - 1)
139
+ end
140
+
141
+ def minutes_in_time(a_time)
142
+ (a_time.hour * 60) + a_time.min
143
+ end
144
+
145
+ def last_minute
146
+ if working?(LAST_TIME_IN_DAY.hour, LAST_TIME_IN_DAY.min)
147
+ return LAST_TIME_IN_DAY
148
+ end
149
+
150
+ top = minutes_in_time(LAST_TIME_IN_DAY)
151
+ bottom = minutes_in_time(FIRST_TIME_IN_DAY)
152
+ mark = top / 2
153
+
154
+ not_done = true
155
+ while not_done
156
+
157
+ minutes = working_minutes(minutes_to_time(mark), minutes_to_time(top))
158
+
159
+ if minutes > 1
160
+ bottom = mark
161
+ mark = mark + ((top - bottom) / 2)
162
+
163
+ elsif minutes == 0
164
+ top = mark
165
+ mark = mark - (( top - bottom) / 2)
166
+
167
+ elsif minutes == 1 && is_resting(mark)
168
+ bottom = mark
169
+ mark = mark + ((top - bottom) / 2)
170
+
171
+ else
172
+ not_done = false
173
+
174
+ end
175
+
176
+ if mark == bottom #& last_mark != mark
177
+ mark = mark + 1
178
+ end
179
+
180
+ if mark == 1 && top == 1
181
+ mark = 0
182
+ end
183
+
184
+ end
185
+ minutes_to_time(mark)
186
+
187
+ end
188
+
189
+ def first_minute
190
+ if working?(FIRST_TIME_IN_DAY.hour, FIRST_TIME_IN_DAY.min)
191
+ return FIRST_TIME_IN_DAY
192
+ end
193
+
194
+ top = minutes_in_time(LAST_TIME_IN_DAY)
195
+ bottom = minutes_in_time(FIRST_TIME_IN_DAY)
196
+ mark = top / 2
197
+
198
+ not_done = true
199
+ while not_done
200
+
201
+ minutes = working_minutes(minutes_to_time(bottom), minutes_to_time(mark))
202
+ if minutes > 1
203
+ top = mark
204
+ mark = mark - ((top - bottom) / 2)
205
+ elsif minutes == 0
206
+ bottom = mark
207
+ mark = mark + (( top - bottom) / 2)
208
+ elsif minutes == 1 && is_resting(mark)
209
+ top = mark
210
+ mark = mark - ((top - bottom) / 2)
211
+ else
212
+ not_done = false
213
+ end
214
+
215
+ if mark == 1 && top == 1
216
+ mark = 0
217
+ end
218
+ end
219
+
220
+ minutes_to_time(mark)
221
+ end
222
+
223
+ def minutes_to_time(minutes)
224
+ Time.gm(1963,6,10,minutes / 60, minutes - (minutes / 60 * 60))
225
+ end
226
+
227
+ def is_resting(minutes)
228
+ a_time =(minutes_to_time(minutes))
229
+ resting?(a_time.hour, a_time.min)
230
+ end
231
+
232
+ def set_first_and_last_minutes
233
+ if working_minutes == 0
234
+ @first_working_minute = nil
235
+ @last_working_minute = nil
236
+ else
237
+ @first_working_minute = first_minute
238
+ @last_working_minute = last_minute
239
+ end
240
+ end
241
+
242
+
243
+ end
244
+
245
+ end
@@ -1,3 +1,3 @@
1
1
  module Workpattern
2
- VERSION = '0.5.0'.freeze
2
+ VERSION = '0.6.0'
3
3
  end
@@ -8,15 +8,16 @@ module Workpattern
8
8
  #
9
9
  # @private
10
10
  class Week
11
- attr_accessor :values, :hours_per_day, :start, :finish, :week_total, :total
11
+ attr_accessor :hours_per_day, :start, :finish, :days
12
+ attr_writer :week_total, :total
12
13
 
13
- def initialize(start, finish, type = 1, hours_per_day = 24)
14
+ def initialize(start, finish, type = WORK_TYPE, hours_per_day = HOURS_IN_DAY)
14
15
  @hours_per_day = hours_per_day
15
16
  @start = Time.gm(start.year, start.month, start.day)
16
17
  @finish = Time.gm(finish.year, finish.month, finish.day)
17
- @values = Array.new(6)
18
- 0.upto(6) do |i|
19
- @values[i] = working_day * type
18
+ @days = Array.new(LAST_DAY_OF_WEEK)
19
+ FIRST_DAY_OF_WEEK.upto(LAST_DAY_OF_WEEK) do |i|
20
+ @days[i] = Day.new(hours_per_day, type)
20
21
  end
21
22
  end
22
23
 
@@ -27,7 +28,7 @@ module Workpattern
27
28
  end
28
29
 
29
30
  def week_total
30
- elapsed_days > 6 ? full_week_total_minutes : part_week_total_minutes
31
+ elapsed_days > 6 ? full_week_working_minutes : part_week_total_minutes
31
32
  end
32
33
 
33
34
  def total
@@ -36,56 +37,62 @@ module Workpattern
36
37
 
37
38
  def workpattern(days, from_time, to_time, type)
38
39
  DAYNAMES[days].each do |day|
39
- if type == 1
40
- work_on_day(day, from_time, to_time)
40
+ if type == WORK_TYPE
41
+ @days[day].set_working(from_time, to_time)
41
42
  else
42
- rest_on_day(day, from_time, to_time)
43
+ @days[day].set_resting(from_time, to_time)
43
44
  end
44
45
  end
45
46
  end
46
47
 
47
48
  def duplicate
48
- duplicate_week = Week.new(start, finish)
49
- 0.upto(6).each { |i| duplicate_week.values[i] = @values[i] }
49
+ duplicate_week = Week.new(@start, @finish)
50
+ FIRST_DAY_OF_WEEK.upto(LAST_DAY_OF_WEEK) do |i|
51
+ duplicate_week.days[i] = @days[i].clone
52
+ duplicate_week.days[i].hours_per_day = @days[i].hours_per_day
53
+ duplicate_week.days[i].pattern = @days[i].pattern
54
+ end
50
55
  duplicate_week
51
56
  end
52
57
 
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)
58
+ def calc(a_date, a_duration, a_day = SAME_DAY)
59
+ if a_duration == 0
60
+ return a_date, a_duration
61
+ elsif a_duration > 0
62
+ return add(a_date, a_duration)
63
+ else
64
+ subtract(a_date, a_duration, a_day)
65
+ end
58
66
  end
59
67
 
60
68
  def working?(time)
61
- return true if bit_pos(time.hour, time.min) & @values[time.wday] > 0
62
- false
69
+ @days[time.wday].working?(time.hour, time.min)
63
70
  end
64
71
 
65
- def resting?(date)
66
- !working?(date)
72
+ def resting?(time)
73
+ @days[time.wday].resting?(time.hour, time.min)
67
74
  end
68
75
 
69
- def diff(start_d, finish_d)
70
- start_d, finish_d = finish_d, start_d if ((start_d <=> finish_d)) == 1
76
+ def diff(start_date, finish_date)
77
+ if start_date > finish_date
78
+ start_date, finish_date = finish_date, start_date
79
+ end
71
80
 
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)
81
+ if jd(start_date) == jd(finish_date)
82
+ return diff_in_same_day(start_date, finish_date)
83
+ else
84
+ return diff_in_same_weekpattern(start_date, finish_date)
85
+ end
75
86
  end
76
-
87
+
77
88
  private
78
89
 
79
- def working_minutes_in(day)
80
- day.to_s(2).count('1')
81
- end
82
-
83
90
  def elapsed_days
84
- (finish - start).to_i / 86_400 + 1
91
+ (finish - start).to_i / DAY + 1
85
92
  end
86
93
 
87
- def full_week_total_minutes
88
- minutes_in_day_range 0, 6
94
+ def full_week_working_minutes
95
+ minutes_in_day_range FIRST_DAY_OF_WEEK, LAST_DAY_OF_WEEK
89
96
  end
90
97
 
91
98
  def part_week_total_minutes
@@ -114,341 +121,157 @@ module Workpattern
114
121
  end
115
122
 
116
123
  def minutes_to_first_saturday
117
- minutes_in_day_range(start.wday, 6)
124
+ minutes_in_day_range(start.wday, LAST_DAY_OF_WEEK)
118
125
  end
119
126
 
120
127
  def minutes_to_finish_day
121
- minutes_in_day_range(0, finish.wday)
128
+ minutes_in_day_range(FIRST_DAY_OF_WEEK, finish.wday)
122
129
  end
123
130
 
124
131
  def minutes_in_day_range(first, last)
125
- @values[first..last].inject(0) { |a, e| a + working_minutes_in(e) }
132
+ @days[first..last].inject(0) { |sum, day| sum + day.working_minutes }
126
133
  end
127
134
 
128
- def add(initial_date, duration)
129
- running_date, duration = add_to_end_of_day(initial_date, duration)
135
+ def add(a_date, a_duration)
130
136
 
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
137
+ r_date, r_duration = add_to_end_of_day(a_date, a_duration)
136
138
 
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)
146
- else
147
- initial_date = consume_minutes(initial_date, duration)
148
- duration = 0
149
- end
150
- [initial_date, duration]
139
+ r_date, r_duration = add_to_finish_day r_date, r_duration
140
+ r_date, r_duration = add_full_weeks r_date, r_duration
141
+ r_date, r_duration = add_remaining_days r_date, r_duration
142
+ [r_date, r_duration, false]
151
143
  end
152
144
 
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
145
+ def add_to_end_of_day(a_date, a_duration)
146
+ r_date, r_duration, r_day = @days[a_date.wday].calc(a_date,a_duration)
157
147
 
158
- [date, duration]
159
- end
148
+ if r_day == NEXT_DAY
149
+ r_date = start_of_next_day(r_date)
160
150
 
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
151
  end
166
152
 
167
- [date, duration]
153
+ [r_date, r_duration]
168
154
  end
169
155
 
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)
173
- end
174
- [date, duration]
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)
156
+ def add_to_finish_day(a_date, a_duration)
157
+ while ( a_duration != 0) && (a_date.wday != next_day(self.finish).wday) && (jd(a_date) <= jd(self.finish))
158
+ a_date, a_duration = add_to_end_of_day(a_date,a_duration)
180
159
  end
181
160
 
182
- return date, duration
161
+ [a_date, a_duration]
183
162
  end
184
163
 
185
- def add_full_weeks(date, duration)
164
+ def add_full_weeks(a_date, a_duration)
186
165
 
187
- while (duration != 0) && (duration >= self.week_total) && ((jd(date) + (6*86400)) <= jd(self.finish))
188
- duration -= self.week_total
189
- date += (7*86400)
166
+ while (a_duration != 0) && (a_duration >= self.week_total) && ((jd(a_date) + (6*86400)) <= jd(self.finish))
167
+ a_duration -= self.week_total
168
+ a_date += (7*86400)
190
169
  end
191
170
 
192
- return date, duration
171
+ [a_date, a_duration]
193
172
  end
194
173
 
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)
174
+ def add_remaining_days(a_date, a_duration)
175
+ while (a_duration != 0) && (jd(a_date) <= jd(self.finish))
176
+ a_date, a_duration = add_to_end_of_day(a_date,a_duration)
198
177
  end
199
- return date, duration
200
- end
201
-
202
- def work_on_day(day,from_time,to_time)
203
- self.values[day] = self.values[day] | time_mask(from_time, to_time)
204
- end
205
-
206
- def rest_on_day(day,from_time,to_time)
207
- mask_of_1s = time_mask(from_time, to_time)
208
- mask = mask_of_1s ^ working_day & working_day
209
- self.values[day] = self.values[day] & mask
210
- end
211
-
212
- def time_mask(from_time, to_time)
213
- bit_pos(to_time.hour, to_time.min + 1) - bit_pos(from_time.hour, from_time.min)
214
- end
215
-
216
- def bit_pos(hour,minute)
217
- 2**( (hour * 60) + minute )
218
- end
219
-
220
- def work_on_day(day, from_time, to_time)
221
- values[day] = values[day] | time_mask(from_time, to_time)
222
- end
223
-
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
228
- end
229
-
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
233
-
234
- def bit_pos(hour, minute)
235
- 2**((hour * 60) + minute)
236
- end
237
-
238
- def minutes_to_end_of_day(date)
239
- working_minutes_in pattern_to_end_of_day(date)
240
- end
241
-
242
- def pattern_to_end_of_day(date)
243
- mask = mask_to_end_of_day(date)
244
- (values[date.wday] & mask)
245
- end
246
-
247
- def mask_to_end_of_day(date)
248
- bit_pos(hours_per_day, 0) - bit_pos(date.hour, date.min)
249
- end
250
-
251
- def working_day
252
- 2**(60 * hours_per_day) - 1
178
+ [a_date, a_duration]
253
179
  end
254
180
 
255
181
  def start_of_next_day(date)
256
182
  next_day(date) - (HOUR * date.hour) - (MINUTE * date.min)
257
183
  end
258
184
 
259
- def start_of_previous_day(date)
260
- prev_day(prev_day(start_of_next_day(date)))
261
- end
262
-
263
- def start_of_today(date)
264
- start_of_next_day(prev_day(date))
265
- end
266
-
267
- def end_of_this_day(date)
268
- position = pattern_to_end_of_day(date).to_s(2).size
269
- adjust_date(date, position)
270
- end
271
-
272
- def adjust_date(date, adjustment)
273
- date - (HOUR * date.hour) - (MINUTE * date.min) + (MINUTE * adjustment)
274
- end
275
-
276
- def mask_to_start_of_day(date)
277
- bit_pos(date.hour, date.min) - bit_pos(0, 0)
278
- end
279
-
280
- def pattern_to_start_of_day(date)
281
- mask = mask_to_start_of_day(date)
282
- (values[date.wday] & mask)
283
- end
284
-
285
- def minutes_to_start_of_day(date)
286
- working_minutes_in pattern_to_start_of_day(date)
287
- end
288
-
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
292
-
293
- top = minutes.size
294
- bottom = 1
295
- mark = top / 2
296
-
297
- while minutes[0, mark].count('1') != duration.abs
298
- last_mark = mark
299
- if minutes[0, mark].count('1') < duration.abs
300
-
301
- bottom = mark
302
- mark = (top - mark) / 2 + mark
303
- mark = top if last_mark == mark
304
-
305
- else
306
-
307
- top = mark
308
- mark = (mark - bottom) / 2 + bottom
309
- mark = bottom if last_mark == mark
310
-
311
- end
312
- end
313
-
314
- mark = minutes_addition_adjustment(minutes, mark) if duration > 0
315
- mark = minutes_subtraction_adjustment(minutes, mark) if duration < 0
316
-
317
- return adjust_date(date, mark) if duration > 0
318
- return start_of_today(date) + (MINUTE * mark) if duration < 0
319
- end
320
-
321
- def minutes_subtraction_adjustment(minutes, mark)
322
- i = mark - 1
185
+ def subtract_to_start_of_day(a_date, a_duration, a_day)
186
+
187
+
188
+ a_date, a_duration, a_day = handle_midnight(a_date, a_duration, a_day)
323
189
 
324
- while minutes[i] == '0'
325
- i -= 1
326
- end
190
+ r_date, r_duration, r_day = @days[a_date.wday].calc(a_date, a_duration)
327
191
 
328
- minutes.size - (i + 1)
329
- end
330
-
331
- def minutes_addition_adjustment(minutes, mark)
332
- minutes = minutes[0, mark]
192
+ [r_date, r_duration, r_day]
333
193
 
334
- while minutes[minutes.size - 1] == '0'
335
- minutes.chop!
336
- end
337
-
338
- minutes.size
339
194
  end
340
195
 
341
- def subtract_to_start_of_day(initial_date, duration, midnight)
342
- initial_date, duration, midnight = handle_midnight(initial_date, duration) if midnight
343
- available_minutes_in_day = minutes_to_start_of_day(initial_date)
196
+ def handle_midnight(a_date, a_duration, a_day)
197
+
198
+ if a_day == PREVIOUS_DAY
199
+ a_date -= DAY
200
+ a_date = Time.gm(a_date.year, a_date.month, a_date.day,LAST_TIME_IN_DAY.hour, LAST_TIME_IN_DAY.min)
344
201
 
345
- if duration != 0
346
- if available_minutes_in_day < duration.abs
347
- duration += available_minutes_in_day
348
- initial_date = start_of_previous_day(initial_date)
349
- midnight = true
350
- else
351
- initial_date = consume_minutes(initial_date, duration)
352
- duration = 0
353
- midnight = false
354
- end
202
+ if @days[a_date.wday].working?(a_date.hour, a_date.min)
203
+ a_duration += 1
204
+ end
355
205
  end
356
- [initial_date, duration, midnight]
357
- end
358
-
359
- def handle_midnight(initial_date, duration)
360
- if working?(start_of_next_day(initial_date) - MINUTE)
361
- duration += 1
362
- end
363
-
364
- initial_date -= (HOUR * initial_date.hour)
365
- initial_date -= (MINUTE * initial_date.min)
366
- initial_date = next_day(initial_date) - MINUTE
367
206
 
368
- [initial_date, duration, false]
207
+ [a_date, a_duration, SAME_DAY]
369
208
  end
370
209
 
371
- def subtract(initial_date, duration, midnight)
372
- initial_date, duration, midnight = handle_midnight(initial_date, duration) if midnight
373
-
374
- initial_date, duration, midnight = subtract_to_start_of_day(initial_date, duration, midnight)
375
-
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)
210
+ def subtract(a_date, a_duration, a_day)
211
+ a_date, a_duration, a_day = handle_midnight(a_date, a_duration, a_day)
212
+ a_date, a_duration, a_day = subtract_to_start_of_day(a_date, a_duration, a_day)
213
+
214
+ while (a_duration != 0) && (a_date.wday != start.wday) && (jd(a_date) > jd(start))
215
+ a_date, a_duration, a_day = handle_midnight(a_date, a_duration, a_day)
216
+ a_date, a_duration, a_day = subtract_to_start_of_day(a_date, a_duration, a_day)
378
217
  end
379
218
 
380
- while (duration != 0) && (duration >= week_total) && ((jd(initial_date) - (6 * 86_400)) >= jd(start))
381
- duration += week_total
382
- initial_date -= 7
219
+ while (a_duration != 0) && (a_duration >= week_total) && ((jd(a_date) - (6 * DAY)) >= jd(start))
220
+ a_duration += week_total
221
+ a_date -= 7
383
222
  end
384
223
 
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)
224
+ while (a_duration != 0) && (jd(a_date) > jd(start))
225
+ a_date, a_duration, a_day = subtract_to_start_of_day(a_date,a_duration,a_day)
389
226
  end
390
227
 
391
- [initial_date, duration, midnight]
228
+ [a_date, a_duration, a_day]
392
229
  end
393
230
 
394
231
  def diff_in_same_weekpattern(start_date, finish_date)
395
- duration, start_date = diff_to_tomorrow(start_date)
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)
400
- duration += minutes_to_end_of_day(start_date)
401
- start_date = start_of_next_day(start_date)
232
+ minutes = @days[start_date.wday].working_minutes(start_date, LAST_TIME_IN_DAY)
233
+ run_date = start_of_next_day(start_date)
234
+ while (run_date.wday != start.wday) && (jd(run_date) < jd(finish)) && (jd(run_date) != jd(finish_date))
235
+ minutes += @days[run_date.wday].working_minutes
236
+ run_date += DAY
402
237
  end
403
238
 
404
- loop do
405
- break if (start_date + (7 * 86_400)) > finish_date
406
- break if jd(start_date + (6 * 86_400)) > jd(finish)
407
- duration += week_total
408
- start_date += (7 * 86_400)
239
+ while ((jd(run_date) + (7 * DAY)) < jd(finish_date)) && ((jd(run_date) + (7 * DAY)) < jd(finish))
240
+ minutes += week_total
241
+ run_date += (7 * DAY)
409
242
  end
410
243
 
411
- loop do
412
- break if jd(start_date) >= jd(finish)
413
- break if jd(start_date) >= jd(finish_date)
414
- duration += minutes_to_end_of_day(start_date)
415
- start_date = start_of_next_day(start_date)
244
+ while (jd(run_date) < jd(finish_date)) && (jd(run_date) <= jd(finish))
245
+ minutes += @days[run_date.wday].working_minutes
246
+ run_date += DAY
416
247
  end
417
248
 
418
- if start_date < finish
419
- interim_duration, start_date = diff_in_same_day(start_date, finish_date)
249
+ if run_date != finish_date
250
+
251
+ if (jd(run_date) == jd(finish_date)) && (jd(run_date) <= jd(finish))
252
+ minutes += @days[run_date.wday].working_minutes(run_date, finish_date - MINUTE)
253
+ run_date = finish_date
254
+ elsif (jd(run_date) <= jd(finish))
255
+ minutes += @days[run_date.wday].working_minutes
256
+ run_date += DAY
257
+ end
420
258
  end
421
- duration += interim_duration unless interim_duration.nil?
422
- [duration, start_date]
423
- end
424
259
 
425
- def diff_beyond_weekpattern(start_date, finish_date)
426
- duration, start_date = diff_in_same_weekpattern(start_date, finish_date)
427
- [duration, start_date]
428
- end
260
+ [minutes, run_date]
429
261
 
430
- def diff_to_tomorrow(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)]
436
262
  end
437
263
 
438
264
  def diff_in_same_day(start_date, 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)
265
+ minutes = @days[start_date.wday].working_minutes(start_date, finish_date - MINUTE)
443
266
  [minutes, finish_date]
444
267
  end
445
268
 
446
269
  def next_day(time)
447
- time + 86_400
270
+ time + DAY
448
271
  end
449
272
 
450
273
  def prev_day(time)
451
- time - 86_400
274
+ time - DAY
452
275
  end
453
276
 
454
277
  def jd(time)