workpattern 0.3.6 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|