workpattern 0.3.6 → 0.4.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 +13 -5
- data/.travis.yml +4 -1
- data/CHANGELOG +5 -0
- data/lib/workpattern.rb +0 -2
- data/lib/workpattern/version.rb +1 -3
- data/lib/workpattern/week.rb +307 -311
- data/lib/workpattern/workpattern.rb +7 -2
- data/test/test_week.rb +95 -23
- metadata +8 -12
- data/lib/workpattern/day.rb +0 -343
- data/lib/workpattern/hour.rb +0 -206
- data/test/test_day.rb +0 -558
- data/test/test_hour.rb +0 -396
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
YmYzOTdhYWIzZTYwZjZmODY3NzBmYmQ5ZWY3MTI0MDcyNWUzNThhYw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MjZmZTg4OTViNzJjY2I0NGI1OTNlZjQ2NzYwZGNmZTg1ZDE4Y2JjZA==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZTIzNDljMTg5NTJmMzgyMGEwZTE2ZmM3OTUzNjVhNzRjYzIzMDAxYTE3ZWY1
|
10
|
+
YTJkYTRkZWQ5YjVmZTQ3N2E4YWVhZGNiZTFmNmQ2YzI1Yjc5MjY5MzU1YWRk
|
11
|
+
NTNmMDFjODY5ZTNkZDliZjBiYzY5ZDljMzkwYWY1NzA3ZjYyNDk=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
OWY5ZmM4Yzc0MmViNmY5YmZmMTE2YzFhMTVmMTA5Y2M5MzRmMmE0ZWM0YWVh
|
14
|
+
MzY3YWFkNjNlYTBkN2JjZjdmYjM3MTRhMDA4NTQ2YjQ1NGZkYmU3ZmQzMDE2
|
15
|
+
NjZlZDI5MzVjZjExNTY4YjVlZjMyMTA4ZjQxMDg0YmY2ZjE1NDI=
|
data/.travis.yml
CHANGED
data/CHANGELOG
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
## Workpattern v0.4.0 ( May 23, 2014) ##
|
2
|
+
|
3
|
+
* Updated Week class to use bits and removed Day and Hour class as a consequence * Barrie Callender *
|
4
|
+
* This resulted in a performance improvement and some new tests
|
5
|
+
|
1
6
|
## Workpattern v0.3.6 (Mar 25, 2014) ##
|
2
7
|
|
3
8
|
* total minutes of week is zero when short week starting after Sunday (#17) * Barrie Callender *
|
data/lib/workpattern.rb
CHANGED
data/lib/workpattern/version.rb
CHANGED
data/lib/workpattern/week.rb
CHANGED
@@ -1,113 +1,19 @@
|
|
1
1
|
module Workpattern
|
2
|
-
|
3
|
-
# @author Barrie Callender
|
4
|
-
# @!attribute values
|
5
|
-
# @return [Array] each day of the week
|
6
|
-
# @!attribute days
|
7
|
-
# @return [Integer] number of days in the week
|
8
|
-
# @!attribute start
|
9
|
-
# @return [DateTime] first date in the range
|
10
|
-
# @!attribute finish
|
11
|
-
# @return [DateTime] last date in the range
|
12
|
-
# @!attribute week_total
|
13
|
-
# @return [Integer] total number of minutes in a week
|
14
|
-
# @!attribute total
|
15
|
-
# @return [Integer] total number of minutes in the range
|
16
|
-
#
|
17
|
-
# Represents working and resting periods for each day in a week for a specified date range.
|
18
|
-
#
|
19
|
-
# @since 0.2.0
|
20
|
-
#
|
2
|
+
|
21
3
|
class Week
|
22
4
|
|
23
|
-
attr_accessor :values, :
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
# @param [DateTime] start first date in the range
|
28
|
-
# @param [DateTime] finish last date in the range
|
29
|
-
# @param [Integer] type working (1) or resting (0)
|
30
|
-
# @return [Week] newly initialised Week object
|
31
|
-
#
|
32
|
-
def initialize(start,finish,type=1)
|
33
|
-
hours_in_days_in_week=[24,24,24,24,24,24,24]
|
34
|
-
@days=hours_in_days_in_week.size
|
35
|
-
@values=Array.new(7) {|index| Day.new(type)}
|
5
|
+
attr_accessor :values, :hours_per_day, :start, :finish, :week_total, :total
|
6
|
+
|
7
|
+
def initialize(start,finish,type=1,hours_per_day=24)
|
8
|
+
@hours_per_day = hours_per_day
|
36
9
|
@start=DateTime.new(start.year,start.month,start.day)
|
37
10
|
@finish=DateTime.new(finish.year,finish.month,finish.day)
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
# Duplicates the current <tt>Week</tt> object
|
43
|
-
#
|
44
|
-
# @return [Week] a duplicated instance of the current <tt>Week</tt> object
|
45
|
-
#
|
46
|
-
def duplicate()
|
47
|
-
duplicate_week=Week.new(self.start,self.finish)
|
48
|
-
duplicate_values=Array.new(self.values.size)
|
49
|
-
self.values.each_index {|index|
|
50
|
-
duplicate_values[index]=self.values[index].duplicate
|
51
|
-
}
|
52
|
-
duplicate_week.values=duplicate_values
|
53
|
-
duplicate_week.days=self.days
|
54
|
-
duplicate_week.start=self.start
|
55
|
-
duplicate_week.finish=self.finish
|
56
|
-
duplicate_week.week_total=self.week_total
|
57
|
-
duplicate_week.total=self.total
|
58
|
-
duplicate_week.refresh
|
59
|
-
return duplicate_week
|
60
|
-
end
|
61
|
-
|
62
|
-
# Recalculates the attributes that define a <tt>Week</tt> object.
|
63
|
-
# This was made public for <tt>#duplicate</tt> to work
|
64
|
-
#
|
65
|
-
def refresh
|
66
|
-
set_attributes
|
67
|
-
end
|
68
|
-
|
69
|
-
# Changes the date range.
|
70
|
-
# This method calls <tt>#refresh</tt> to update the attributes.
|
71
|
-
#
|
72
|
-
# @param [DateTime] start is the new starting date for the <tt>Week</tt>
|
73
|
-
# @param [DateTime] finish is the new finish date for the <tt>Week</tt>
|
74
|
-
#
|
75
|
-
def adjust(start_date,finish_date)
|
76
|
-
self.start=DateTime.new(start_date.year,start_date.month,start_date.day)
|
77
|
-
self.finish=DateTime.new(finish_date.year,finish_date.month,finish_date.day)
|
78
|
-
refresh
|
79
|
-
end
|
80
|
-
|
81
|
-
# Sets a range of minutes in a week to be working or resting. The parameters supplied
|
82
|
-
# to this method determine exactly what should be changed
|
83
|
-
#
|
84
|
-
# @param [Hash(DAYNAMES)] days identifies the days to be included in the range
|
85
|
-
# @param [DateTime] from_time where the time portion is used to specify the first minute to be set
|
86
|
-
# @param [DateTime] to_time where the time portion is used to specify the last minute to be set
|
87
|
-
# @param [Integer] type where a 1 sets it to working and a 0 to resting
|
88
|
-
#
|
89
|
-
def workpattern(days,from_time,to_time,type)
|
90
|
-
DAYNAMES[days].each {|day| self.values[day].workpattern(from_time,to_time,type)}
|
91
|
-
refresh
|
92
|
-
end
|
93
|
-
|
94
|
-
# Calculates a new date by adding or subtracting a duration in minutes.
|
95
|
-
#
|
96
|
-
# @param [DateTime] start original date
|
97
|
-
# @param [Integer] duration minutes to add or subtract
|
98
|
-
# @param [Boolean] midnight flag used for subtraction that indicates the start date is midnight
|
99
|
-
#
|
100
|
-
def calc(start_date,duration, midnight=false)
|
101
|
-
return start_date,duration,false if duration==0
|
102
|
-
return add(start_date,duration) if duration > 0
|
103
|
-
return subtract(self.start,duration, midnight) if (self.total==0) && (duration <0)
|
104
|
-
return subtract(start_date,duration, midnight) if duration <0
|
11
|
+
@values = Array.new(6)
|
12
|
+
0.upto(6) do |i|
|
13
|
+
@values[i] = working_day * type
|
14
|
+
end
|
105
15
|
end
|
106
|
-
|
107
|
-
# Comparison Returns an integer (-1, 0, or +1) if week is less than, equal to, or greater than other_week
|
108
|
-
#
|
109
|
-
# @param [Week] other_week object to compare to
|
110
|
-
# @return [Integer] -1,0 or +1 if week is less than, equal to or greater than other_week
|
16
|
+
|
111
17
|
def <=>(other_week)
|
112
18
|
if self.start < other_week.start
|
113
19
|
return -1
|
@@ -117,268 +23,358 @@ module Workpattern
|
|
117
23
|
return 1
|
118
24
|
end
|
119
25
|
end
|
120
|
-
|
121
|
-
# Returns true if the supplied DateTime is working and false if resting
|
122
|
-
#
|
123
|
-
# @param [DateTime] start DateTime to be tested
|
124
|
-
# @return [Boolean] true if the minute is working otherwise false if it is a resting minute
|
125
|
-
#
|
126
|
-
def working?(start_date)
|
127
|
-
self.values[start_date.wday].working?(start_date)
|
128
|
-
end
|
129
|
-
|
130
|
-
# Returns the difference in minutes between two DateTime values.
|
131
|
-
#
|
132
|
-
# @param [DateTime] start starting DateTime
|
133
|
-
# @param [DateTime] finish ending DateTime
|
134
|
-
# @return [Integer, DateTime] number of minutes and start date for rest of calculation.
|
135
|
-
#
|
136
|
-
def diff(start_date,finish_date)
|
137
|
-
start_date,finish_date=finish_date,start_date if ((start_date <=> finish_date))==1
|
138
|
-
# calculate to end of day
|
139
|
-
#
|
140
|
-
if (start_date.jd==finish_date.jd) # same day
|
141
|
-
duration, start_date=self.values[start_date.wday].diff(start_date,finish_date)
|
142
|
-
elsif (finish_date.jd<=self.finish.jd) #within this week
|
143
|
-
duration, start_date=diff_detail(start_date,finish_date,finish_date)
|
144
|
-
else # after this week
|
145
|
-
duration, start_date=diff_detail(start_date,finish_date,self.finish)
|
146
|
-
end
|
147
|
-
return duration, start_date
|
148
|
-
end
|
149
|
-
|
26
|
+
|
150
27
|
def week_total
|
151
|
-
span_in_days > 6 ?
|
28
|
+
span_in_days > 6 ? full_week_total_minutes : part_week_total_minutes
|
152
29
|
end
|
153
30
|
|
154
31
|
def total
|
155
32
|
total_days = span_in_days
|
156
33
|
return week_total if total_days < 8
|
157
|
-
sum =
|
34
|
+
sum = sum_of_minutes_in_day_range(self.start.wday, 6)
|
158
35
|
total_days -= (7-self.start.wday)
|
159
|
-
sum +=
|
36
|
+
sum += sum_of_minutes_in_day_range(0,self.finish.wday)
|
160
37
|
total_days-=(self.finish.wday+1)
|
161
38
|
sum += week_total * total_days / 7
|
162
39
|
return sum
|
163
40
|
end
|
164
41
|
|
165
|
-
|
42
|
+
def workpattern(days,from_time,to_time,type)
|
43
|
+
DAYNAMES[days].each do |day|
|
44
|
+
type==1 ? work_on_day(day,from_time,to_time) : rest_on_day(day,from_time,to_time)
|
45
|
+
end
|
46
|
+
end
|
166
47
|
|
167
|
-
def
|
168
|
-
|
48
|
+
def duplicate()
|
49
|
+
duplicate_week=Week.new(self.start,self.finish)
|
50
|
+
0.upto(6).each do |i| duplicate_week.values[i] = @values[i] end
|
51
|
+
return duplicate_week
|
169
52
|
end
|
170
53
|
|
171
|
-
|
54
|
+
def calc(start_date,duration, midnight=false)
|
55
|
+
return start_date,duration,false if duration==0
|
56
|
+
return add(start_date,duration) if duration > 0
|
57
|
+
return subtract(self.start,duration, midnight) if (self.total==0) && (duration <0)
|
58
|
+
return subtract(start_date,duration, midnight) if duration <0
|
59
|
+
end
|
60
|
+
|
61
|
+
def working?(date)
|
62
|
+
return true if bit_pos_time(date) & @values[date.wday] > 0
|
63
|
+
false
|
64
|
+
end
|
65
|
+
|
66
|
+
def resting?(date)
|
67
|
+
!working?(date)
|
68
|
+
end
|
69
|
+
|
70
|
+
def diff(start_date,finish_date)
|
71
|
+
start_date,finish_date=finish_date,start_date if ((start_date <=> finish_date))==1
|
72
|
+
|
73
|
+
if (start_date.jd==finish_date.jd)
|
74
|
+
duration, start_date=diff_in_same_day(start_date, finish_date)
|
75
|
+
elsif (finish_date.jd<=self.finish.jd)
|
76
|
+
duration, start_date=diff_in_same_weekpattern(start_date,finish_date)
|
77
|
+
else
|
78
|
+
duration, start_date=diff_beyond_weekpattern(start_date,finish_date)
|
79
|
+
end
|
80
|
+
return duration, start_date
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
172
86
|
def span_in_days
|
173
87
|
(self.finish-self.start).to_i + 1
|
174
88
|
end
|
175
89
|
|
176
|
-
|
177
|
-
|
178
|
-
def set_attributes
|
179
|
-
|
90
|
+
def full_week_total_minutes
|
91
|
+
sum_of_minutes_in_day_range 0, 6
|
180
92
|
end
|
181
93
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
start.upto(finish) {|day|
|
191
|
-
total+=self.values[day].total
|
192
|
-
}
|
94
|
+
def part_week_total_minutes
|
95
|
+
|
96
|
+
if self.start.wday <= self.finish.wday
|
97
|
+
total = sum_of_minutes_in_day_range(self.start.wday, self.finish.wday)
|
98
|
+
else
|
99
|
+
total = sum_of_minutes_in_day_range(self.start.wday, 6)
|
100
|
+
total += sum_of_minutes_in_day_range(0, self.finish.wday)
|
101
|
+
end
|
193
102
|
return total
|
194
103
|
end
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
def
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
104
|
+
|
105
|
+
def sum_of_minutes_in_day_range(first,last)
|
106
|
+
@values[first..last].inject(0) {|sum,item| sum + item.to_s(2).count('1')}
|
107
|
+
end
|
108
|
+
|
109
|
+
def work_on_day(day,from_time,to_time)
|
110
|
+
self.values[day] = self.values[day] | time_mask(from_time, to_time)
|
111
|
+
end
|
112
|
+
|
113
|
+
def rest_on_day(day,from_time,to_time)
|
114
|
+
mask_of_1s = time_mask(from_time, to_time)
|
115
|
+
mask = mask_of_1s ^ working_day & working_day
|
116
|
+
self.values[day] = self.values[day] & mask
|
117
|
+
end
|
118
|
+
|
119
|
+
def time_mask(from_time, to_time)
|
120
|
+
bit_pos_above_time(to_time) - bit_pos_time(from_time)
|
121
|
+
end
|
122
|
+
|
123
|
+
def bit_pos_above_time(time)
|
124
|
+
bit_pos(time.hour, time.min+1)
|
125
|
+
end
|
126
|
+
|
127
|
+
def bit_pos(hour,minute)
|
128
|
+
2**( (hour * 60) + minute )
|
129
|
+
end
|
130
|
+
|
131
|
+
def bit_pos_time(time)
|
132
|
+
bit_pos(time.hour,time.min)
|
133
|
+
end
|
134
|
+
|
135
|
+
def add(initial_date,duration)
|
136
|
+
|
137
|
+
initial_date, duration = add_to_end_of_day(initial_date,duration)
|
138
|
+
|
139
|
+
while ( duration != 0) && (initial_date.wday != self.finish.next_day.wday) && (initial_date.jd <= self.finish.jd)
|
140
|
+
initial_date, duration = add_to_end_of_day(initial_date,duration)
|
219
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
|
220
152
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
153
|
+
end
|
154
|
+
|
155
|
+
def add_to_end_of_day(initial_date, duration)
|
156
|
+
available_minutes_in_day = minutes_to_end_of_day(initial_date)
|
157
|
+
|
158
|
+
if available_minutes_in_day < duration
|
159
|
+
duration -= available_minutes_in_day
|
160
|
+
initial_date = start_of_next_day(initial_date)
|
161
|
+
elsif available_minutes_in_day == duration
|
162
|
+
duration -= available_minutes_in_day
|
163
|
+
initial_date = end_of_this_day(initial_date)
|
164
|
+
else
|
165
|
+
initial_date = consume_minutes(initial_date,duration)
|
166
|
+
duration=0
|
227
167
|
end
|
168
|
+
return initial_date, duration
|
169
|
+
end
|
228
170
|
|
229
|
-
|
171
|
+
def minutes_to_end_of_day(date)
|
172
|
+
pattern_to_end_of_day(date).to_s(2).count('1')
|
173
|
+
end
|
230
174
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
175
|
+
def pattern_to_end_of_day(date)
|
176
|
+
mask = mask_to_end_of_day(date)
|
177
|
+
(self.values[date.wday] & mask)
|
178
|
+
end
|
179
|
+
|
180
|
+
def mask_to_end_of_day(date)
|
181
|
+
bit_pos(self.hours_per_day,0) - bit_pos(date.hour, date.min)
|
182
|
+
end
|
183
|
+
|
184
|
+
def working_day
|
185
|
+
2**(60*self.hours_per_day)-1
|
186
|
+
end
|
187
|
+
|
188
|
+
def start_of_next_day(date)
|
189
|
+
date.next_day - (HOUR * date.hour) - (MINUTE * date.minute)
|
190
|
+
end
|
191
|
+
|
192
|
+
def start_of_previous_day(date)
|
193
|
+
start_of_next_day(date).prev_day.prev_day
|
194
|
+
end
|
195
|
+
|
196
|
+
def start_of_today(date)
|
197
|
+
start_of_next_day(date.prev_day)
|
198
|
+
end
|
199
|
+
|
200
|
+
def end_of_this_day(date)
|
201
|
+
position = pattern_to_end_of_day(date).to_s(2).size
|
202
|
+
return adjust_date(date,position)
|
203
|
+
end
|
204
|
+
|
205
|
+
def adjust_date(date,adjustment)
|
206
|
+
date - (HOUR * date.hour) - (MINUTE * date.min) + (MINUTE * adjustment)
|
207
|
+
end
|
208
|
+
|
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
|
+
def mask_to_start_of_day(date)
|
215
|
+
bit_pos(date.hour, date.min) - bit_pos(0,0)
|
242
216
|
end
|
243
217
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
# @param [Boolean] midnight flag indicates the start date is midnight when true
|
249
|
-
# @return [DateTime, Integer, Boolean] the calculated date, remaining number of minutes and
|
250
|
-
# true if the time is midnight on the date
|
251
|
-
#
|
252
|
-
def subtract(start,duration,midnight=false)
|
253
|
-
|
254
|
-
# Handle subtraction from start of day
|
255
|
-
if midnight
|
256
|
-
start,duration=minute_b4_midnight(start,duration)
|
257
|
-
midnight=false
|
258
|
-
end
|
218
|
+
def pattern_to_start_of_day(date)
|
219
|
+
mask = mask_to_start_of_day(date)
|
220
|
+
(self.values[date.wday] & mask)
|
221
|
+
end
|
259
222
|
|
260
|
-
|
261
|
-
|
223
|
+
def minutes_to_start_of_day(date)
|
224
|
+
pattern_to_start_of_day(date).to_s(2).count('1')
|
225
|
+
end
|
226
|
+
|
227
|
+
def consume_minutes(date,duration)
|
228
|
+
|
229
|
+
minutes=pattern_to_end_of_day(date).to_s(2).reverse! if duration > 0
|
230
|
+
minutes=pattern_to_start_of_day(date).to_s(2) if duration < 0
|
262
231
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
elsif midnight
|
267
|
-
return start,duration,midnight
|
268
|
-
elsif (duration==0) || (start.jd ==self.start.jd)
|
269
|
-
return start,duration, midnight
|
270
|
-
end
|
232
|
+
top=minutes.size
|
233
|
+
bottom=1
|
234
|
+
mark = top / 2
|
271
235
|
|
272
|
-
|
273
|
-
|
236
|
+
while minutes[0,mark].count('1') != duration.abs
|
237
|
+
last_mark = mark
|
238
|
+
if minutes[0,mark].count('1') < duration.abs
|
239
|
+
|
240
|
+
bottom = mark
|
241
|
+
mark = (top-mark) / 2 + mark
|
242
|
+
mark = top if last_mark == mark
|
274
243
|
|
275
|
-
if (duration.abs>=self.values[start.wday].total)
|
276
|
-
duration = duration + self.values[start.wday].total
|
277
|
-
start=start.prev_day
|
278
244
|
else
|
279
|
-
|
280
|
-
|
281
|
-
|
245
|
+
|
246
|
+
top = mark
|
247
|
+
mark = (mark-bottom) / 2 + bottom
|
248
|
+
mark = bottom if last_mark = mark
|
249
|
+
|
250
|
+
end
|
282
251
|
end
|
283
252
|
|
284
|
-
|
253
|
+
mark = minutes_addition_adjustment(minutes, mark) if duration > 0
|
254
|
+
mark = minutes_subtraction_adjustment(minutes,mark) if duration < 0
|
255
|
+
|
256
|
+
return adjust_date(date, mark) if duration > 0
|
257
|
+
return start_of_today(date) + (MINUTE * mark) if duration < 0
|
258
|
+
|
259
|
+
end
|
260
|
+
|
261
|
+
def minutes_subtraction_adjustment(minutes,mark)
|
262
|
+
i = mark - 1
|
263
|
+
|
264
|
+
while minutes[i]=='0'
|
265
|
+
i-=1
|
266
|
+
end
|
267
|
+
|
268
|
+
minutes.size - (i + 1)
|
269
|
+
end
|
270
|
+
|
271
|
+
def minutes_addition_adjustment(minutes,mark)
|
272
|
+
minutes=minutes[0,mark]
|
285
273
|
|
286
|
-
|
287
|
-
|
288
|
-
duration=duration + self.week_total
|
289
|
-
start=start-7
|
274
|
+
while minutes[minutes.size-1]=='0'
|
275
|
+
minutes.chop!
|
290
276
|
end
|
291
277
|
|
292
|
-
|
278
|
+
minutes.size
|
279
|
+
end
|
280
|
+
|
281
|
+
def subtract_to_start_of_day(initial_date, duration, midnight)
|
282
|
+
|
283
|
+
initial_date,duration, midnight = handle_midnight(initial_date, duration) if midnight
|
284
|
+
available_minutes_in_day = minutes_to_start_of_day(initial_date)
|
293
285
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
286
|
+
if duration != 0
|
287
|
+
if available_minutes_in_day < duration.abs
|
288
|
+
duration += available_minutes_in_day
|
289
|
+
initial_date = start_of_previous_day(initial_date)
|
290
|
+
midnight = true
|
299
291
|
else
|
300
|
-
|
301
|
-
|
292
|
+
initial_date = consume_minutes(initial_date,duration)
|
293
|
+
duration = 0
|
294
|
+
midnight = false
|
302
295
|
end
|
303
|
-
end
|
304
|
-
|
305
|
-
return start, duration , midnight
|
306
|
-
|
307
|
-
end
|
308
|
-
|
309
|
-
# Supports calculating from midnight by updating the given duration depending on whether the
|
310
|
-
# last minute in the day is resting or working. It then sets the time to this minute.
|
311
|
-
#
|
312
|
-
# @param [DateTime] start is the date whose midnight is to be used as the start date
|
313
|
-
# @param [Integer] duration is the number of minutes to subtract
|
314
|
-
# @return [DateTime, Integer] the date with a time of 23:59 and remaining duration
|
315
|
-
# adjusted according to whether 23:59 is resting or not
|
316
|
-
#
|
317
|
-
def minute_b4_midnight(start,duration)
|
318
|
-
start -= start.hour * HOUR
|
319
|
-
start -= start.min * MINUTE
|
320
|
-
duration += self.values[start.wday].minutes(23,59,23,59)
|
321
|
-
start = start.next_day - MINUTE
|
322
|
-
return start,duration
|
323
|
-
end
|
324
|
-
|
325
|
-
# Calculates the date and time after the last working minute of the current date
|
326
|
-
#
|
327
|
-
# @param [DateTime] start is the current date
|
328
|
-
# @return [DateTime] the new date
|
329
|
-
#
|
330
|
-
def after_last_work(start_date)
|
331
|
-
if self.values[start_date.wday].last_hour.nil?
|
332
|
-
return start_date.next_day
|
333
|
-
else
|
334
|
-
start_date = start_date + HOUR * (self.values[start_date.wday].last_hour - start_date.hour)
|
335
|
-
start_date = start_date + MINUTE * (self.values[start_date.wday].last_min - start_date.min + 1)
|
336
|
-
return start_date
|
337
|
-
end
|
296
|
+
end
|
297
|
+
return initial_date, duration, midnight
|
338
298
|
end
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
# @return [DateTime, Integer] new date for rest of calculation and total number of minutes calculated thus far.
|
346
|
-
#
|
347
|
-
def diff_detail(start_date,finish_date,finish_on_date)
|
348
|
-
|
349
|
-
duration, start_date=diff_in_day(start_date, finish_date)
|
350
|
-
return duration,start_date if start_date > finish_on_date
|
299
|
+
|
300
|
+
|
301
|
+
def handle_midnight(initial_date,duration)
|
302
|
+
if working?(start_of_next_day(initial_date) - MINUTE)
|
303
|
+
duration += 1
|
304
|
+
end
|
351
305
|
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
306
|
+
initial_date -= (HOUR * initial_date.hour)
|
307
|
+
initial_date -= (MINUTE * initial_date.min)
|
308
|
+
initial_date = initial_date.next_day - MINUTE
|
309
|
+
|
310
|
+
return initial_date, duration, false
|
311
|
+
end
|
312
|
+
|
313
|
+
|
314
|
+
def subtract(initial_date, duration, midnight)
|
315
|
+
initial_date,duration, midnight = handle_midnight(initial_date, duration) if midnight
|
316
|
+
|
317
|
+
initial_date, duration, midnight = subtract_to_start_of_day(initial_date, duration, midnight)
|
318
|
+
|
319
|
+
while ( duration != 0) && (initial_date.wday != self.start.prev_day.wday) && (initial_date.jd >= self.start.jd)
|
320
|
+
initial_date, duration, midnight = subtract_to_start_of_day(initial_date,duration, midnight)
|
356
321
|
end
|
357
322
|
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
start_date+=7
|
323
|
+
while (duration != 0) && (duration >= self.week_total) && ((initial_date.jd - 6) >= self.start.jd)
|
324
|
+
duration += self.week_total
|
325
|
+
initial_date -= 7
|
362
326
|
end
|
363
327
|
|
364
|
-
|
365
|
-
|
366
|
-
duration+=day_total(start_date)
|
367
|
-
start_date=start_date.next_day
|
328
|
+
while (duration != 0) && (initial_date.jd >= self.start.jd)
|
329
|
+
initial_date, duration, midnight = subtract_to_start_of_day(initial_date,duration, midnight)
|
368
330
|
end
|
369
331
|
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
332
|
+
return initial_date, duration, midnight
|
333
|
+
|
334
|
+
end
|
335
|
+
|
336
|
+
def diff_in_same_weekpattern(start_date, finish_date)
|
337
|
+
duration, start_date = diff_to_tomorrow(start_date)
|
338
|
+
while true
|
339
|
+
break if (start_date.wday == (self.finish.wday + 1))
|
340
|
+
break if (start_date.jd == self.finish.jd)
|
341
|
+
break if (start_date.jd == finish_date.jd)
|
342
|
+
duration += minutes_to_end_of_day(start_date)
|
343
|
+
start_date = start_of_next_day(start_date)
|
344
|
+
end
|
345
|
+
|
346
|
+
while true
|
347
|
+
break if ((start_date + 7) > finish_date)
|
348
|
+
break if ((start_date + 6).jd > self.finish.jd)
|
349
|
+
duration += week_total
|
350
|
+
start_date += 7
|
351
|
+
end
|
352
|
+
|
353
|
+
while true
|
354
|
+
break if (start_date.jd >= self.finish.jd)
|
355
|
+
break if (start_date.jd >= finish_date.jd)
|
356
|
+
duration += minutes_to_end_of_day(start_date)
|
357
|
+
start_date = start_of_next_day(start_date)
|
358
|
+
end
|
359
|
+
|
360
|
+
interim_duration, start_date = diff_in_same_day(start_date, finish_date) if (start_date < self.finish)
|
361
|
+
duration += interim_duration unless interim_duration.nil?
|
362
|
+
return duration, start_date
|
374
363
|
end
|
375
364
|
|
376
|
-
def
|
377
|
-
|
365
|
+
def diff_beyond_weekpattern(start_date,finish_date)
|
366
|
+
duration, start_date = diff_in_same_weekpattern(start_date, finish_date)
|
367
|
+
return duration, start_date
|
368
|
+
end
|
369
|
+
|
370
|
+
def diff_to_tomorrow(start_date)
|
371
|
+
mask = bit_pos(self.hours_per_day, 0) - bit_pos(start_date.hour, start_date.min)
|
372
|
+
return (self.values[start_date.wday] & mask).to_s(2).count('1'), start_of_next_day(start_date)
|
378
373
|
end
|
379
374
|
|
380
|
-
def
|
381
|
-
|
375
|
+
def diff_in_same_day(start_date, finish_date)
|
376
|
+
mask = bit_pos(finish_date.hour, finish_date.min) - bit_pos(start_date.hour, start_date.min)
|
377
|
+
return (self.values[start_date.wday] & mask).to_s(2).count('1'), finish_date
|
382
378
|
end
|
383
379
|
|
384
380
|
end
|