← Index
NYTProf Performance Profile   « line view »
For svc/members/upsert
  Run on Tue Jan 13 11:50:22 2015
Reported on Tue Jan 13 12:09:50 2015

Filename/mnt/catalyst/koha/Koha/Calendar.pm
StatementsExecuted 20 statements in 3.29ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
1114.07ms27.6msKoha::Calendar::::BEGIN@7Koha::Calendar::BEGIN@7
1113.19ms3.44msKoha::Calendar::::BEGIN@11Koha::Calendar::BEGIN@11
111470µs482µsKoha::Calendar::::BEGIN@2Koha::Calendar::BEGIN@2
1119µs9µsKoha::Calendar::::BEGIN@4Koha::Calendar::BEGIN@4
1118µs11µsKoha::Calendar::::BEGIN@9Koha::Calendar::BEGIN@9
1117µs11µsKoha::Calendar::::BEGIN@3Koha::Calendar::BEGIN@3
1117µs43µsKoha::Calendar::::BEGIN@10Koha::Calendar::BEGIN@10
1117µs7µsKoha::Calendar::::BEGIN@8Koha::Calendar::BEGIN@8
1116µs6µsKoha::Calendar::::BEGIN@6Koha::Calendar::BEGIN@6
0000s0sKoha::Calendar::::_initKoha::Calendar::_init
0000s0sKoha::Calendar::::addDateKoha::Calendar::addDate
0000s0sKoha::Calendar::::addDaysKoha::Calendar::addDays
0000s0sKoha::Calendar::::addHoursKoha::Calendar::addHours
0000s0sKoha::Calendar::::add_holidayKoha::Calendar::add_holiday
0000s0sKoha::Calendar::::clear_weekly_closed_daysKoha::Calendar::clear_weekly_closed_days
0000s0sKoha::Calendar::::days_betweenKoha::Calendar::days_between
0000s0sKoha::Calendar::::exception_holidaysKoha::Calendar::exception_holidays
0000s0sKoha::Calendar::::hours_betweenKoha::Calendar::hours_between
0000s0sKoha::Calendar::::is_holidayKoha::Calendar::is_holiday
0000s0sKoha::Calendar::::newKoha::Calendar::new
0000s0sKoha::Calendar::::next_open_dayKoha::Calendar::next_open_day
0000s0sKoha::Calendar::::prev_open_dayKoha::Calendar::prev_open_day
0000s0sKoha::Calendar::::set_daysmodeKoha::Calendar::set_daysmode
0000s0sKoha::Calendar::::single_holidaysKoha::Calendar::single_holidays
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1package Koha::Calendar;
2227µs2495µ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
use strict;
# spent 482µs making 1 call to Koha::Calendar::BEGIN@2 # spent 12µs making 1 call to strict::import
3219µs216µ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
use warnings;
# spent 11µs making 1 call to Koha::Calendar::BEGIN@3 # spent 4µs making 1 call to warnings::import
4234µs19µs
# spent 9µs within Koha::Calendar::BEGIN@4 which was called: # once (9µs+0s) by C4::Circulation::BEGIN@49 at line 4
use 5.010;
# spent 9µs making 1 call to Koha::Calendar::BEGIN@4
5
6221µs16µs
# spent 6µs within Koha::Calendar::BEGIN@6 which was called: # once (6µs+0s) by C4::Circulation::BEGIN@49 at line 6
use DateTime;
# spent 6µs making 1 call to Koha::Calendar::BEGIN@6
72709µs127.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
use DateTime::Set;
# spent 27.6ms making 1 call to Koha::Calendar::BEGIN@7
8221µs17µs
# spent 7µs within Koha::Calendar::BEGIN@8 which was called: # once (7µs+0s) by C4::Circulation::BEGIN@49 at line 8
use DateTime::Duration;
# spent 7µs making 1 call to Koha::Calendar::BEGIN@8
9221µs214µ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
use C4::Context;
# spent 11µs making 1 call to Koha::Calendar::BEGIN@9 # spent 3µs making 1 call to C4::Context::import
10221µs279µ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
use Carp;
# spent 43µs making 1 call to Koha::Calendar::BEGIN@10 # spent 36µs making 1 call to Exporter::import
1122.41ms23.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
use Readonly;
# spent 3.44ms making 1 call to Koha::Calendar::BEGIN@11 # spent 24µs making 1 call to Exporter::import
12
13sub 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
28sub _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.
621200nsour ( $exception_holidays, $single_holidays );
63sub 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
91sub 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}
117sub 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
141sub 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
166sub 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
208sub 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
244sub 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
257sub 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
270sub 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
297sub 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
325sub 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
336sub 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
342sub 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
35413µs1;
355__END__