Filename | /mnt/catalyst/koha/Koha/Calendar.pm |
Statements | Executed 20 statements in 3.29ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1 | 1 | 1 | 4.07ms | 27.6ms | BEGIN@7 | Koha::Calendar::
1 | 1 | 1 | 3.19ms | 3.44ms | BEGIN@11 | Koha::Calendar::
1 | 1 | 1 | 470µs | 482µs | BEGIN@2 | Koha::Calendar::
1 | 1 | 1 | 9µs | 9µs | BEGIN@4 | Koha::Calendar::
1 | 1 | 1 | 8µs | 11µs | BEGIN@9 | Koha::Calendar::
1 | 1 | 1 | 7µs | 11µs | BEGIN@3 | Koha::Calendar::
1 | 1 | 1 | 7µs | 43µs | BEGIN@10 | Koha::Calendar::
1 | 1 | 1 | 7µs | 7µs | BEGIN@8 | Koha::Calendar::
1 | 1 | 1 | 6µs | 6µs | BEGIN@6 | Koha::Calendar::
0 | 0 | 0 | 0s | 0s | _init | Koha::Calendar::
0 | 0 | 0 | 0s | 0s | addDate | Koha::Calendar::
0 | 0 | 0 | 0s | 0s | addDays | Koha::Calendar::
0 | 0 | 0 | 0s | 0s | addHours | Koha::Calendar::
0 | 0 | 0 | 0s | 0s | add_holiday | Koha::Calendar::
0 | 0 | 0 | 0s | 0s | clear_weekly_closed_days | Koha::Calendar::
0 | 0 | 0 | 0s | 0s | days_between | Koha::Calendar::
0 | 0 | 0 | 0s | 0s | exception_holidays | Koha::Calendar::
0 | 0 | 0 | 0s | 0s | hours_between | Koha::Calendar::
0 | 0 | 0 | 0s | 0s | is_holiday | Koha::Calendar::
0 | 0 | 0 | 0s | 0s | new | Koha::Calendar::
0 | 0 | 0 | 0s | 0s | next_open_day | Koha::Calendar::
0 | 0 | 0 | 0s | 0s | prev_open_day | Koha::Calendar::
0 | 0 | 0 | 0s | 0s | set_daysmode | Koha::Calendar::
0 | 0 | 0 | 0s | 0s | single_holidays | Koha::Calendar::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | package Koha::Calendar; | ||||
2 | 2 | 27µs | 2 | 495µs | # spent 482µs (470+12) within Koha::Calendar::BEGIN@2 which was called:
# once (470µs+12µs) by C4::Circulation::BEGIN@49 at line 2 # spent 482µs making 1 call to Koha::Calendar::BEGIN@2
# spent 12µs making 1 call to strict::import |
3 | 2 | 19µs | 2 | 16µs | # spent 11µs (7+4) within Koha::Calendar::BEGIN@3 which was called:
# once (7µs+4µs) by C4::Circulation::BEGIN@49 at line 3 # spent 11µs making 1 call to Koha::Calendar::BEGIN@3
# spent 4µs making 1 call to warnings::import |
4 | 2 | 34µs | 1 | 9µs | # spent 9µs within Koha::Calendar::BEGIN@4 which was called:
# once (9µs+0s) by C4::Circulation::BEGIN@49 at line 4 # spent 9µs making 1 call to Koha::Calendar::BEGIN@4 |
5 | |||||
6 | 2 | 21µs | 1 | 6µs | # spent 6µs within Koha::Calendar::BEGIN@6 which was called:
# once (6µs+0s) by C4::Circulation::BEGIN@49 at line 6 # spent 6µs making 1 call to Koha::Calendar::BEGIN@6 |
7 | 2 | 709µs | 1 | 27.6ms | # spent 27.6ms (4.07+23.5) within Koha::Calendar::BEGIN@7 which was called:
# once (4.07ms+23.5ms) by C4::Circulation::BEGIN@49 at line 7 # spent 27.6ms making 1 call to Koha::Calendar::BEGIN@7 |
8 | 2 | 21µs | 1 | 7µs | # spent 7µs within Koha::Calendar::BEGIN@8 which was called:
# once (7µs+0s) by C4::Circulation::BEGIN@49 at line 8 # spent 7µs making 1 call to Koha::Calendar::BEGIN@8 |
9 | 2 | 21µs | 2 | 14µs | # spent 11µs (8+3) within Koha::Calendar::BEGIN@9 which was called:
# once (8µs+3µs) by C4::Circulation::BEGIN@49 at line 9 # spent 11µs making 1 call to Koha::Calendar::BEGIN@9
# spent 3µs making 1 call to C4::Context::import |
10 | 2 | 21µs | 2 | 79µs | # spent 43µs (7+36) within Koha::Calendar::BEGIN@10 which was called:
# once (7µs+36µs) by C4::Circulation::BEGIN@49 at line 10 # spent 43µs making 1 call to Koha::Calendar::BEGIN@10
# spent 36µs making 1 call to Exporter::import |
11 | 2 | 2.41ms | 2 | 3.46ms | # spent 3.44ms (3.19+242µs) within Koha::Calendar::BEGIN@11 which was called:
# once (3.19ms+242µs) by C4::Circulation::BEGIN@49 at line 11 # spent 3.44ms making 1 call to Koha::Calendar::BEGIN@11
# spent 24µs making 1 call to Exporter::import |
12 | |||||
13 | sub new { | ||||
14 | my ( $classname, %options ) = @_; | ||||
15 | my $self = {}; | ||||
16 | bless $self, $classname; | ||||
17 | for my $o_name ( keys %options ) { | ||||
18 | my $o = lc $o_name; | ||||
19 | $self->{$o} = $options{$o_name}; | ||||
20 | } | ||||
21 | if ( !defined $self->{branchcode} ) { | ||||
22 | croak 'No branchcode argument passed to Koha::Calendar->new'; | ||||
23 | } | ||||
24 | $self->_init(); | ||||
25 | return $self; | ||||
26 | } | ||||
27 | |||||
28 | sub _init { | ||||
29 | my $self = shift; | ||||
30 | my $branch = $self->{branchcode}; | ||||
31 | my $dbh = C4::Context->dbh(); | ||||
32 | my $weekly_closed_days_sth = $dbh->prepare( | ||||
33 | 'SELECT weekday FROM repeatable_holidays WHERE branchcode = ? AND weekday IS NOT NULL' | ||||
34 | ); | ||||
35 | $weekly_closed_days_sth->execute( $branch ); | ||||
36 | $self->{weekly_closed_days} = [ 0, 0, 0, 0, 0, 0, 0 ]; | ||||
37 | Readonly::Scalar my $sunday => 7; | ||||
38 | while ( my $tuple = $weekly_closed_days_sth->fetchrow_hashref ) { | ||||
39 | $self->{weekly_closed_days}->[ $tuple->{weekday} ] = 1; | ||||
40 | } | ||||
41 | my $day_month_closed_days_sth = $dbh->prepare( | ||||
42 | 'SELECT day, month FROM repeatable_holidays WHERE branchcode = ? AND weekday IS NULL' | ||||
43 | ); | ||||
44 | $day_month_closed_days_sth->execute( $branch ); | ||||
45 | $self->{day_month_closed_days} = {}; | ||||
46 | while ( my $tuple = $day_month_closed_days_sth->fetchrow_hashref ) { | ||||
47 | $self->{day_month_closed_days}->{ $tuple->{month} }->{ $tuple->{day} } = | ||||
48 | 1; | ||||
49 | } | ||||
50 | |||||
51 | $self->{days_mode} = C4::Context->preference('useDaysMode'); | ||||
52 | $self->{test} = 0; | ||||
53 | return; | ||||
54 | } | ||||
55 | |||||
56 | |||||
57 | # FIXME: use of package-level variables for caching the holiday | ||||
58 | # lists breaks persistance engines. As of 2013-12-10, the RM | ||||
59 | # is allowing this with the expectation that prior to release of | ||||
60 | # 3.16, bug 8089 will be fixed and we can switch the caching over | ||||
61 | # to Koha::Cache. | ||||
62 | 1 | 200ns | our ( $exception_holidays, $single_holidays ); | ||
63 | sub exception_holidays { | ||||
64 | my ( $self ) = @_; | ||||
65 | my $dbh = C4::Context->dbh; | ||||
66 | my $branch = $self->{branchcode}; | ||||
67 | if ( $exception_holidays ) { | ||||
68 | $self->{exception_holidays} = $exception_holidays; | ||||
69 | return $exception_holidays; | ||||
70 | } | ||||
71 | my $exception_holidays_sth = $dbh->prepare( | ||||
72 | 'SELECT day, month, year FROM special_holidays WHERE branchcode = ? AND isexception = 1' | ||||
73 | ); | ||||
74 | $exception_holidays_sth->execute( $branch ); | ||||
75 | my $dates = []; | ||||
76 | while ( my ( $day, $month, $year ) = $exception_holidays_sth->fetchrow ) { | ||||
77 | push @{$dates}, | ||||
78 | DateTime->new( | ||||
79 | day => $day, | ||||
80 | month => $month, | ||||
81 | year => $year, | ||||
82 | time_zone => C4::Context->tz() | ||||
83 | )->truncate( to => 'day' ); | ||||
84 | } | ||||
85 | $self->{exception_holidays} = | ||||
86 | DateTime::Set->from_datetimes( dates => $dates ); | ||||
87 | $exception_holidays = $self->{exception_holidays}; | ||||
88 | return $exception_holidays; | ||||
89 | } | ||||
90 | |||||
91 | sub single_holidays { | ||||
92 | my ( $self ) = @_; | ||||
93 | my $dbh = C4::Context->dbh; | ||||
94 | my $branch = $self->{branchcode}; | ||||
95 | if ( $single_holidays ) { | ||||
96 | $self->{single_holidays} = $single_holidays; | ||||
97 | return $single_holidays; | ||||
98 | } | ||||
99 | my $single_holidays_sth = $dbh->prepare( | ||||
100 | 'SELECT day, month, year FROM special_holidays WHERE branchcode = ? AND isexception = 0' | ||||
101 | ); | ||||
102 | $single_holidays_sth->execute( $branch ); | ||||
103 | my $dates = []; | ||||
104 | while ( my ( $day, $month, $year ) = $single_holidays_sth->fetchrow ) { | ||||
105 | push @{$dates}, | ||||
106 | DateTime->new( | ||||
107 | day => $day, | ||||
108 | month => $month, | ||||
109 | year => $year, | ||||
110 | time_zone => C4::Context->tz() | ||||
111 | )->truncate( to => 'day' ); | ||||
112 | } | ||||
113 | $self->{single_holidays} = DateTime::Set->from_datetimes( dates => $dates ); | ||||
114 | $single_holidays = $self->{single_holidays}; | ||||
115 | return $single_holidays; | ||||
116 | } | ||||
117 | sub addDate { | ||||
118 | my ( $self, $startdate, $add_duration, $unit ) = @_; | ||||
119 | |||||
120 | # Default to days duration (legacy support I guess) | ||||
121 | if ( ref $add_duration ne 'DateTime::Duration' ) { | ||||
122 | $add_duration = DateTime::Duration->new( days => $add_duration ); | ||||
123 | } | ||||
124 | |||||
125 | $unit ||= 'days'; # default days ? | ||||
126 | my $dt; | ||||
127 | |||||
128 | if ( $unit eq 'hours' ) { | ||||
129 | # Fixed for legacy support. Should be set as a branch parameter | ||||
130 | Readonly::Scalar my $return_by_hour => 10; | ||||
131 | |||||
132 | $dt = $self->addHours($startdate, $add_duration, $return_by_hour); | ||||
133 | } else { | ||||
134 | # days | ||||
135 | $dt = $self->addDays($startdate, $add_duration); | ||||
136 | } | ||||
137 | |||||
138 | return $dt; | ||||
139 | } | ||||
140 | |||||
141 | sub addHours { | ||||
142 | my ( $self, $startdate, $hours_duration, $return_by_hour ) = @_; | ||||
143 | my $base_date = $startdate->clone(); | ||||
144 | |||||
145 | $base_date->add_duration($hours_duration); | ||||
146 | |||||
147 | # If we are using the calendar behave for now as if Datedue | ||||
148 | # was the chosen option (current intended behaviour) | ||||
149 | |||||
150 | if ( $self->{days_mode} ne 'Days' && | ||||
151 | $self->is_holiday($base_date) ) { | ||||
152 | |||||
153 | if ( $hours_duration->is_negative() ) { | ||||
154 | $base_date = $self->prev_open_day($base_date); | ||||
155 | } else { | ||||
156 | $base_date = $self->next_open_day($base_date); | ||||
157 | } | ||||
158 | |||||
159 | $base_date->set_hour($return_by_hour); | ||||
160 | |||||
161 | } | ||||
162 | |||||
163 | return $base_date; | ||||
164 | } | ||||
165 | |||||
166 | sub addDays { | ||||
167 | my ( $self, $startdate, $days_duration ) = @_; | ||||
168 | my $base_date = $startdate->clone(); | ||||
169 | |||||
170 | if ( $self->{days_mode} eq 'Calendar' ) { | ||||
171 | # use the calendar to skip all days the library is closed | ||||
172 | # when adding | ||||
173 | my $days = abs $days_duration->in_units('days'); | ||||
174 | |||||
175 | if ( $days_duration->is_negative() ) { | ||||
176 | while ($days) { | ||||
177 | $base_date = $self->prev_open_day($base_date); | ||||
178 | --$days; | ||||
179 | } | ||||
180 | } else { | ||||
181 | while ($days) { | ||||
182 | $base_date = $self->next_open_day($base_date); | ||||
183 | --$days; | ||||
184 | } | ||||
185 | } | ||||
186 | |||||
187 | } else { # Days or Datedue | ||||
188 | # use straight days, then use calendar to push | ||||
189 | # the date to the next open day if Datedue | ||||
190 | $base_date->add_duration($days_duration); | ||||
191 | |||||
192 | if ( $self->{days_mode} eq 'Datedue' ) { | ||||
193 | # Datedue, then use the calendar to push | ||||
194 | # the date to the next open day if holiday | ||||
195 | if ( $self->is_holiday($base_date) ) { | ||||
196 | if ( $days_duration->is_negative() ) { | ||||
197 | $base_date = $self->prev_open_day($base_date); | ||||
198 | } else { | ||||
199 | $base_date = $self->next_open_day($base_date); | ||||
200 | } | ||||
201 | } | ||||
202 | } | ||||
203 | } | ||||
204 | |||||
205 | return $base_date; | ||||
206 | } | ||||
207 | |||||
208 | sub is_holiday { | ||||
209 | my ( $self, $dt ) = @_; | ||||
210 | my $localdt = $dt->clone(); | ||||
211 | my $day = $localdt->day; | ||||
212 | my $month = $localdt->month; | ||||
213 | |||||
214 | $localdt->truncate( to => 'day' ); | ||||
215 | |||||
216 | if ( $self->exception_holidays->contains($localdt) ) { | ||||
217 | # exceptions are not holidays | ||||
218 | return 0; | ||||
219 | } | ||||
220 | |||||
221 | my $dow = $localdt->day_of_week; | ||||
222 | # Representation fix | ||||
223 | # TODO: Shouldn't we shift the rest of the $dow also? | ||||
224 | if ( $dow == 7 ) { | ||||
225 | $dow = 0; | ||||
226 | } | ||||
227 | |||||
228 | if ( $self->{weekly_closed_days}->[$dow] == 1 ) { | ||||
229 | return 1; | ||||
230 | } | ||||
231 | |||||
232 | if ( exists $self->{day_month_closed_days}->{$month}->{$day} ) { | ||||
233 | return 1; | ||||
234 | } | ||||
235 | |||||
236 | if ( $self->single_holidays->contains($localdt) ) { | ||||
237 | return 1; | ||||
238 | } | ||||
239 | |||||
240 | # damn have to go to work after all | ||||
241 | return 0; | ||||
242 | } | ||||
243 | |||||
244 | sub next_open_day { | ||||
245 | my ( $self, $dt ) = @_; | ||||
246 | my $base_date = $dt->clone(); | ||||
247 | |||||
248 | $base_date->add(days => 1); | ||||
249 | |||||
250 | while ($self->is_holiday($base_date)) { | ||||
251 | $base_date->add(days => 1); | ||||
252 | } | ||||
253 | |||||
254 | return $base_date; | ||||
255 | } | ||||
256 | |||||
257 | sub prev_open_day { | ||||
258 | my ( $self, $dt ) = @_; | ||||
259 | my $base_date = $dt->clone(); | ||||
260 | |||||
261 | $base_date->add(days => -1); | ||||
262 | |||||
263 | while ($self->is_holiday($base_date)) { | ||||
264 | $base_date->add(days => -1); | ||||
265 | } | ||||
266 | |||||
267 | return $base_date; | ||||
268 | } | ||||
269 | |||||
270 | sub days_between { | ||||
271 | my $self = shift; | ||||
272 | my $start_dt = shift; | ||||
273 | my $end_dt = shift; | ||||
274 | |||||
275 | if ( $start_dt->compare($end_dt) > 0 ) { | ||||
276 | # swap dates | ||||
277 | my $int_dt = $end_dt; | ||||
278 | $end_dt = $start_dt; | ||||
279 | $start_dt = $int_dt; | ||||
280 | } | ||||
281 | |||||
282 | |||||
283 | # start and end should not be closed days | ||||
284 | my $days = $start_dt->delta_days($end_dt)->delta_days; | ||||
285 | for (my $dt = $start_dt->clone(); | ||||
286 | $dt <= $end_dt; | ||||
287 | $dt->add(days => 1) | ||||
288 | ) { | ||||
289 | if ($self->is_holiday($dt)) { | ||||
290 | $days--; | ||||
291 | } | ||||
292 | } | ||||
293 | return DateTime::Duration->new( days => $days ); | ||||
294 | |||||
295 | } | ||||
296 | |||||
297 | sub hours_between { | ||||
298 | my ($self, $start_date, $end_date) = @_; | ||||
299 | my $start_dt = $start_date->clone(); | ||||
300 | my $end_dt = $end_date->clone(); | ||||
301 | my $duration = $end_dt->delta_ms($start_dt); | ||||
302 | $start_dt->truncate( to => 'day' ); | ||||
303 | $end_dt->truncate( to => 'day' ); | ||||
304 | # NB this is a kludge in that it assumes all days are 24 hours | ||||
305 | # However for hourly loans the logic should be expanded to | ||||
306 | # take into account open/close times then it would be a duration | ||||
307 | # of library open hours | ||||
308 | my $skipped_days = 0; | ||||
309 | for (my $dt = $start_dt->clone(); | ||||
310 | $dt <= $end_dt; | ||||
311 | $dt->add(days => 1) | ||||
312 | ) { | ||||
313 | if ($self->is_holiday($dt)) { | ||||
314 | ++$skipped_days; | ||||
315 | } | ||||
316 | } | ||||
317 | if ($skipped_days) { | ||||
318 | $duration->subtract_duration(DateTime::Duration->new( hours => 24 * $skipped_days)); | ||||
319 | } | ||||
320 | |||||
321 | return $duration; | ||||
322 | |||||
323 | } | ||||
324 | |||||
325 | sub set_daysmode { | ||||
326 | my ( $self, $mode ) = @_; | ||||
327 | |||||
328 | # if not testing this is a no op | ||||
329 | if ( $self->{test} ) { | ||||
330 | $self->{days_mode} = $mode; | ||||
331 | } | ||||
332 | |||||
333 | return; | ||||
334 | } | ||||
335 | |||||
336 | sub clear_weekly_closed_days { | ||||
337 | my $self = shift; | ||||
338 | $self->{weekly_closed_days} = [ 0, 0, 0, 0, 0, 0, 0 ]; # Sunday only | ||||
339 | return; | ||||
340 | } | ||||
341 | |||||
342 | sub add_holiday { | ||||
343 | my $self = shift; | ||||
344 | my $new_dt = shift; | ||||
345 | my @dt = $self->single_holidays->as_list; | ||||
346 | push @dt, $new_dt; | ||||
347 | $self->{single_holidays} = | ||||
348 | DateTime::Set->from_datetimes( dates => \@dt ); | ||||
349 | $single_holidays = $self->{single_holidays}; | ||||
350 | |||||
351 | return; | ||||
352 | } | ||||
353 | |||||
354 | 1 | 3µs | 1; | ||
355 | __END__ |