workpattern 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/.gitignore +4 -1
- data/.travis.yml +8 -2
- data/CHANGELOG +19 -0
- data/Gemfile +0 -0
- data/README.md +58 -57
- data/Rakefile +0 -0
- data/lib/workpattern.rb +65 -55
- data/lib/workpattern/clock.rb +17 -20
- data/lib/workpattern/utility/base.rb +16 -21
- data/lib/workpattern/version.rb +2 -2
- data/lib/workpattern/week.rb +266 -189
- data/lib/workpattern/workpattern.rb +170 -142
- data/script/console +0 -0
- data/script/destroy +0 -0
- data/script/generate +0 -0
- data/script/txt2html +0 -0
- data/test/test_clock.rb +19 -22
- data/test/test_helper.rb +0 -0
- data/test/test_week.rb +393 -368
- data/test/test_workpattern.rb +238 -207
- data/test/test_workpattern_module.rb +57 -51
- data/workpattern.gemspec +7 -6
- metadata +44 -16
- data/config/website.yml +0 -2
@@ -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
|
-
|
6
|
+
# @private
|
7
|
+
module Base
|
8
|
+
# Holds local timezone info
|
9
|
+
@@tz = nil
|
8
10
|
|
9
|
-
#
|
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
|
17
|
-
|
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
|
28
|
-
|
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
|
data/lib/workpattern/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module Workpattern
|
2
|
-
VERSION = '0.
|
3
|
-
end
|
2
|
+
VERSION = '0.5.0'.freeze
|
3
|
+
end
|
data/lib/workpattern/week.rb
CHANGED
@@ -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=
|
10
|
-
@finish=
|
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 <=>(
|
18
|
-
if
|
19
|
-
|
20
|
-
|
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
|
-
|
29
|
-
end
|
30
|
+
elapsed_days > 6 ? full_week_total_minutes : part_week_total_minutes
|
31
|
+
end
|
30
32
|
|
31
33
|
def total
|
32
|
-
|
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
|
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(
|
50
|
-
0.upto(6).each
|
51
|
-
|
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(
|
58
|
-
|
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?(
|
62
|
-
return true if
|
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(
|
71
|
-
|
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
|
-
|
77
|
+
private
|
78
|
+
|
79
|
+
def working_minutes_in(day)
|
80
|
+
day.to_s(2).count('1')
|
81
|
+
end
|
85
82
|
|
86
|
-
def
|
87
|
-
(
|
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
|
-
|
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
|
-
|
97
|
-
|
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
|
-
|
100
|
-
|
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
|
-
|
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
|
-
|
106
|
-
|
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
|
-
|
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
|
132
|
-
|
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
|
136
|
-
|
137
|
-
|
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
|
156
|
-
|
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
|
-
|
159
|
-
|
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)
|
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
|
-
(
|
244
|
+
(values[date.wday] & mask)
|
178
245
|
end
|
179
246
|
|
180
|
-
def mask_to_end_of_day(date)
|
181
|
-
bit_pos(
|
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*
|
252
|
+
2**(60 * hours_per_day) - 1
|
186
253
|
end
|
187
254
|
|
188
255
|
def start_of_next_day(date)
|
189
|
-
date
|
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)
|
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
|
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
|
-
|
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
|
-
(
|
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)
|
286
|
+
working_minutes_in pattern_to_start_of_day(date)
|
225
287
|
end
|
226
288
|
|
227
|
-
def consume_minutes(date,duration)
|
228
|
-
|
229
|
-
minutes=
|
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
|
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
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
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
|
-
|
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
|
366
|
+
initial_date = next_day(initial_date) - MINUTE
|
309
367
|
|
310
|
-
|
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 (
|
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 >=
|
324
|
-
duration +=
|
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
|
329
|
-
initial_date, duration, midnight = subtract_to_start_of_day(initial_date,
|
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
|
-
|
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
|
-
|
339
|
-
break if
|
340
|
-
break if (start_date
|
341
|
-
break if (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)
|
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
|
-
|
347
|
-
break if (
|
348
|
-
break if (
|
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
|
-
|
354
|
-
break if (start_date
|
355
|
-
break if (start_date
|
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
|
-
|
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
|
-
|
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
|
-
|
427
|
+
[duration, start_date]
|
368
428
|
end
|
369
429
|
|
370
430
|
def diff_to_tomorrow(start_date)
|
371
|
-
|
372
|
-
|
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
|
-
|
377
|
-
|
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
|