View | Details | Raw Unified | Return to bug 8800
Collapse All | Expand All

(-)a/C4/Circulation.pm (-2 / +3 lines)
Lines 3014-3026 sub CalcDateDue { Link Here
3014
              ->truncate( to => 'minute' );
3014
              ->truncate( to => 'minute' );
3015
            if ( $loanlength->{lengthunit} eq 'hours' ) {
3015
            if ( $loanlength->{lengthunit} eq 'hours' ) {
3016
                $dt->add( hours => $loanlength->{issuelength} );
3016
                $dt->add( hours => $loanlength->{issuelength} );
3017
                return $dt;
3018
            } else {    # days
3017
            } else {    # days
3019
                $dt->add( days => $loanlength->{issuelength} );
3018
                $dt->add( days => $loanlength->{issuelength} );
3020
                $dt->set_hour(23);
3019
                $dt->set_hour(23);
3021
                $dt->set_minute(59);
3020
                $dt->set_minute(59);
3022
                return $dt;
3023
            }
3021
            }
3022
            # break
3023
            return $dt;
3024
3024
        } else {
3025
        } else {
3025
            my $dur;
3026
            my $dur;
3026
            if ($loanlength->{lengthunit} eq 'hours') {
3027
            if ($loanlength->{lengthunit} eq 'hours') {
(-)a/Koha/Calendar.pm (-41 / +149 lines)
Lines 85-141 sub _init { Link Here
85
85
86
sub addDate {
86
sub addDate {
87
    my ( $self, $startdate, $add_duration, $unit ) = @_;
87
    my ( $self, $startdate, $add_duration, $unit ) = @_;
88
    my $base_date = $startdate->clone();
88
89
    # Default to days duration (legacy support I guess)
89
    if ( ref $add_duration ne 'DateTime::Duration' ) {
90
    if ( ref $add_duration ne 'DateTime::Duration' ) {
90
        $add_duration = DateTime::Duration->new( days => $add_duration );
91
        $add_duration = DateTime::Duration->new( days => $add_duration );
91
    }
92
    }
92
    $unit ||= q{};    # default days ?
93
93
    my $days_mode = $self->{days_mode};
94
    $unit ||= 'days'; # default days ?
94
    Readonly::Scalar my $return_by_hour => 10;
95
    my $dt;
95
    my $day_dur = DateTime::Duration->new( days => 1 );
96
96
    if ( $add_duration->is_negative() ) {
97
    if ( $unit eq 'hours' ) {
97
        $day_dur = DateTime::Duration->new( days => -1 );
98
        # Fixed for legacy support. Should be set as a branch parameter
99
        Readonly::Scalar my $return_by_hour => 10;
100
101
        $dt = $self->addHours($startdate, $add_duration, $return_by_hour);
102
    } else {
103
        # days
104
        $dt = $self->addDays($startdate, $add_duration);
98
    }
105
    }
99
    if ( $days_mode eq 'Datedue' ) {
100
106
101
        my $dt = $base_date + $add_duration;
107
    return $dt;
102
        while ( $self->is_holiday($dt) ) {
108
}
103
109
104
            $dt->add_duration($day_dur);
110
sub addHours {
105
            if ( $unit eq 'hours' ) {
111
    my ( $self, $startdate, $hours_duration, $return_by_hour ) = @_;
106
                $dt->set_hour($return_by_hour);    # Staffs specific
112
    my $base_date = $startdate->clone();
107
            }
113
114
    $base_date->add_duration($hours_duration);
115
116
    # If we are using the calendar behave for now as if Datedue
117
    # was the chosen option (current intended behaviour)
118
119
    if ( $self->{days_mode} ne 'Days' &&
120
          $self->is_holiday($base_date) ) {
121
122
        if ( $hours_duration->is_negative() ) {
123
            $base_date = $self->prev_open_day($base_date);
124
        } else {
125
            $base_date = $self->next_open_day($base_date);
108
        }
126
        }
109
        return $dt;
110
    } elsif ( $days_mode eq 'Calendar' ) {
111
        if ( $unit eq 'hours' ) {
112
            $base_date->add_duration($add_duration);
113
            while ( $self->is_holiday($base_date) ) {
114
                $base_date->add_duration($day_dur);
115
127
116
            }
128
        $base_date->set_hour($return_by_hour);
129
130
    }
131
132
    return $base_date;
133
}
117
134
135
sub addDays {
136
    my ( $self, $startdate, $days_duration ) = @_;
137
    my $base_date = $startdate->clone();
138
139
    if ( $self->{days_mode} eq 'Calendar' ) {
140
        # use the calendar to skip all days the library is closed
141
        # when adding
142
        my $days = abs $days_duration->in_units('days');
143
144
        if ( $days_duration->is_negative() ) {
145
            while ($days) {
146
                $base_date = $self->prev_open_day($base_date);
147
                --$days;
148
            }
118
        } else {
149
        } else {
119
            my $days = abs $add_duration->in_units('days');
120
            while ($days) {
150
            while ($days) {
121
                $base_date->add_duration($day_dur);
151
                $base_date = $self->next_open_day($base_date);
122
                if ( $self->is_holiday($base_date) ) {
152
                --$days;
123
                    next;
124
                } else {
125
                    --$days;
126
                }
127
            }
153
            }
128
        }
154
        }
129
        if ( $unit eq 'hours' ) {
155
130
            my $dt = $base_date->clone()->subtract( days => 1 );
156
    } else { # Days or Datedue
131
            if ( $self->is_holiday($dt) ) {
157
        # use straight days, then use calendar to push
132
                $base_date->set_hour($return_by_hour);    # Staffs specific
158
        # the date to the next open day if Datedue
159
        $base_date->add_duration($days_duration);
160
161
        if ( $self->{days_mode} eq 'Datedue' ) {
162
            # Datedue, then use the calendar to push
163
            # the date to the next open day if holiday
164
            if ( $self->is_holiday($base_date) ) {
165
                if ( $days_duration->is_negative() ) {
166
                    $base_date = $self->prev_open_day($base_date);
167
                } else {
168
                    $base_date = $self->next_open_day($base_date);
169
                }
133
            }
170
            }
134
        }
171
        }
135
        return $base_date;
136
    } else {    # Days
137
        return $base_date + $add_duration;
138
    }
172
    }
173
174
    return $base_date;
139
}
175
}
140
176
141
sub is_holiday {
177
sub is_holiday {
Lines 154-163 sub is_holiday { Link Here
154
    if ( exists $self->{day_month_closed_days}->{$month}->{$day} ) {
190
    if ( exists $self->{day_month_closed_days}->{$month}->{$day} ) {
155
        return 1;
191
        return 1;
156
    }
192
    }
157
    if ( $self->{exception_holidays}->contains($dt) ) {
193
    if ( $self->{exception_holidays}->contains($localdt) ) {
158
        return 1;
194
        return 1;
159
    }
195
    }
160
    if ( $self->{single_holidays}->contains($dt) ) {
196
    if ( $self->{single_holidays}->contains($localdt) ) {
161
        return 1;
197
        return 1;
162
    }
198
    }
163
199
Lines 165-170 sub is_holiday { Link Here
165
    return 0;
201
    return 0;
166
}
202
}
167
203
204
sub next_open_day {
205
    my ( $self, $dt ) = @_;
206
    my $base_date = $dt->clone();
207
208
    $base_date->add(days => 1);
209
210
    while ($self->is_holiday($base_date)) {
211
        $base_date->add(days => 1);
212
    }
213
214
    return $base_date;
215
}
216
217
sub prev_open_day {
218
    my ( $self, $dt ) = @_;
219
    my $base_date = $dt->clone();
220
221
    $base_date->add(days => -1);
222
223
    while ($self->is_holiday($base_date)) {
224
        $base_date->add(days => -1);
225
    }
226
227
    return $base_date;
228
}
229
168
sub days_between {
230
sub days_between {
169
    my $self     = shift;
231
    my $self     = shift;
170
    my $start_dt = shift;
232
    my $start_dt = shift;
Lines 228-234 sub _mockinit { Link Here
228
    );
290
    );
229
    push @{$dates}, $special;
291
    push @{$dates}, $special;
230
    $self->{single_holidays} = DateTime::Set->from_datetimes( dates => $dates );
292
    $self->{single_holidays} = DateTime::Set->from_datetimes( dates => $dates );
231
    $self->{days_mode} = 'Calendar';
293
294
    # if not defined, days_mode defaults to 'Calendar'
295
    if ( !defined($self->{days_mode}) ) {
296
        $self->{days_mode} = 'Calendar';
297
    }
298
232
    $self->{test} = 1;
299
    $self->{test} = 1;
233
    return;
300
    return;
234
}
301
}
Lines 274-282 This documentation refers to Koha::Calendar version 0.0.1 Link Here
274
341
275
=head1 SYNOPSIS
342
=head1 SYNOPSIS
276
343
277
  use Koha::Calendat
344
  use Koha::Calendar
278
345
279
  my $c = Koha::Calender->new( branchcode => 'MAIN' );
346
  my $c = Koha::Calendar->new( branchcode => 'MAIN' );
280
  my $dt = DateTime->now();
347
  my $dt = DateTime->now();
281
348
282
  # are we open
349
  # are we open
Lines 312-322 Currently unit is only used to invoke Staffs return Monday at 10 am rule this Link Here
312
parameter will be removed when issuingrules properly cope with that
379
parameter will be removed when issuingrules properly cope with that
313
380
314
381
382
=head2 addHours
383
384
    my $dt = $calendar->addHours($date, $dur, $return_by_hour )
385
386
C<$date> is a DateTime object representing the starting date of the interval.
387
388
C<$offset> is a DateTime::Duration to add to it
389
390
C<$return_by_hour> is an integer value representing the opening hour for the branch
391
392
393
=head2 addDays
394
395
    my $dt = $calendar->addDays($date, $dur)
396
397
C<$date> is a DateTime object representing the starting date of the interval.
398
399
C<$offset> is a DateTime::Duration to add to it
400
401
C<$unit> is a string value 'days' or 'hours' toflag granularity of duration
402
403
Currently unit is only used to invoke Staffs return Monday at 10 am rule this
404
parameter will be removed when issuingrules properly cope with that
405
406
315
=head2 is_holiday
407
=head2 is_holiday
316
408
317
$yesno = $calendar->is_holiday($dt);
409
$yesno = $calendar->is_holiday($dt);
318
410
319
passed at DateTime object returns 1 if it is a closed day
411
passed a DateTime object returns 1 if it is a closed day
320
0 if not according to the calendar
412
0 if not according to the calendar
321
413
322
=head2 days_between
414
=head2 days_between
Lines 327-332 Passed two dates returns a DateTime::Duration object measuring the length betwee Link Here
327
ignoring closed days. Always returns a positive number irrespective of the
419
ignoring closed days. Always returns a positive number irrespective of the
328
relative order of the parameters
420
relative order of the parameters
329
421
422
=head2 next_open_day
423
424
$datetime = $calendar->next_open_day($duedate_dt)
425
426
Passed a Datetime returns another Datetime representing the next open day. It is
427
intended for use to calculate the due date when useDaysMode syspref is set to either
428
'Datedue' or 'Calendar'.
429
430
=head2 prev_open_day
431
432
$datetime = $calendar->prev_open_day($duedate_dt)
433
434
Passed a Datetime returns another Datetime representing the previous open day. It is
435
intended for use to calculate the due date when useDaysMode syspref is set to either
436
'Datedue' or 'Calendar'.
437
330
=head2 set_daysmode
438
=head2 set_daysmode
331
439
332
For testing only allows the calling script to change days mode
440
For testing only allows the calling script to change days mode
(-)a/t/Calendar.t (-13 / +63 lines)
Lines 3-9 Link Here
3
use strict;
3
use strict;
4
use warnings;
4
use warnings;
5
use DateTime;
5
use DateTime;
6
use Test::More tests => 23;
6
use DateTime::Duration;
7
use Test::More tests => 22;
7
use Koha::DateUtils;
8
use Koha::DateUtils;
8
9
9
BEGIN {
10
BEGIN {
Lines 89-106 my $daycount = $cal->days_between( $test_dt, $later_dt ); Link Here
89
cmp_ok( $daycount->in_units('days'),
90
cmp_ok( $daycount->in_units('days'),
90
    '==', 48, 'days_between calculates correctly' );
91
    '==', 48, 'days_between calculates correctly' );
91
92
92
my $ret = $cal->addDate( $test_dt, 1, 'days' );
93
my $ret;
93
94
94
cmp_ok( $ret->ymd(), 'eq', '2012-07-24', 'Simple Single Day Add (Calendar)' );
95
96
$ret = $cal->addDate( $test_dt, 7, 'days' );
97
cmp_ok( $ret->ymd(), 'eq', '2012-07-31', 'Add 7 days Calendar mode' );
98
$cal->set_daysmode('Datedue');
99
$ret = $cal->addDate( $test_dt, 7, 'days' );
100
cmp_ok( $ret->ymd(), 'eq', '2012-07-30', 'Add 7 days Datedue mode' );
101
$cal->set_daysmode('Days');
102
$ret = $cal->addDate( $test_dt, 7, 'days' );
103
cmp_ok( $ret->ymd(), 'eq', '2012-07-30', 'Add 7 days Days mode' );
104
$cal->set_daysmode('Calendar');
95
$cal->set_daysmode('Calendar');
105
96
106
# see bugzilla #8966
97
# see bugzilla #8966
Lines 131-133 $cal->add_holiday( dt_from_string('2012-07-07','iso') ); Link Here
131
$daycount = $cal->days_between( dt_from_string("2012-07-01",'iso'),
122
$daycount = $cal->days_between( dt_from_string("2012-07-01",'iso'),
132
    dt_from_string("2012-07-15",'iso') )->in_units('days');
123
    dt_from_string("2012-07-15",'iso') )->in_units('days');
133
cmp_ok( $daycount, '==', 12, 'multiple holidays correctly recognized' );
124
cmp_ok( $daycount, '==', 12, 'multiple holidays correctly recognized' );
134
- 
125
126
my $one_day_dur = DateTime::Duration->new( days => 1 );
127
my $two_day_dur = DateTime::Duration->new( days => 2 );
128
my $seven_day_dur = DateTime::Duration->new( days => 7 );
129
130
subtest '\'Datedue\' tests' => sub {
131
    my $cal = Koha::Calendar->new( TEST_MODE => 1 ,
132
                                   days_mode => 'Datedue');
133
134
    $cal->add_holiday( dt_from_string('2012-07-04','iso') );
135
    $dt = dt_from_string( '2012-07-03','iso' );
136
137
    is($cal->addDate( $dt, $one_day_dur, 'days' ),
138
        dt_from_string('2012-07-05','iso'),
139
        'Single day add (Datedue, matches holiday, shift)' );
140
141
    is($cal->addDate( $dt, $two_day_dur, 'days' ),
142
        dt_from_string('2012-07-05','iso'),
143
        'Two days add, skips holiday (Datedue)' );
144
145
    cmp_ok($cal->addDate( $test_dt, $seven_day_dur, 'days' ), 'eq',
146
        '2012-07-30T11:53:00',
147
        'Add 7 days (Datedue)' );
148
};
149
150
151
subtest '\'Calendar\' tests' => sub {
152
    my $cal = Koha::Calendar->new( TEST_MODE => 1,
153
                                   days_mode => 'Calendar' );
154
155
    $cal->add_holiday( dt_from_string('2012-07-04','iso') );
156
    $dt = dt_from_string('2012-07-03','iso');
157
158
    is($cal->addDate( $dt, $one_day_dur, 'days' ),
159
        dt_from_string('2012-07-05','iso'),
160
        'Single day add (Calendar)' );
161
162
    cmp_ok($cal->addDate( $test_dt, $seven_day_dur, 'days' ), 'eq',
163
       '2012-07-31T11:53:00',
164
       'Add 7 days (Calendar)' );
165
};
166
167
168
subtest '\'Days\' tests' => sub {
169
    my $cal = Koha::Calendar->new( TEST_MODE => 1,
170
                                   days_mode => 'Days' );
171
172
    $cal->add_holiday( dt_from_string('2012-07-04','iso') );
173
    $dt = dt_from_string('2012-07-03','iso');
174
175
    is($cal->addDate( $dt, $one_day_dur, 'days' ),
176
        dt_from_string('2012-07-04','iso'),
177
        'Single day add (Days)' );
178
179
    cmp_ok($cal->addDate( $test_dt, $seven_day_dur, 'days' ),'eq',
180
        '2012-07-30T11:53:00',
181
        'Add 7 days (Days)' );
182
};
183
184

Return to bug 8800