workpattern 0.5.0 → 0.6.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.
@@ -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)