workpattern 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|