From c83aa4058d31896dc8df67fd26d42b626892a43e Mon Sep 17 00:00:00 2001
From: Charles Farmer <charles.farmer@inLibro.com>
Date: Wed, 4 Sep 2019 14:58:02 -0400
Subject: [PATCH] Bug 17015: Tests for DiscreteCalendar

---
 t/Calendar.t                             | 324 ----------------
 t/db_dependent/Calendar.t                | 393 -------------------
 t/db_dependent/Circulation.t             |  73 ++--
 t/db_dependent/Circulation/CalcDateDue.t |  47 ++-
 t/db_dependent/Circulation/CalcFine.t    |  26 +-
 t/db_dependent/DiscreteCalendar.t        | 462 +++++++++++++++++++++++
 t/db_dependent/Fines.t                   |  11 +-
 t/db_dependent/Hold.t                    |  20 +-
 t/db_dependent/HoldsQueue.t              |  34 +-
 t/db_dependent/Holidays.t                | 282 --------------
 t/lib/TestBuilder.pm                     |   6 +
 11 files changed, 575 insertions(+), 1103 deletions(-)
 delete mode 100755 t/Calendar.t
 delete mode 100755 t/db_dependent/Calendar.t
 create mode 100644 t/db_dependent/DiscreteCalendar.t
 delete mode 100755 t/db_dependent/Holidays.t

diff --git a/t/Calendar.t b/t/Calendar.t
deleted file mode 100755
index 8965087301..0000000000
--- a/t/Calendar.t
+++ /dev/null
@@ -1,324 +0,0 @@
-#!/usr/bin/perl
-
-# This file is part of Koha.
-#
-# Koha is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# Koha is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Koha; if not, see <http://www.gnu.org/licenses>.
-
-use Modern::Perl;
-
-use Test::More;
-use Test::MockModule;
-
-use DateTime;
-use DateTime::Duration;
-use Koha::Caches;
-use Koha::DateUtils;
-
-use t::lib::Mocks;
-
-use Module::Load::Conditional qw/check_install/;
-
-BEGIN {
-    if ( check_install( module => 'Test::DBIx::Class' ) ) {
-        plan tests => 40;
-    } else {
-        plan skip_all => "Need Test::DBIx::Class"
-    }
-}
-
-use_ok('Koha::Calendar');
-
-use Test::DBIx::Class;
-
-my $db = Test::MockModule->new('Koha::Database');
-$db->mock(
-    _new_schema => sub { return Schema(); }
-);
-
-# We need to mock the C4::Context->preference method for
-# simplicity and re-usability of the session definition. Any
-# syspref fits for syspref-agnostic tests.
-my $module_context = Test::MockModule->new('C4::Context');
-$module_context->mock(
-    'preference',
-    sub {
-        return 'Calendar';
-    }
-);
-
-fixtures_ok [
-    # weekly holidays
-    RepeatableHoliday => [
-        [ qw( branchcode day month weekday title description) ],
-        [ 'MPL', undef, undef, 0, '', '' ], # sundays
-        [ 'MPL', undef, undef, 6, '', '' ],# saturdays
-        [ 'MPL', 1, 1, undef, '', ''], # new year's day
-        [ 'MPL', 25, 12, undef, '', ''], # chrismas
-    ],
-    # exception holidays
-    SpecialHoliday => [
-        [qw( branchcode day month year title description isexception )],
-        [ 'MPL', 11, 11, 2012, '', '', 1 ],    # sunday exception
-        [ 'MPL', 1,  6,  2011, '', '', 0 ],
-        [ 'MPL', 4,  7,  2012, '', '', 0 ],
-        [ 'CPL', 6,  8,  2012, '', '', 0 ],
-        [ 'MPL', 7,  7,  2012, '', '', 1 ], # holiday exception
-        [ 'MPL', 7,  7,  2012, '', '', 0 ], # holiday
-      ],
-], "add fixtures";
-
-my $cache = Koha::Caches->get_instance();
-$cache->clear_from_cache('MPL_holidays');
-$cache->clear_from_cache('CPL_holidays');
-
-# 'MPL' branch is arbitrary, is not used at all but is needed for initialization
-my $cal = Koha::Calendar->new( branchcode => 'MPL' );
-
-isa_ok( $cal, 'Koha::Calendar', 'Calendar class returned' );
-
-my $saturday = DateTime->new(
-    year      => 2012,
-    month     => 11,
-    day       => 24,
-);
-
-my $sunday = DateTime->new(
-    year      => 2012,
-    month     => 11,
-    day       => 25,
-);
-
-my $monday = DateTime->new(
-    year      => 2012,
-    month     => 11,
-    day       => 26,
-);
-
-my $new_year = DateTime->new(
-    year        => 2013,
-    month       => 1,
-    day         => 1,
-);
-
-my $single_holiday = DateTime->new(
-    year      => 2011,
-    month     => 6,
-    day       => 1,
-);    # should be a holiday
-
-my $notspecial = DateTime->new(
-    year      => 2011,
-    month     => 6,
-    day       => 2
-);    # should NOT be a holiday
-
-my $sunday_exception = DateTime->new(
-    year      => 2012,
-    month     => 11,
-    day       => 11
-);
-
-my $day_after_christmas = DateTime->new(
-    year    => 2012,
-    month   => 12,
-    day     => 26
-);  # for testing negative addDuration
-
-my $holiday_for_another_branch = DateTime->new(
-    year => 2012,
-    month => 8,
-    day => 6, # This is a monday
-);
-
-my $holiday_excepted = DateTime->new(
-    year => 2012,
-    month => 7,
-    day => 7, # Both a holiday and exception
-);
-
-{   # Syspref-agnostic tests
-    is ( $saturday->day_of_week, 6, '\'$saturday\' is actually a saturday (6th day of week)');
-    is ( $sunday->day_of_week, 7, '\'$sunday\' is actually a sunday (7th day of week)');
-    is ( $monday->day_of_week, 1, '\'$monday\' is actually a monday (1st day of week)');
-    is ( $cal->is_holiday($saturday), 1, 'Saturday is a closed day' );
-    is ( $cal->is_holiday($sunday), 1, 'Sunday is a closed day' );
-    is ( $cal->is_holiday($monday), 0, 'Monday is not a closed day' );
-    is ( $cal->is_holiday($new_year), 1, 'Month/Day closed day test (New year\'s day)' );
-    is ( $cal->is_holiday($single_holiday), 1, 'Single holiday closed day test' );
-    is ( $cal->is_holiday($notspecial), 0, 'Fixed single date that is not a holiday test' );
-    is ( $cal->is_holiday($sunday_exception), 0, 'Exception holiday is not a closed day test' );
-    is ( $cal->is_holiday($holiday_for_another_branch), 0, 'Holiday defined for another branch should not be defined as an holiday' );
-    is ( $cal->is_holiday($holiday_excepted), 0, 'Holiday defined and excepted should not be a holiday' );
-}
-
-{   # Bugzilla #8966 - is_holiday truncates referenced date
-    my $later_dt = DateTime->new(    # Monday
-        year      => 2012,
-        month     => 9,
-        day       => 17,
-        hour      => 17,
-        minute    => 30,
-        time_zone => 'Europe/London',
-    );
-
-
-    is( $cal->is_holiday($later_dt), 0, 'bz-8966 (1/2) Apply is_holiday for the next test' );
-    cmp_ok( $later_dt, 'eq', '2012-09-17T17:30:00', 'bz-8966 (2/2) Date should be the same after is_holiday' );
-}
-
-{   # Bugzilla #8800 - is_holiday should use truncated date for 'contains' call
-    my $single_holiday_time = DateTime->new(
-        year  => 2011,
-        month => 6,
-        day   => 1,
-        hour  => 11,
-        minute  => 2
-    );
-
-    is( $cal->is_holiday($single_holiday_time),
-        $cal->is_holiday($single_holiday) ,
-        'bz-8800 is_holiday should truncate the date for holiday validation' );
-}
-
-    my $one_day_dur = DateTime::Duration->new( days => 1 );
-    my $two_day_dur = DateTime::Duration->new( days => 2 );
-    my $seven_day_dur = DateTime::Duration->new( days => 7 );
-
-    my $dt = dt_from_string( '2012-07-03','iso' ); #tuesday
-
-    my $test_dt = DateTime->new(    # Monday
-        year      => 2012,
-        month     => 7,
-        day       => 23,
-        hour      => 11,
-        minute    => 53,
-    );
-
-    my $later_dt = DateTime->new(    # Monday
-        year      => 2012,
-        month     => 9,
-        day       => 17,
-        hour      => 17,
-        minute    => 30,
-        time_zone => 'Europe/London',
-    );
-
-{    ## 'Datedue' tests
-
-    $cal = Koha::Calendar->new( branchcode => 'MPL', days_mode => 'Datedue' );
-
-    is($cal->addDuration( $dt, $one_day_dur, 'days' ), # tuesday
-        dt_from_string('2012-07-05','iso'),
-        'Single day add (Datedue, matches holiday, shift)' );
-
-    is($cal->addDuration( $dt, $two_day_dur, 'days' ),
-        dt_from_string('2012-07-05','iso'),
-        'Two days add, skips holiday (Datedue)' );
-
-    cmp_ok($cal->addDuration( $test_dt, $seven_day_dur, 'days' ), 'eq',
-        '2012-07-30T11:53:00',
-        'Add 7 days (Datedue)' );
-
-    is( $cal->addDuration( $saturday, $one_day_dur, 'days' )->day_of_week, 1,
-        'addDuration skips closed Sunday (Datedue)' );
-
-    is( $cal->addDuration($day_after_christmas, -1, 'days')->ymd(), '2012-12-24',
-        'Negative call to addDuration (Datedue)' );
-
-    ## Note that the days_between API says closed days are not considered.
-    ## This tests are here as an API test.
-    cmp_ok( $cal->days_between( $test_dt, $later_dt )->in_units('days'),
-                '==', 40, 'days_between calculates correctly (Days)' );
-
-    cmp_ok( $cal->days_between( $later_dt, $test_dt )->in_units('days'),
-                '==', 40, 'Test parameter order not relevant (Days)' );
-}
-
-{   ## 'Calendar' tests'
-
-    $cal = Koha::Calendar->new( branchcode => 'MPL', days_mode => 'Calendar' );
-
-    $dt = dt_from_string('2012-07-03','iso');
-
-    is($cal->addDuration( $dt, $one_day_dur, 'days' ),
-        dt_from_string('2012-07-05','iso'),
-        'Single day add (Calendar)' );
-
-    cmp_ok($cal->addDuration( $test_dt, $seven_day_dur, 'days' ), 'eq',
-       '2012-08-01T11:53:00',
-       'Add 7 days (Calendar)' );
-
-    is( $cal->addDuration( $saturday, $one_day_dur, 'days' )->day_of_week, 1,
-            'addDuration skips closed Sunday (Calendar)' );
-
-    is( $cal->addDuration($day_after_christmas, -1, 'days')->ymd(), '2012-12-24',
-            'Negative call to addDuration (Calendar)' );
-
-    cmp_ok( $cal->days_between( $test_dt, $later_dt )->in_units('days'),
-                '==', 40, 'days_between calculates correctly (Calendar)' );
-
-    cmp_ok( $cal->days_between( $later_dt, $test_dt )->in_units('days'),
-                '==', 40, 'Test parameter order not relevant (Calendar)' );
-}
-
-
-{   ## 'Days' tests
-
-    $cal = Koha::Calendar->new( branchcode => 'MPL', days_mode => 'Days' );
-
-    $dt = dt_from_string('2012-07-03','iso');
-
-    is($cal->addDuration( $dt, $one_day_dur, 'days' ),
-        dt_from_string('2012-07-04','iso'),
-        'Single day add (Days)' );
-
-    cmp_ok($cal->addDuration( $test_dt, $seven_day_dur, 'days' ),'eq',
-        '2012-07-30T11:53:00',
-        'Add 7 days (Days)' );
-
-    is( $cal->addDuration( $saturday, $one_day_dur, 'days' )->day_of_week, 7,
-        'addDuration doesn\'t skip closed Sunday (Days)' );
-
-    is( $cal->addDuration($day_after_christmas, -1, 'days')->ymd(), '2012-12-25',
-        'Negative call to addDuration (Days)' );
-
-    ## Note that the days_between API says closed days are not considered.
-    ## This tests are here as an API test.
-    cmp_ok( $cal->days_between( $test_dt, $later_dt )->in_units('days'),
-                '==', 40, 'days_between calculates correctly (Days)' );
-
-    cmp_ok( $cal->days_between( $later_dt, $test_dt )->in_units('days'),
-                '==', 40, 'Test parameter order not relevant (Days)' );
-
-}
-
-{
-    $cal = Koha::Calendar->new( branchcode => 'CPL' );
-    is ( $cal->is_holiday($single_holiday), 0, 'Single holiday for MPL, not CPL' );
-    is ( $cal->is_holiday($holiday_for_another_branch), 1, 'Holiday defined for CPL should be defined as an holiday' );
-}
-
-subtest 'days_mode parameter' => sub {
-    plan tests => 1;
-
-    t::lib::Mocks::mock_preference('useDaysMode', 'Days');
-
-    $cal = Koha::Calendar->new( branchcode => 'CPL', days_mode => 'Calendar' );
-    is( $cal->{days_mode}, 'Calendar', q|If set, days_mode is correctly set|);
-};
-
-END {
-    $cache->clear_from_cache('MPL_holidays');
-    $cache->clear_from_cache('CPL_holidays');
-};
diff --git a/t/db_dependent/Calendar.t b/t/db_dependent/Calendar.t
deleted file mode 100755
index 0900b5719d..0000000000
--- a/t/db_dependent/Calendar.t
+++ /dev/null
@@ -1,393 +0,0 @@
-#!/usr/bin/perl
-
-# This file is part of Koha.
-#
-# Koha is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# Koha is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Koha; if not, see <http://www.gnu.org/licenses>.
-
-use Modern::Perl;
-
-use Test::More tests => 6;
-use Time::Fake;
-
-use t::lib::Mocks;
-use t::lib::TestBuilder;
-
-use DateTime;
-use Koha::Caches;
-use Koha::DateUtils;
-
-use_ok('Koha::Calendar');
-
-my $schema  = Koha::Database->new->schema;
-$schema->storage->txn_begin;
-
-my $today = dt_from_string();
-my $holiday_dt = $today->clone;
-$holiday_dt->add(days => 3);
-
-Koha::Caches->get_instance()->flush_all();
-
-my $builder = t::lib::TestBuilder->new();
-my $library = $builder->build_object({ class => 'Koha::Libraries' });
-my $holiday = $builder->build(
-    {
-        source => 'SpecialHoliday',
-        value  => {
-            branchcode  => $library->branchcode,
-            day         => $holiday_dt->day,
-            month       => $holiday_dt->month,
-            year        => $holiday_dt->year,
-            title       => 'My holiday',
-            isexception => 0
-        },
-    }
-);
-
-my $calendar = Koha::Calendar->new( branchcode => $library->branchcode, days_mode => 'Calendar' );
-
-subtest 'days_forward' => sub {
-
-    plan tests => 4;
-    my $forwarded_dt = $calendar->days_forward( $today, 2 );
-    my $expected = $today->clone->add( days => 2 );
-    is( $forwarded_dt->ymd, $expected->ymd, 'With no holiday on the perioddays_forward should add 2 days' );
-
-    $forwarded_dt = $calendar->days_forward( $today, 5 );
-    $expected = $today->clone->add( days => 6 );
-    is( $forwarded_dt->ymd, $expected->ymd, 'With holiday on the perioddays_forward should add 5 days + 1 day for holiday'
-    );
-
-    $forwarded_dt = $calendar->days_forward( $today, 0 );
-    is( $forwarded_dt->ymd, $today->ymd, '0 day should return start dt' );
-
-    $forwarded_dt = $calendar->days_forward( $today, -2 );
-    is( $forwarded_dt->ymd, $today->ymd, 'negative day should return start dt' );
-};
-
-subtest 'crossing_DST' => sub {
-
-    plan tests => 3;
-
-    my $tz = DateTime::TimeZone->new( name => 'America/New_York' );
-    my $start_date = dt_from_string( "2016-03-09 02:29:00", undef, $tz );
-    my $end_date   = dt_from_string( "2017-01-01 00:00:00", undef, $tz );
-    my $days_between = $calendar->days_between( $start_date, $end_date );
-    is( $days_between->delta_days, 298, "Days calculated correctly" );
-    $days_between = $calendar->days_between( $end_date, $start_date );
-    is( $days_between->delta_days, 298, "Swapping returns the same" );
-    my $hours_between = $calendar->hours_between( $start_date, $end_date );
-    is(
-        $hours_between->delta_minutes,
-        298 * 24 * 60 - 149,
-        "Hours (in minutes) calculated correctly"
-    );
-};
-
-subtest 'hours_between | days_between' => sub {
-
-    plan tests => 2;
-
-    #    November 2019
-    # Su Mo Tu We Th Fr Sa
-    #                 1  2
-    #  3  4 *5* 6  7  8  9
-    # 10 11 12 13 14 15 16
-    # 17 18 19 20 21 22 23
-    # 24 25 26 27 28 29 30
-
-    my $now    = dt_from_string('2019-11-05 12:34:56'); # Today is 2019 Nov 5th
-    my $nov_6  = dt_from_string('2019-11-06 12:34:56');
-    my $nov_7  = dt_from_string('2019-11-07 12:34:56');
-    my $nov_12 = dt_from_string('2019-11-12 12:34:56');
-    my $nov_13 = dt_from_string('2019-11-13 12:34:56');
-    my $nov_15 = dt_from_string('2019-11-15 12:34:56');
-    Time::Fake->offset($now->epoch);
-
-    subtest 'No holiday' => sub {
-
-        plan tests => 2;
-
-        my $library = $builder->build_object({ class => 'Koha::Libraries' });
-        my $calendar = Koha::Calendar->new( branchcode => $library->branchcode );
-
-        subtest 'Same hours' => sub {
-
-            plan tests => 8;
-
-            # Between 5th and 6th
-            my $diff_hours = $calendar->hours_between( $now, $nov_6 )->hours;
-            is( $diff_hours, 1 * 24, 'hours: 1 day, no holiday' );
-            my $diff_days = $calendar->days_between( $now, $nov_6 )->delta_days;
-            is( $diff_days, 1, 'days: 1 day, no holiday' );
-
-            # Between 5th and 7th
-            $diff_hours = $calendar->hours_between( $now, $nov_7 )->hours;
-            is( $diff_hours, 2 * 24, 'hours: 2 days, no holiday' );
-            $diff_days = $calendar->days_between( $now, $nov_7 )->delta_days;
-            is( $diff_days, 2, 'days: 2 days, no holiday' );
-
-            # Between 5th and 12th
-            $diff_hours = $calendar->hours_between( $now, $nov_12 )->hours;
-            is( $diff_hours, 7 * 24, 'hours: 7 days, no holiday' );
-            $diff_days = $calendar->days_between( $now, $nov_12 )->delta_days;
-            is( $diff_days, 7, 'days: 7 days, no holiday' );
-
-            # Between 5th and 15th
-            $diff_hours = $calendar->hours_between( $now, $nov_15 )->hours;
-            is( $diff_hours, 10 * 24, 'hours: 10 days, no holiday' );
-            $diff_days = $calendar->days_between( $now, $nov_15 )->delta_days;
-            is( $diff_days, 10, 'days: 10 days, no holiday' );
-        };
-
-        subtest 'Different hours' => sub {
-
-            plan tests => 10;
-
-            # Between 5th and 5th (Same day short hours loan)
-            my $diff_hours = $calendar->hours_between( $now, $now->clone->add(hours => 3) )->hours;
-            is( $diff_hours, 3, 'hours: 3 hours, no holidays' );
-            my $diff_days = $calendar->days_between( $now, $now->clone->add(hours => 3) )->delta_days;
-            is( $diff_days, 0, 'days: 3 hours, no holidays' );
-
-            # Between 5th and 6th
-            $diff_hours = $calendar->hours_between( $now, $nov_6->clone->subtract(hours => 3) )->hours;
-            is( $diff_hours, 1 * 24 - 3, 'hours: 21 hours, no holidays' );
-            $diff_days = $calendar->days_between( $now, $nov_6->clone->subtract(hours => 3) )->delta_days;
-            is( $diff_days, 1, 'days: 21 hours, no holidays' );
-
-            # Between 5th and 7th
-            $diff_hours = $calendar->hours_between( $now, $nov_7->clone->subtract(hours => 3) )->hours;
-            is( $diff_hours, 2 * 24 - 3, 'hours: 45 hours, no holidays' );
-            $diff_days = $calendar->days_between( $now, $nov_7->clone->subtract(hours => 3) )->delta_days;
-            is( $diff_days, 2, 'days: 45 hours, no holidays' );
-
-            # Between 5th and 12th
-            $diff_hours = $calendar->hours_between( $now, $nov_12->clone->subtract(hours => 3) )->hours;
-            is( $diff_hours, 7 * 24 - 3, 'hours: 165 hours, no holidays' );
-            $diff_days = $calendar->days_between( $now, $nov_12->clone->subtract(hours => 3) )->delta_days;
-            is( $diff_days, 7, 'days: 165 hours, no holidays' );
-
-            # Between 5th and 15th
-            $diff_hours = $calendar->hours_between( $now, $nov_15->clone->subtract(hours => 3) )->hours;
-            is( $diff_hours, 10 * 24 - 3, 'hours: 237 hours, no holidays' );
-            $diff_days = $calendar->days_between( $now, $nov_15->clone->subtract(hours => 3) )->delta_days;
-            is( $diff_days, 10, 'days: 237 hours, no holidays' );
-        };
-    };
-
-    subtest 'With holiday' => sub {
-        plan tests => 2;
-
-        my $library = $builder->build_object({ class => 'Koha::Libraries' });
-
-        # Wednesdays are closed
-        my $dbh = C4::Context->dbh;
-        $dbh->do(q|
-            INSERT INTO repeatable_holidays (branchcode,weekday,day,month,title,description)
-            VALUES ( ?, ?, NULL, NULL, ?, '' )
-        |, undef, $library->branchcode, 3, 'Closed on Wednesday');
-
-        my $calendar = Koha::Calendar->new( branchcode => $library->branchcode );
-
-        subtest 'Same hours' => sub {
-            plan tests => 12;
-
-            my ( $diff_hours, $diff_days );
-
-            # Between 5th and 6th (This case should never happen in real code, one cannot return on a closed day)
-            $diff_hours = $calendar->hours_between( $now, $nov_6 )->hours;
-            is( $diff_hours, 0 * 24, 'hours: 1 day, end_dt = holiday' ); # FIXME Is this really should be 0?
-            $diff_days = $calendar->days_between( $now, $nov_6)->delta_days;
-            is( $diff_days, 0, 'days: 1 day, end_dt = holiday' ); # FIXME Is this really should be 0?
-
-            # Between 6th and 7th (This case should never happen in real code, one cannot issue on a closed day)
-            $diff_hours = $calendar->hours_between( $nov_6, $nov_7 )->hours;
-            is( $diff_hours, 0 * 24, 'hours: 1 day, start_dt = holiday' ); # FIXME Is this really should be 0?
-            $diff_days = $calendar->days_between( $nov_6, $nov_7 )->delta_days;
-            is( $diff_days, 0, 'days: 1 day, start_dt = holiday' ); # FIXME Is this really should be 0?
-
-            # Between 5th and 7th
-            $diff_hours = $calendar->hours_between( $now, $nov_7 )->hours;
-            is( $diff_hours, 2 * 24 - 1 * 24, 'hours: 2 days, 1 holiday' );
-            $diff_days = $calendar->days_between( $now, $nov_7 )->delta_days;
-            is( $diff_days, 2 - 1, 'days: 2 days, 1 holiday' );
-
-            # Between 5th and 12th
-            $diff_hours = $calendar->hours_between( $now, $nov_12 )->hours;
-            is( $diff_hours, 7 * 24 - 1 * 24, 'hours: 7 days, 1 holiday' );
-            $diff_days = $calendar->days_between( $now, $nov_12)->delta_days;
-            is( $diff_days, 7 - 1, 'day: 7 days, 1 holiday' );
-
-            # Between 5th and 13th
-            $diff_hours = $calendar->hours_between( $now, $nov_13 )->hours;
-            is( $diff_hours, 8 * 24 - 2 * 24, 'hours: 8 days, 2 holidays' );
-            $diff_days = $calendar->days_between( $now, $nov_13)->delta_days;
-            is( $diff_days, 8 - 2, 'days: 8 days, 2 holidays' );
-
-            # Between 5th and 15th
-            $diff_hours = $calendar->hours_between( $now, $nov_15 )->hours;
-            is( $diff_hours, 10 * 24 - 2 * 24, 'hours: 10 days, 2 holidays' );
-            $diff_days = $calendar->days_between( $now, $nov_15)->delta_days;
-            is( $diff_days, 10 - 2, 'days: 10 days, 2 holidays' );
-        };
-
-        subtest 'Different hours' => sub {
-            plan tests => 14;
-
-            my ( $diff_hours, $diff_days );
-
-            # Between 5th and 5th (Same day short hours loan)
-            # No test - Tested above as 5th is an open day
-
-            # Between 5th and 6th (This case should never happen in real code, one cannot return on a closed day)
-            my $duration = $calendar->hours_between( $now, $nov_6->clone->subtract(hours => 3) );
-            is( $duration->hours, abs(0 * 24 - 3), 'hours: 21 hours, end_dt = holiday' ); # FIXME $duration->hours always return a abs
-            is( $duration->is_negative, 1, '? is negative ?' ); # FIXME Do really test for that case in our calls to hours_between?
-            $duration = $calendar->days_between( $now, $nov_6->clone->subtract(hours => 3) );
-            is( $duration->hours, abs(0), 'days: 21 hours, end_dt = holiday' ); # FIXME Is this correct?
-
-            # Between 6th and 7th (This case should never happen in real code, one cannot issue on a closed day)
-            $duration = $calendar->hours_between( $nov_6, $nov_7->clone->subtract(hours => 3) );
-            is( $duration->hours, abs(0 * 24 - 3), 'hours: 21 hours, start_dt = holiday' ); # FIXME $duration->hours always return a abs
-            is( $duration->is_negative, 1, '? is negative ?' ); # FIXME Do really test for that case in our calls to hours_between?
-            $duration = $calendar->days_between( $nov_6, $nov_7->clone->subtract(hours => 3) );
-            is( $duration->hours, abs(0), 'days: 21 hours, start_dt = holiday' ); # FIXME Is this correct?
-
-            # Between 5th and 7th
-            $diff_hours = $calendar->hours_between( $now, $nov_7->clone->subtract(hours => 3) )->hours;
-            is( $diff_hours, 2 * 24 - 1 * 24 - 3, 'hours: 45 hours, 1 holiday' );
-            $diff_days = $calendar->days_between( $now, $nov_7->clone->subtract(hours => 3) )->delta_days;
-            is( $diff_days, 2 - 1, 'days: 45 hours, 1 holiday' );
-
-            # Between 5th and 12th
-            $diff_hours = $calendar->hours_between( $now, $nov_12->clone->subtract(hours => 3) )->hours;
-            is( $diff_hours, 7 * 24 - 1 * 24 - 3, 'hours: 165 hours, 1 holiday' );
-            $diff_days = $calendar->days_between( $now, $nov_12->clone->subtract(hours => 3) )->delta_days;
-            is( $diff_days, 7 - 1, 'days: 165 hours, 1 holiday' );
-
-            # Between 5th and 13th
-            $diff_hours = $calendar->hours_between( $now, $nov_13->clone->subtract(hours => 3) )->hours;
-            is( $diff_hours, 8 * 24 - 2 * 24 - 3, 'hours: 289 hours, 2 holidays ' );
-            $diff_days = $calendar->days_between( $now, $nov_13->clone->subtract(hours => 3) )->delta_days;
-            is( $diff_days, 8 - 1, 'days: 289 hours, 2 holidays' );
-
-            # Between 5th and 15th
-            $diff_hours = $calendar->hours_between( $now, $nov_15->clone->subtract(hours => 3) )->hours;
-            is( $diff_hours, 10 * 24 - 2 * 24 - 3, 'hours: 237 hours, 2 holidays' );
-            $diff_days = $calendar->days_between( $now, $nov_15->clone->subtract(hours => 3) )->delta_days;
-            is( $diff_days, 10 - 2, 'days: 237 hours, 2 holidays' );
-        };
-
-    };
-
-    Time::Fake->reset;
-};
-
-subtest 'is_holiday' => sub {
-    plan tests => 1;
-
-    subtest 'weekday holidays' => sub {
-        plan tests => 7;
-
-        my $library = $builder->build_object( { class => 'Koha::Libraries' } );
-
-        my $day = dt_from_string();
-        my $dow = scalar $day->day_of_week;
-        $dow = 0 if $dow == 7;
-
-        # Closed this day of the week
-        my $dbh = C4::Context->dbh;
-        $dbh->do(
-            q|
-            INSERT INTO repeatable_holidays (branchcode,weekday,day,month,title,description)
-            VALUES ( ?, ?, NULL, NULL, ?, '' )
-        |, undef, $library->branchcode, $dow, "TEST"
-        );
-
-        # Iterate 7 days
-        my $sth = $dbh->prepare(
-"UPDATE repeatable_holidays SET weekday = ? WHERE branchcode = ? AND title = 'TEST'"
-        );
-        for my $i ( 0 .. 6 ) {
-            my $calendar =
-              Koha::Calendar->new( branchcode => $library->branchcode );
-
-            is( $calendar->is_holiday($day), 1, $day->day_name() ." works as a repeatable holiday");
-
-            # Increment the date and holiday day
-            $day->add( days => 1 );
-            $dow++;
-            $dow = 0 if $dow == 7;
-            $sth->execute($dow, $library->branchcode);
-        }
-    };
-};
-
-subtest 'get_push_amt' => sub {
-    plan tests => 1;
-
-    t::lib::Mocks::mock_preference('useDaysMode', 'Dayweek');
-
-    subtest 'weekday holidays' => sub {
-        plan tests => 7;
-
-        my $library = $builder->build_object( { class => 'Koha::Libraries' } );
-
-        my $day = dt_from_string();
-        my $dow = scalar $day->day_of_week;
-        $dow = 0 if $dow == 7;
-
-        # Closed this day of the week
-        my $dbh = C4::Context->dbh;
-        $dbh->do(
-            q|
-            INSERT INTO repeatable_holidays (branchcode,weekday,day,month,title,description)
-            VALUES ( ?, ?, NULL, NULL, ?, '' )
-        |, undef, $library->branchcode, $dow, "TEST"
-        );
-
-        # Iterate 7 days
-        my $sth = $dbh->prepare(
-"UPDATE repeatable_holidays SET weekday = ? WHERE branchcode = ? AND title = 'TEST'"
-        );
-        for my $i ( 0 .. 6 ) {
-            my $calendar =
-              Koha::Calendar->new( branchcode => $library->branchcode, days_mode => 'Dayweek' );
-
-            my $npa;
-            eval {
-                local $SIG{ALRM} = sub { die "alarm\n" };    # NB: \n required
-                alarm 2;
-                $npa = $calendar->next_open_days( $day, 0 );
-                alarm 0;
-            };
-            if ($@) {
-                die unless $@ eq "alarm\n";    # propagate unexpected errors
-                # timed out
-                ok(0, "next_push_amt succeeded for ".$day->day_name()." weekday holiday");
-            }
-            else {
-                ok($npa, "next_push_amt succeeded for ".$day->day_name()." weekday holiday");
-            }
-
-            # Increment the date and holiday day
-            $day->add( days => 1 );
-            $dow++;
-            $dow = 0 if $dow == 7;
-            $sth->execute( $dow, $library->branchcode );
-        }
-    };
-};
-
-$schema->storage->txn_rollback();
diff --git a/t/db_dependent/Circulation.t b/t/db_dependent/Circulation.t
index d0bfe6e207..a9cfdf87cd 100755
--- a/t/db_dependent/Circulation.t
+++ b/t/db_dependent/Circulation.t
@@ -41,6 +41,7 @@ use C4::Reserves;
 use C4::Overdues qw(UpdateFine CalcFine);
 use Koha::DateUtils;
 use Koha::Database;
+use Koha::DiscreteCalendar;
 use Koha::Items;
 use Koha::Item::Transfers;
 use Koha::Checkouts;
@@ -110,8 +111,6 @@ my $mocked_datetime = Test::MockModule->new('DateTime');
 $mocked_datetime->mock( 'now', sub { return $now_value->clone; } );
 
 my $cache = Koha::Caches->get_instance();
-$dbh->do(q|DELETE FROM special_holidays|);
-$dbh->do(q|DELETE FROM repeatable_holidays|);
 my $branches = Koha::Libraries->search();
 for my $branch ( $branches->next ) {
     my $key = $branch->branchcode . "_holidays";
@@ -2236,16 +2235,16 @@ subtest 'AddReturn + suspension_chargeperiod' => sub {
     );
 
     my $now = dt_from_string;
-    my $five_days_ago = $now->clone->subtract( days => 5 );
+    my $in_five_days = $now->clone->add( days => 5 );
     # We want to charge 2 days every day, without grace
     # With 5 days of overdue: 5 * Z
-    my $expected_expiration = $now->clone->add( days => ( 5 * 2 ) / 1 );
+    my $expected_expiration = $now->clone->add( days => 5 + ( 5 * 2 ) / 1 );
     test_debarment_on_checkout(
         {
             item            => $item_1,
             library         => $library,
             patron          => $patron,
-            due_date        => $five_days_ago,
+            return_date     => $in_five_days,
             expiration_date => $expected_expiration,
         }
     );
@@ -2294,13 +2293,13 @@ subtest 'AddReturn + suspension_chargeperiod' => sub {
         }
     );
 
-    $expected_expiration = $now->clone->add( days => floor( 5 * 2 ) / 2 );
+    $expected_expiration = $now->clone->add( days => 5 + floor( 5 * 2 ) / 2 );
     test_debarment_on_checkout(
         {
             item            => $item_1,
             library         => $library,
             patron          => $patron,
-            due_date        => $five_days_ago,
+            return_date     => $in_five_days,
             expiration_date => $expected_expiration,
         }
     );
@@ -2318,13 +2317,13 @@ subtest 'AddReturn + suspension_chargeperiod' => sub {
             }
         }
     );
-    $expected_expiration = $now->clone->add( days => floor( ( ( 5 - 1 ) / 3 ) * 2 ) );
+    $expected_expiration = $now->clone->add( days => 5 + floor( ( ( 5 - 1 ) / 3 ) * 2 ) );
     test_debarment_on_checkout(
         {
             item            => $item_1,
             library         => $library,
             patron          => $patron,
-            due_date        => $five_days_ago,
+            return_date     => $in_five_days,
             expiration_date => $expected_expiration,
         }
     );
@@ -2347,65 +2346,63 @@ subtest 'AddReturn + suspension_chargeperiod' => sub {
     t::lib::Mocks::mock_preference('SuspensionsCalendar', 'noSuspensionsWhenClosed');
 
     # Adding a holiday 2 days ago
-    my $calendar = C4::Calendar->new(branchcode => $library->{branchcode});
-    my $two_days_ago = $now->clone->subtract( days => 2 );
-    $calendar->insert_single_holiday(
-        day             => $two_days_ago->day,
-        month           => $two_days_ago->month,
-        year            => $two_days_ago->year,
-        title           => 'holidayTest-2d',
-        description     => 'holidayDesc 2 days ago'
+    my $calendar = Koha::DiscreteCalendar->new(branchcode => $library->{branchcode});
+    my $in_two_days = $now->clone->add( days => 2 );
+    $calendar->edit_holiday(
+        title           => 'holidayTest+2d',
+        holiday_type    => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
+        start_date      => $in_two_days,
+        end_date        => $in_two_days,
     );
     # With 5 days of overdue, only 4 (x finedays=2) days must charged (one was an holiday)
-    $expected_expiration = $now->clone->add( days => floor( ( ( 5 - 0 - 1 ) / 1 ) * 2 ) );
+    $expected_expiration = $now->clone->add( days => 5 + floor( ( ( 5 - 0 - 1 ) / 1 ) * 2 ) );
     test_debarment_on_checkout(
         {
             item            => $item_1,
             library         => $library,
             patron          => $patron,
-            due_date        => $five_days_ago,
+            return_date     => $in_five_days,
             expiration_date => $expected_expiration,
         }
     );
 
     # Adding a holiday 2 days ahead, with finesCalendar=noFinesWhenClosed it should be skipped
-    my $two_days_ahead = $now->clone->add( days => 2 );
-    $calendar->insert_single_holiday(
-        day             => $two_days_ahead->day,
-        month           => $two_days_ahead->month,
-        year            => $two_days_ahead->year,
-        title           => 'holidayTest+2d',
-        description     => 'holidayDesc 2 days ahead'
+    my $in_seven_days = $now->clone->add( days => 7 );
+    $calendar->edit_holiday(
+        title           => 'holidayTest+7d',
+        holiday_type    => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
+        start_date      => $in_seven_days,
+        end_date        => $in_seven_days,
     );
 
     # Same as above, but we should skip D+2
-    $expected_expiration = $now->clone->add( days => floor( ( ( 5 - 0 - 1 ) / 1 ) * 2 ) + 1 );
+    $expected_expiration = $now->clone->add( days => 5 + floor( ( ( 5 - 0 - 1 ) / 1 ) * 2 ) + 1 );
     test_debarment_on_checkout(
         {
             item            => $item_1,
             library         => $library,
             patron          => $patron,
-            due_date        => $five_days_ago,
+            return_date     => $in_five_days,
             expiration_date => $expected_expiration,
         }
     );
 
     # Adding another holiday, day of expiration date
     my $expected_expiration_dt = dt_from_string($expected_expiration);
-    $calendar->insert_single_holiday(
-        day             => $expected_expiration_dt->day,
-        month           => $expected_expiration_dt->month,
-        year            => $expected_expiration_dt->year,
-        title           => 'holidayTest_exp',
-        description     => 'holidayDesc on expiration date'
-    );
+    $calendar->edit_holiday( {
+        title           => 'holidayTest+expected_expiration',
+        holiday_type    => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
+        start_date      => $expected_expiration_dt,
+        end_date        => $expected_expiration_dt,
+    } );
+
     # Expiration date will be the day after
     test_debarment_on_checkout(
         {
             item            => $item_1,
             library         => $library,
             patron          => $patron,
-            due_date        => $five_days_ago,
+            return_date     => $in_five_days,
             expiration_date => $expected_expiration_dt->clone->add( days => 1 ),
         }
     );
@@ -2416,7 +2413,7 @@ subtest 'AddReturn + suspension_chargeperiod' => sub {
             library         => $library,
             patron          => $patron,
             return_date     => $now->clone->add(days => 5),
-            expiration_date => $now->clone->add(days => 5 + (5 * 2 - 1) ),
+            expiration_date => $now->clone->add(days => 5 + (5 * 2 - 1) + 1 ), # We add an extra +1 because of the holiday
         }
     );
 
@@ -2427,7 +2424,7 @@ subtest 'AddReturn + suspension_chargeperiod' => sub {
             patron          => $patron,
             due_date        => $now->clone->add(days => 1),
             return_date     => $now->clone->add(days => 5),
-            expiration_date => $now->clone->add(days => 5 + (4 * 2 - 1) ),
+            expiration_date => $now->clone->add(days => 5 + (4 * 2 - 1) + 1 ),
         }
     );
 
diff --git a/t/db_dependent/Circulation/CalcDateDue.t b/t/db_dependent/Circulation/CalcDateDue.t
index 8085dbd486..356615f16d 100755
--- a/t/db_dependent/Circulation/CalcDateDue.t
+++ b/t/db_dependent/Circulation/CalcDateDue.t
@@ -8,7 +8,8 @@ use DBI;
 use DateTime;
 use t::lib::Mocks;
 use t::lib::TestBuilder;
-use C4::Calendar;
+use Koha::DateUtils;
+use Koha::DiscreteCalendar;
 
 use Koha::CirculationRules;
 
@@ -47,12 +48,11 @@ my $cache = Koha::Caches->get_instance();
 my $key   = $branchcode . "_holidays";
 $cache->clear_from_cache($key);
 
-my $dateexpiry = '2013-01-01';
-
+my $dateexpiry = dt_from_string->truncate(to => 'day')->add(days => 30, hours => 23, minutes => 59)->iso8601;
 my $borrower = {categorycode => 'B', dateexpiry => $dateexpiry};
-my $start_date = DateTime->new({year => 2013, month => 2, day => 9});
+my $start_date =dt_from_string->truncate(to => 'day')->add(days => 60);
 my $date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower );
-is($date, $dateexpiry . 'T23:59:00', 'date expiry');
+is($date, $dateexpiry, 'date expiry');
 $date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower, 1 );
 
 
@@ -61,31 +61,30 @@ t::lib::Mocks::mock_preference('ReturnBeforeExpiry', 1);
 t::lib::Mocks::mock_preference('useDaysMode', 'noDays');
 
 $borrower = {categorycode => 'B', dateexpiry => $dateexpiry};
-$start_date = DateTime->new({year => 2013, month => 2, day => 9});
+$start_date =dt_from_string->truncate(to => 'day')->add(days => 60);
 $date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower );
-is($date, $dateexpiry . 'T23:59:00', 'date expiry with useDaysMode to noDays');
+is($date, $dateexpiry, 'date expiry with useDaysMode to noDays');
 
 # Let's add a special holiday on 2013-01-01. With ReturnBeforeExpiry and
 # useDaysMode different from 'Days', return should forward the dateexpiry.
-my $calendar = C4::Calendar->new(branchcode => $branchcode);
-$calendar->insert_single_holiday(
-    day             => 1,
-    month           => 1,
-    year            => 2013,
-    title           =>'holidayTest',
-    description     => 'holidayDesc'
-);
+my $calendar = Koha::DiscreteCalendar->new({branchcode => $branchcode});
+$calendar->edit_holiday({
+    title => 'holidayTest',
+    holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
+    start_date => dt_from_string->add(days=>30),
+    end_date => dt_from_string->add(days=>30)
+});
 $date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower );
-is($date, '2012-12-31T23:59:00', 'date expiry should be 2013-01-01 -1 day');
-$calendar->insert_single_holiday(
-    day             => 31,
-    month           => 12,
-    year            => 2012,
-    title           =>'holidayTest',
-    description     => 'holidayDesc'
-);
+is($date, dt_from_string->truncate(to => 'day')->add(days=>29, hours=>23, minutes=>59), 'date expiry should be 2013-01-01 -1 day');
+
+$calendar->edit_holiday({
+    title => 'holidayTest',
+    holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
+    start_date => dt_from_string->add(days => 29),
+    end_date => dt_from_string->add(days => 29)
+});
 $date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower );
-is($date, '2012-12-30T23:59:00', 'date expiry should be 2013-01-01 -2 day');
+is($date, dt_from_string->truncate(to => 'day')->add(days=> 28, hours=>23, minutes=>59), 'date expiry should be 2013-01-01 -2 day');
 
 
 $date = C4::Circulation::CalcDateDue( $start_date, $itemtype, $branchcode, $borrower, 1 );
diff --git a/t/db_dependent/Circulation/CalcFine.t b/t/db_dependent/Circulation/CalcFine.t
index 85f38ea42e..1e5f279a2f 100755
--- a/t/db_dependent/Circulation/CalcFine.t
+++ b/t/db_dependent/Circulation/CalcFine.t
@@ -81,17 +81,8 @@ subtest 'Test basic functionality' => sub {
         },
     );
 
-    my $start_dt = DateTime->new(
-        year       => 2000,
-        month      => 1,
-        day        => 1,
-    );
-
-    my $end_dt = DateTime->new(
-        year       => 2000,
-        month      => 1,
-        day        => 30,
-    );
+    my $start_dt = DateTime->today;
+    my $end_dt = DateTime->today->add(days => 29);
 
     my ($amount) = CalcFine( $item->unblessed, $patron->{categorycode}, $branch->{branchcode}, $start_dt, $end_dt );
 
@@ -121,17 +112,8 @@ subtest 'Test cap_fine_to_replacement_price' => sub {
         }
     );
 
-    my $start_dt = DateTime->new(
-        year       => 2000,
-        month      => 1,
-        day        => 1,
-    );
-
-    my $end_dt = DateTime->new(
-        year       => 2000,
-        month      => 1,
-        day        => 30,
-    );
+    my $start_dt = DateTime->today;
+    my $end_dt = DateTime->today->add(days => 29);
 
     my $item = $builder->build_sample_item(
         {
diff --git a/t/db_dependent/DiscreteCalendar.t b/t/db_dependent/DiscreteCalendar.t
new file mode 100644
index 0000000000..99582926b5
--- /dev/null
+++ b/t/db_dependent/DiscreteCalendar.t
@@ -0,0 +1,462 @@
+#!/usr/bin/perl
+
+# Copyright 2015 Koha Development team
+#
+# This file is part of Koha
+#
+# Koha is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# Koha is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Koha; if not, see <http://www.gnu.org/licenses>.
+
+use Modern::Perl;
+use Test::More tests => 50;
+use Test::MockModule;
+
+use C4::Context;
+use C4::Output;
+use Koha::DateUtils;
+use Koha::DiscreteCalendar;
+
+
+my $module_context = new Test::MockModule('C4::Context');
+my $today = DateTime->today;
+my $schema = Koha::Database->new->schema;
+$schema->storage->txn_begin;
+
+my $branch1 = "Library1";
+my $branch2 = "Library2";
+
+$schema->resultset('DiscreteCalendar')->search({
+    branchcode  =>''
+})->update_all({
+    is_opened    => 1,
+    holiday_type => '',
+    note        => '',
+    open_hour    => '08:00:00',
+    close_hour   => '16:00:00'
+});
+
+#Added entries for the branches.
+Koha::DiscreteCalendar->add_new_branch('', $branch1);
+Koha::DiscreteCalendar->add_new_branch('', $branch2);
+
+isnt($branch1,'', "First branch to do tests. BranchCode => $branch1");
+isnt($branch2,'', "Second branch to do tests. BranchCode => $branch2");
+
+#2 Calendars to make sure branches are treated separately.
+my $calendar = Koha::DiscreteCalendar->new({branchcode => $branch1});
+my $calendar2 = Koha::DiscreteCalendar->new({branchcode => $branch2});
+
+my $unique_holiday = DateTime->today;
+$calendar->edit_holiday({
+    title => "Single holiday Today",
+    holiday_type=> $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
+    start_date=>$unique_holiday,
+    end_date=>$unique_holiday
+});
+is($calendar->is_opened($unique_holiday), 0, "Branch closed today : $unique_holiday");
+
+my @unique_holidays = $calendar->get_unique_holidays();
+is(scalar @unique_holidays, 1, "Set of exception holidays at 1");
+
+my $yesterday = DateTime->today->subtract(days => 1);
+$calendar->edit_holiday({
+    title => "Single holiday Today",
+    holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
+    start_date => $yesterday,
+    end_date => $yesterday
+});
+is($calendar->is_opened($yesterday), 1, "Cannot edit dates in the past without override : $yesterday is not a holiday");
+
+$calendar->edit_holiday({
+    title => "Single holiday Today",
+    holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
+    start_date => $yesterday,
+    end_date => $yesterday,
+    override => 1
+});
+is($calendar->is_opened($yesterday), 0, "Can edit dates in the past without override : $yesterday is a holiday");
+
+
+my $days_between_start = DateTime->today;
+my $days_between_end = DateTime->today->add(days => 6);
+my $days_between = $calendar->days_between($days_between_start, $days_between_end)->in_units('days');
+is($days_between, 5, "Days between skips holiday");
+
+my $repeatable_holiday = DateTime->today->add(days => 1);
+$calendar->edit_holiday({
+    title => "repeatable",
+    holiday_type=> $Koha::DiscreteCalendar::HOLIDAYS->{REPEATABLE},
+    start_date=>$repeatable_holiday,
+    end_date=>$repeatable_holiday
+});
+is($calendar->is_opened($repeatable_holiday), 0, "Branch not opened on $repeatable_holiday");
+
+my @repeatable_holidays = $calendar->get_repeatable_holidays();
+is(scalar @repeatable_holidays, 1, "Set of repeatable holidays at 1");
+
+# 1 being mysql DAYOFWEEK() for Sunday
+$calendar->edit_holiday({
+    title => "Weekly",
+    weekday=>1,
+    holiday_type=>$Koha::DiscreteCalendar::HOLIDAYS->{WEEKLY},
+    start_date=>$today,
+    end_date=>$today
+});
+my @weekly_holidays = $calendar->get_week_days_holidays();
+is(scalar @weekly_holidays, 1, "Set of weekly holidays at 1");
+
+my $sunday = DateTime->today->add(days =>( 7 - $today->day_of_week));
+is($calendar->is_opened($sunday), 0, "Branch not opened on " . $sunday->day_name ."s");
+
+my $unique_holiday_range_start = DateTime->today->add(days => 2);
+my $unique_holiday_range_end = DateTime->today->add(days => 7);
+$calendar->edit_holiday({
+    title => "Single holiday range",
+    holiday_type=>"E",
+    start_date=>$unique_holiday_range_start,
+    end_date=>$unique_holiday_range_end
+});
+@unique_holidays = $calendar->get_unique_holidays();
+is(scalar @unique_holidays, 8, "Set of exception holidays at 7");
+
+my $repeatable_holiday_range_start = DateTime->today->add(days => 8);
+my $repeatable_holiday_range_end = DateTime->today->add(days => 13);
+$calendar->edit_holiday({
+    title => "Repeatable holiday range",
+    holiday_type=>$Koha::DiscreteCalendar::HOLIDAYS->{REPEATABLE},
+    start_date=>$repeatable_holiday_range_start,
+    end_date=>$repeatable_holiday_range_end
+});
+@repeatable_holidays = $calendar->get_repeatable_holidays();
+is(scalar @repeatable_holidays, 7, "Set of repeatable holidays at 7");
+
+#Hourly loan
+# ex :
+# item due      : 2017-01-24 11:00:00
+# item returned : 2017-01-26 10:00:00
+# Branch closed : 2017-01-25
+# Open/close hours : 08:00 to 16:00 (8h day)
+# Hours due : 5 hours on 2017-01-24 + 2 hours on 2017-01-26 = 7hours
+
+my $open_hours_since_start = dt_from_string->truncate(to => 'day')->add(days => 40, hours => 11);
+my $open_hours_since_end = dt_from_string->truncate(to => 'day')->add(days => 42, hours => 10);
+my $holiday_between =  dt_from_string->truncate(to => 'day')->add(days => 41);
+$calendar->edit_holiday({
+    title => '',
+    holiday_type=>$Koha::DiscreteCalendar::HOLIDAYS->{NONE},
+    start_date=>$open_hours_since_start,
+    end_date=>$open_hours_since_end
+});
+
+$calendar->edit_holiday({
+    title => "Single holiday",
+    holiday_type=>$Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
+    start_date=> $holiday_between,
+    end_date=>$holiday_between
+});
+my $open_hours_between = $calendar->open_hours_between($open_hours_since_start, $open_hours_since_end);
+is($open_hours_between, 7, "7 Hours open between $open_hours_since_start and $open_hours_since_end");
+
+my $christmas = DateTime->new(
+    year  => $today->year(),
+    month => 12,
+    day   => 25,
+);
+$calendar->edit_holiday({
+    title => "Christmas",
+    holiday_type=>$Koha::DiscreteCalendar::HOLIDAYS->{REPEATABLE},
+    start_date=>$christmas,
+    end_date=>$christmas,
+    override => 1, # necessary if unit tests are run between Christmas and the first of the year
+});
+is($calendar->is_opened($christmas), 0, "Branch closed on Christmas : $christmas");
+
+my $newyear = DateTime->new(
+    year  => $today->year() +1,
+    month => 1,
+    day   => 1,
+);
+
+$calendar->edit_holiday({
+    title => "New Year",
+    holiday_type=>$Koha::DiscreteCalendar::HOLIDAYS->{REPEATABLE},
+    start_date=>$newyear,
+    end_date=>$newyear
+});
+is($calendar->is_opened($newyear), 0, "Branch closed on New Year : $newyear");
+
+#Hours between :
+my $date_due = DateTime->today->add(days =>1);
+my $date_returned = DateTime->today->add(hours => 8);
+my $hours_between = $calendar->hours_between($date_due, $date_returned);
+
+is($hours_between->in_units('hours'), 16, "Date due $date_due, returned $date_returned, 16 hours in advance");
+
+$date_due = DateTime->today->add(days =>1);
+$date_returned = DateTime->today->add(days => 2, hours=> 8);
+$hours_between = $calendar->hours_between($date_due, $date_returned);
+
+is($hours_between->in_units('hours'), -16, "Date due $date_due, returned $date_returned, 16 hours late");
+
+
+#delete holidays
+is($calendar->is_opened($today), 0, "Today is closed");
+$calendar->edit_holiday({
+    title => '',
+    holiday_type=>$Koha::DiscreteCalendar::HOLIDAYS->{NONE},
+    start_date=>$unique_holiday,
+    end_date=>$unique_holiday
+});
+is($calendar->is_opened($today), 1, "Today's holiday was removed");
+
+$calendar->edit_holiday({
+    title => '',
+    holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{NONE},
+    start_date => $yesterday,
+    end_date => $yesterday,
+    override => 1
+});
+is($calendar->is_opened($yesterday), 1, "Yesterdays's holiday was removed with override");
+
+my $new_open_hours = '08:00';
+$calendar->edit_holiday({
+    title => '',
+    holiday_type=>$Koha::DiscreteCalendar::HOLIDAYS->{NONE},
+    open_hour=>$new_open_hours,
+    close_hour=>'',
+    start_date=>$today,
+    end_date=>$today
+});
+is($calendar->get_date_info($today, $branch1)->{open_hour}, '08:00:00', "Open hour changed to 08:00:00");
+
+isnt($calendar->get_date_info($today, $branch1)->{close_hour}, '', "Close hour not removed");
+
+my $delete_range_end = DateTime->today->add(days => 30);
+$calendar->edit_holiday({
+    title => '',
+    holiday_type=>$Koha::DiscreteCalendar::HOLIDAYS->{NONE},
+    start_date=>$today,
+    end_date=>$delete_range_end
+});
+
+
+is($calendar->is_opened($today), 1, "Today's holiday was already removed");
+is($calendar->is_opened(DateTime->today->add(days => 1)), 1, "Tomorrow's holiday was removed");
+is($calendar->is_opened($delete_range_end), 1, "End of range holiday was removed");
+
+#Check if other branch was affected
+my @unique_holidays_branch2 = $calendar2->get_unique_holidays();
+is(scalar @unique_holidays_branch2, 0, "Set of exception holidays at 0 for branch => $branch2");
+my @repeatable_holidays_branch2 = $calendar2->get_repeatable_holidays();
+is(scalar @repeatable_holidays_branch2, 0, "Set of repeatable holidays at 0 for branch => $branch2");
+my @weekly_holidays_branch2 = $calendar2->get_week_days_holidays();
+is(scalar @weekly_holidays_branch2, 0, "Set of weekly holidays at 0 for branch => $branch2");
+
+
+#Tests for addDate()
+
+my $one_day_dur = DateTime::Duration->new( days => 1 );
+my $negative_one_day_dur = DateTime::Duration->new( days => - 1 );
+my $two_day_dur = DateTime::Duration->new( days => 2 );
+my $seven_day_dur = DateTime::Duration->new( days => 7 );
+
+#Preference useDaysMode Calendar
+$calendar->{days_mode} = 'Calendar';
+$unique_holiday->add(days => 1);
+my $tomorrow = DateTime->today->add(days => 1);
+$calendar->edit_holiday({
+    title => "Single holiday Today",
+    holiday_type=>$Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
+    start_date=>$tomorrow,
+    end_date=>$tomorrow
+});
+
+is($calendar->addDate( $today, $one_day_dur, 'days' )->ymd(),
+    $today->add(days => 2)->ymd(),
+    'Single day add (Calendar)' );
+
+is($calendar->addDate( $today, $two_day_dur, 'days' )->ymd(),
+    $today->add(days => 2)->ymd(),
+    'Two days add, skips holiday (Calendar)' );
+
+cmp_ok($calendar->addDate( $today, $seven_day_dur, 'days' )->ymd(), 'eq',
+    $today->add(days => 7)->ymd(),
+    'Add 7 days (Calendar)' );
+#Closed Sunday
+$calendar->edit_holiday({
+    title => "Weekly",
+    weekday=>1,
+    holiday_type=>$Koha::DiscreteCalendar::HOLIDAYS->{WEEKLY},
+    start_date=>$today,
+    end_date=>$today
+});
+is( $calendar->addDate( $sunday, $one_day_dur, 'days' )->day_of_week, 1,
+    'addDate skips closed Sunday (Calendar)' );
+#to remove the closed sundays
+$today = DateTime->today;
+$calendar->edit_holiday({
+    title => '',
+    holiday_type=>$Koha::DiscreteCalendar::HOLIDAYS->{NONE},
+    start_date=>$today,
+    end_date=>$delete_range_end
+});
+
+is( $calendar->addDate($today, $negative_one_day_dur , 'days')->ymd(),
+    $today->add(days => - 1)->ymd(),
+    'Negative call to addDate (Calendar)' );
+
+#Preference useDaysMode DateDue
+$calendar->{days_mode} = 'Datedue';
+#clean everything
+$today = DateTime->today;
+$calendar->edit_holiday({
+    title => '',
+    holiday_type=>$Koha::DiscreteCalendar::HOLIDAYS->{NONE},
+    start_date=>$today,
+    end_date=>$delete_range_end
+});
+$calendar->edit_holiday({
+    title => "Single holiday Today",
+    holiday_type=>$Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
+    start_date=>$tomorrow,
+    end_date=>$tomorrow
+});
+
+is($calendar->addDate( $today, $one_day_dur, 'days' )->ymd(),
+    $today->add(days => 2)->ymd(),
+    'Single day add' );
+
+is($calendar->addDate( $today, $two_day_dur, 'days' )->ymd(),
+    $today->add(days => 2)->ymd(),
+    'Two days add, skips holiday (Datedue)' );
+
+cmp_ok($calendar->addDate( $today, $seven_day_dur, 'days' )->ymd(), 'eq',
+    $today->add(days => 7)->ymd(),
+    'Add 7 days (Datedue)' );
+#Closed Sunday
+$calendar->edit_holiday({
+    title => "Weekly",
+    weekday=>1,
+    holiday_type=> $Koha::DiscreteCalendar::HOLIDAYS->{WEEKLY},
+    start_date=>$today,
+    end_date=>$today
+});
+is( $calendar->addDate( $sunday, $one_day_dur, 'days' )->day_of_week, 1,
+    'addDate skips closed Sunday (Datedue)' );
+#to remove the closed sundays
+$today = DateTime->today;
+$calendar->edit_holiday({
+    title => '',
+    holiday_type=>$Koha::DiscreteCalendar::HOLIDAYS->{NONE},
+    start_date=>$today,
+    end_date=>$delete_range_end
+});
+
+is( $calendar->addDate($today, $negative_one_day_dur , 'days')->ymd(),
+    $today->add(days => - 1)->ymd(),
+    'Negative call to addDate (Datedue)' );
+
+#Preference useDaysMode Days
+$calendar->{days_mode} = 'Days';
+#clean everything
+$today = DateTime->today;
+$calendar->edit_holiday({
+    title => '',
+    holiday_type=>$Koha::DiscreteCalendar::HOLIDAYS->{NONE},
+    start_date=>$today,
+    end_date=>$delete_range_end
+});
+$calendar->edit_holiday({
+    title => "Single holiday Today",
+    holiday_type=>$Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
+    start_date=>$tomorrow,
+    end_date=>$tomorrow
+});
+is($calendar->addDate( $today, $one_day_dur, 'days' )->ymd(),
+    $today->add(days => 1)->ymd(),
+    'Single day add' );
+
+is($calendar->addDate( $today, $two_day_dur, 'days' )->ymd(),
+    $today->add(days => 2)->ymd(),
+    'Two days add, skips holiday (Days)' );
+
+cmp_ok($calendar->addDate( $today, $seven_day_dur, 'days' )->ymd(), 'eq',
+    $today->add(days => 7)->ymd(),
+    'Add 7 days (Days)' );
+#Closed Sunday
+$calendar->edit_holiday({
+    title => "Weekly",
+    weekday => 1,
+    holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{WEEKLY},
+    start_date => $today,
+    end_date => $today
+});
+is( $calendar->addDate( $sunday, $one_day_dur, 'days' )->day_of_week, 1,
+    'addDate skips closed Sunday (Days)' );
+
+is( $calendar->addDate($today, $negative_one_day_dur , 'days')->ymd(),
+    $today->add(days => - 1)->ymd(),
+    'Negative call to addDate (Days)' );
+
+#Days_forward
+
+#clean the range
+$today = DateTime->today;
+$calendar->edit_holiday({
+    title => '',
+    holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{NONE},
+    start_date => $today,
+    end_date => DateTime->today->add(days => 20)
+});
+my $forwarded_dt = $calendar->days_forward($today, 10);
+
+my $expected = $today->clone;
+$expected->add(days => 10);
+is($forwarded_dt->ymd, $expected->ymd, 'With no holiday on the perioddays_forward should add 10 days');
+
+#Added a holiday
+$unique_holiday = DateTime->today->add(days => 15);
+$calendar->edit_holiday({
+    title => "Single holiday Today",
+    holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
+    start_date => $unique_holiday,
+    end_date => $unique_holiday
+});
+$forwarded_dt = $calendar->days_forward($today, 20);
+
+$expected->add(days => 11);
+is($forwarded_dt->ymd, $expected->ymd, 'With holiday on the perioddays_forward should add 20 days + 1 day for holiday');
+
+$forwarded_dt = $calendar->days_forward($today, 0);
+is($forwarded_dt->ymd, $today->ymd, '0 day should return start dt');
+
+$forwarded_dt = $calendar->days_forward($today, -2);
+is($forwarded_dt->ymd, $today->ymd, 'negative day should return start dt');
+
+#copying a branch to an other
+is($calendar2->is_opened($tomorrow), 1, "$branch2 opened tomorrow");
+#Added a goliday tomorrow for branch1
+$calendar->edit_holiday({
+    title => "Single holiday Today",
+    holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
+    start_date => $tomorrow,
+    end_date => $tomorrow
+});
+#Copy dates from branch1 to branch2
+$calendar->copy_to_branch($branch2);
+#Check if branch2 is also closed tomorrow after copying from branch1
+is($calendar2->is_opened($tomorrow), 0, "$branch2 close tomorrow after copying from $branch1");
+
+$schema->storage->txn_rollback;
+
+1;
diff --git a/t/db_dependent/Fines.t b/t/db_dependent/Fines.t
index 76cc0638a9..97ff83b220 100755
--- a/t/db_dependent/Fines.t
+++ b/t/db_dependent/Fines.t
@@ -1,6 +1,7 @@
 #!/usr/bin/perl
 
 use Modern::Perl;
+use DateTime;
 
 use C4::Context;
 use C4::Overdues;
@@ -33,13 +34,13 @@ my $issuingrule = Koha::CirculationRules->set_rules(
 
 ok( $issuingrule, 'Issuing rule created' );
 
-my $period_start = dt_from_string('2000-01-01');
-my $period_end = dt_from_string('2000-01-05');
+my $period_start = dt_from_string;
+my $period_end = dt_from_string->add(days => 4);
 
 my ( $fine ) = CalcFine( {}, q{}, q{}, $period_start, $period_end  );
 is( $fine, 0, '4 days overdue, charge period 7 days, charge at end of interval gives fine of $0' );
 
-$period_end = dt_from_string('2000-01-10');
+$period_end = dt_from_string->add(days => 9);
 ( $fine ) = CalcFine( {}, q{}, q{}, $period_start, $period_end  );
 is( $fine, 1, '9 days overdue, charge period 7 days, charge at end of interval gives fine of $1' );
 
@@ -55,10 +56,10 @@ $issuingrule = Koha::CirculationRules->set_rules(
     }
 );
 
-$period_end = dt_from_string('2000-01-05');
+$period_end = dt_from_string->add(days => 4);
 ( $fine ) = CalcFine( {}, q{}, q{}, $period_start, $period_end  );
 is( $fine, 1, '4 days overdue, charge period 7 days, charge at start of interval gives fine of $1' );
 
-$period_end = dt_from_string('2000-01-10');
+$period_end = dt_from_string->add(days => 9);
 ( $fine ) = CalcFine( {}, q{}, q{}, $period_start, $period_end  );
 is( $fine, 2, '9 days overdue, charge period 7 days, charge at start of interval gives fine of $2' );
diff --git a/t/db_dependent/Hold.t b/t/db_dependent/Hold.t
index 5b71b64292..c2ddaf451d 100755
--- a/t/db_dependent/Hold.t
+++ b/t/db_dependent/Hold.t
@@ -22,7 +22,7 @@ use C4::Context;
 use C4::Biblio qw( AddBiblio );
 use Koha::Database;
 use Koha::Libraries;
-use C4::Calendar;
+use Koha::DiscreteCalendar;
 use Koha::Patrons;
 use Koha::Holds;
 use Koha::Item;
@@ -68,7 +68,7 @@ my $hold = Koha::Hold->new(
     {
         biblionumber   => $biblionumber,
         itemnumber     => $item->id(),
-        reservedate    => '2017-01-01',
+        reservedate    => dt_from_string->subtract(days => 2),
         waitingdate    => '2000-01-01',
         borrowernumber => $borrower->{borrowernumber},
         branchcode     => $branches[1]->{branchcode},
@@ -77,11 +77,19 @@ my $hold = Koha::Hold->new(
 );
 $hold->store();
 
-my $b1_cal = C4::Calendar->new( branchcode => $branches[1]->{branchcode} );
-$b1_cal->insert_single_holiday( day => 2, month => 1, year => 2017, title => "Morty Day", description => "Rick" ); #Add a holiday
+my $b1_cal = Koha::DiscreteCalendar->new({ branchcode => $branches[1]->{branchcode} });
+my $holiday = dt_from_string->subtract(days => 1);
+$b1_cal->edit_holiday({
+    title => "Morty Day",
+    holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
+    start_date => $holiday,
+    end_date => $holiday,
+    override => 1
+}); #Add a holiday
+
 my $today = dt_from_string;
-is( $hold->age(), $today->delta_days( dt_from_string( '2017-01-01' ) )->in_units( 'days')  , "Age of hold is days from reservedate to now if calendar ignored");
-is( $hold->age(1), $today->delta_days( dt_from_string( '2017-01-01' ) )->in_units( 'days' ) - 1 , "Age of hold is days from reservedate to now minus 1 if calendar used");
+is( $hold->age(), $today->delta_days( dt_from_string->subtract(days => 2) )->in_units( 'days' ), "Age of hold is days from reservedate to now if calendar ignored");
+is( $hold->age(1), $today->delta_days( dt_from_string->subtract(days => 2) )->in_units( 'days' ) - 1, "Age of hold is days from reservedate to now minus 1 if calendar used");
 
 is( $hold->suspend, 0, "Hold is not suspended" );
 $hold->suspend_hold();
diff --git a/t/db_dependent/HoldsQueue.t b/t/db_dependent/HoldsQueue.t
index 358555e94b..19fa5dac78 100755
--- a/t/db_dependent/HoldsQueue.t
+++ b/t/db_dependent/HoldsQueue.t
@@ -11,7 +11,7 @@ use Modern::Perl;
 use Test::More tests => 57;
 use Data::Dumper;
 
-use C4::Calendar;
+use Koha::DiscreteCalendar;
 use C4::Context;
 use C4::Members;
 use C4::Circulation;
@@ -200,6 +200,21 @@ $library2 = $builder->build({
 $library3 = $builder->build({
     source => 'Branch',
 });
+
+$schema->resultset('DiscreteCalendar')->search({
+    branchcode  =>''
+})->update_all({
+    is_opened    => 1,
+    holiday_type => '',
+    note        => '',
+    open_hour    => '08:00:00',
+    close_hour   => '16:00:00'
+});
+
+Koha::DiscreteCalendar->new({ branchcode => '' })->add_new_branch('', $library1->{branchcode});
+Koha::DiscreteCalendar->new({ branchcode => '' })->add_new_branch('', $library2->{branchcode});
+Koha::DiscreteCalendar->new({ branchcode => '' })->add_new_branch('', $library3->{branchcode});
+
 @branchcodes = ( $library1->{branchcode}, $library2->{branchcode}, $library3->{branchcode} );
 
 my $category = $builder->build({ source => 'Category', value => {exclude_from_local_holds_priority => 0} } );
@@ -327,16 +342,17 @@ is( $holds_queue->[1]->{cardnumber}, $borrower2->{cardnumber}, "Holds queue fill
 # have 1 row in the holds queue
 t::lib::Mocks::mock_preference('HoldsQueueSkipClosed', 1);
 my $today = dt_from_string();
-C4::Calendar->new( branchcode => $branchcodes[0] )->insert_single_holiday(
-    day         => $today->day(),
-    month       => $today->month(),
-    year        => $today->year(),
-    title       => "$today",
-    description => "$today",
-);
+
 # If the test below is removed, aother tests using the holiday will fail. For some reason if we call is_holiday now
 # the holiday will get set in cache correctly, but not if we let C4::HoldsQueue call is_holiday instead.
-is( Koha::Calendar->new( branchcode => $branchcodes[0] )->is_holiday( $today ), 1, 'Is today a holiday for pickup branch' );
+Koha::DiscreteCalendar->new({ branchcode => $branchcodes[0] })->edit_holiday({
+    title        => "Today",
+    holiday_type => $Koha::DiscreteCalendar::HOLIDAYS->{EXCEPTION},
+    start_date   => $today,
+    end_date     => $today
+});
+
+is( Koha::DiscreteCalendar->new({ branchcode => $branchcodes[0] })->is_holiday( $today ), 1, 'Is today a holiday for pickup branch' );
 C4::HoldsQueue::CreateQueue();
 $holds_queue = $dbh->selectall_arrayref("SELECT * FROM tmp_holdsqueue", { Slice => {} });
 is( scalar( @$holds_queue ), 1, "Holds not filled with items from closed libraries" );
diff --git a/t/db_dependent/Holidays.t b/t/db_dependent/Holidays.t
deleted file mode 100755
index ced8bd538b..0000000000
--- a/t/db_dependent/Holidays.t
+++ /dev/null
@@ -1,282 +0,0 @@
-#!/usr/bin/perl
-
-# This file is part of Koha.
-#
-# Koha is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# Koha is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Koha; if not, see <http://www.gnu.org/licenses>.
-
-use Modern::Perl;
-
-use Test::More tests => 12;
-
-use DateTime;
-use DateTime::TimeZone;
-
-use t::lib::TestBuilder;
-use C4::Context;
-use Koha::Database;
-use Koha::DateUtils;
-
-
-BEGIN {
-    use_ok('Koha::Calendar');
-    use_ok('C4::Calendar');
-}
-
-my $schema = Koha::Database->new->schema;
-my $dbh = C4::Context->dbh;
-my $builder = t::lib::TestBuilder->new;
-
-subtest 'is_holiday timezone tests' => sub {
-
-    plan tests => 1;
-
-    $schema->storage->txn_begin;
-
-    $dbh->do("DELETE FROM special_holidays");
-    # Clear cache
-    Koha::Caches->get_instance->flush_all;
-
-    # Artificially set timezone
-    my $timezone = 'America/Santiago';
-    $ENV{TZ} = $timezone;
-    use POSIX qw(tzset);
-    tzset;
-
-    my $branch = $builder->build( { source => 'Branch' } )->{branchcode};
-    my $calendar = Koha::Calendar->new( branchcode => $branch );
-
-    C4::Calendar->new( branchcode => $branch )->insert_exception_holiday(
-        day         => 6,
-        month       => 9,
-        year        => 2015,
-        title       => 'Invalid date',
-        description => 'Invalid date description',
-    );
-
-    my $exception_holiday = DateTime->new( day => 6, month => 9, year => 2015 );
-    my $now_dt            = DateTime->now;
-
-    my $diff;
-    eval { $diff = $calendar->days_between( $now_dt, $exception_holiday ) };
-    unlike(
-        $@,
-        qr/Invalid local time for date in time zone: America\/Santiago/,
-        'Avoid invalid datetime due to DST'
-    );
-
-    $schema->storage->txn_rollback;
-};
-
-$schema->storage->txn_begin;
-
-# Create two fresh branches for the tests
-my $branch_1 = $builder->build({ source => 'Branch' })->{ branchcode };
-my $branch_2 = $builder->build({ source => 'Branch' })->{ branchcode };
-
-C4::Calendar->new( branchcode => $branch_1 )->insert_week_day_holiday(
-    weekday     => 0,
-    title       => '',
-    description => 'Sundays',
-);
-
-my $holiday2add = dt_from_string("2015-01-01");
-C4::Calendar->new( branchcode => $branch_1 )->insert_day_month_holiday(
-    day         => $holiday2add->day(),
-    month       => $holiday2add->month(),
-    year        => $holiday2add->year(),
-    title       => '',
-    description => "New Year's Day",
-);
-$holiday2add = dt_from_string("2014-12-25");
-C4::Calendar->new( branchcode => $branch_1 )->insert_day_month_holiday(
-    day         => $holiday2add->day(),
-    month       => $holiday2add->month(),
-    year        => $holiday2add->year(),
-    title       => '',
-    description => 'Christmas',
-);
-
-my $koha_calendar = Koha::Calendar->new( branchcode => $branch_1 );
-my $c4_calendar = C4::Calendar->new( branchcode => $branch_1 );
-
-isa_ok( $koha_calendar, 'Koha::Calendar', 'Koha::Calendar class returned' );
-isa_ok( $c4_calendar,   'C4::Calendar',   'C4::Calendar class returned' );
-
-my $sunday = DateTime->new(
-    year  => 2011,
-    month => 6,
-    day   => 26,
-);
-my $monday = DateTime->new(
-    year  => 2011,
-    month => 6,
-    day   => 27,
-);
-my $christmas = DateTime->new(
-    year  => 2032,
-    month => 12,
-    day   => 25,
-);
-my $newyear = DateTime->new(
-    year  => 2035,
-    month => 1,
-    day   => 1,
-);
-
-is( $koha_calendar->is_holiday($sunday),    1, 'Sunday is a closed day' );
-is( $koha_calendar->is_holiday($monday),    0, 'Monday is not a closed day' );
-is( $koha_calendar->is_holiday($christmas), 1, 'Christmas is a closed day' );
-is( $koha_calendar->is_holiday($newyear),   1, 'New Years day is a closed day' );
-
-$dbh->do("DELETE FROM repeatable_holidays");
-$dbh->do("DELETE FROM special_holidays");
-
-my $custom_holiday = DateTime->new(
-    year  => 2013,
-    month => 11,
-    day   => 12,
-);
-
-my $today = dt_from_string();
-C4::Calendar->new( branchcode => $branch_2 )->insert_single_holiday(
-    day         => $today->day(),
-    month       => $today->month(),
-    year        => $today->year(),
-    title       => "$today",
-    description => "$today",
-);
-
-is( Koha::Calendar->new( branchcode => $branch_2 )->is_holiday( $today ), 1, "Today is a holiday for $branch_2" );
-is( Koha::Calendar->new( branchcode => $branch_1 )->is_holiday( $today ), 0, "Today is not a holiday for $branch_1");
-
-$schema->storage->txn_rollback;
-
-subtest 'copy_to_branch' => sub {
-
-    plan tests => 8;
-
-    $schema->storage->txn_begin;
-
-    my $branch1 = $builder->build( { source => 'Branch' } )->{ branchcode };
-    my $calendar1 = C4::Calendar->new( branchcode => $branch1 );
-    my $sunday = dt_from_string("2020-03-15");
-    $calendar1->insert_week_day_holiday(
-        weekday     => 0,
-        title       => '',
-        description => 'Sundays',
-    );
-
-    my $day_month = dt_from_string("2020-03-17");
-    $calendar1->insert_day_month_holiday(
-        day         => $day_month->day(),
-        month       => $day_month->month(),
-        year        => $day_month->year(),
-        title       => '',
-        description => "",
-    );
-
-    my $future_date = dt_from_string("9999-12-31");
-    $calendar1->insert_single_holiday(
-        day         => $future_date->day(),
-        month       => $future_date->month(),
-        year        => $future_date->year(),
-        title       => "",
-        description => "",
-    );
-
-    my $future_exception = dt_from_string("9999-12-30");
-    $calendar1->insert_exception_holiday(
-        day         => $future_exception->day(),
-        month       => $future_exception->month(),
-        year        => $future_exception->year(),
-        title       => "",
-        description => "",
-    );
-
-    my $past_date = dt_from_string("2019-11-20");
-    $calendar1->insert_single_holiday(
-        day         => $past_date->day(),
-        month       => $past_date->month(),
-        year        => $past_date->year(),
-        title       => "",
-        description => "",
-    );
-
-    my $past_exception = dt_from_string("2020-03-09");
-    $calendar1->insert_exception_holiday(
-        day         => $past_exception->day(),
-        month       => $past_exception->month(),
-        year        => $past_exception->year(),
-        title       => "",
-        description => "",
-    );
-
-    my $branch2 = $builder->build( { source => 'Branch' } )->{branchcode};
-
-    C4::Calendar->new( branchcode => $branch1 )->copy_to_branch( $branch2 );
-
-    my $calendar2 = C4::Calendar->new( branchcode => $branch2 );
-    my $exceptions = $calendar2->get_exception_holidays;
-
-    is( $calendar2->isHoliday( $sunday->day, $sunday->month, $sunday->year ), 1, "Weekday holiday copied to branch 2" );
-    is( $calendar2->isHoliday( $day_month->day, $day_month->month, $day_month->year ), 1, "Day/month holiday copied to branch 2" );
-    is( $calendar2->isHoliday( $future_date->day, $future_date->month, $future_date->year ), 1, "Single holiday copied to branch 2" );
-    is( ( grep { $_->{date} eq "9999-12-30"} values %$exceptions ), 1, "Exception holiday copied to branch 2" );
-    is( $calendar2->isHoliday( $past_date->day, $past_date->month, $past_date->year ), 0, "Don't copy past single holidays" );
-    is( ( grep { $_->{date} eq "2020-03-09"} values %$exceptions ), 0, "Don't copy past exception holidays " );
-
-    C4::Calendar->new( branchcode => $branch1 )->copy_to_branch( $branch2 );
-
-    #Select all rows with same values from database
-    my $dbh = C4::Context->dbh;
-    my $get_repeatable_holidays = "SELECT a.* FROM repeatable_holidays a
-        JOIN (SELECT branchcode, weekday, day, month, COUNT(*)
-        FROM repeatable_holidays
-        GROUP BY branchcode, weekday, day, month HAVING count(*) > 1) b
-        ON a.branchcode = b.branchcode
-        AND ( a.weekday = b.weekday OR (a.day = b.day AND a.month = b.month))
-        ORDER BY a.branchcode;";
-    my $sth  = $dbh->prepare($get_repeatable_holidays);
-    $sth->execute;
-
-    my @repeatable_holidays;
-    while(my $row = $sth->fetchrow_hashref){
-        push @repeatable_holidays, $row
-    }
-
-    is( scalar(@repeatable_holidays), 0, "None of the repeatable holidays were doubled");
-
-    my $get_special_holidays = "SELECT a.* FROM special_holidays a
-    JOIN (SELECT branchcode, day, month, year, isexception, COUNT(*)
-    FROM special_holidays
-    GROUP BY branchcode, day, month, year, isexception HAVING count(*) > 1) b
-    ON a.branchcode = b.branchcode
-    AND a.day = b.day AND a.month = b.month AND a.year = b.year AND a.isexception = b.isexception
-    ORDER BY a.branchcode;";
-    $sth  = $dbh->prepare($get_special_holidays);
-    $sth->execute;
-
-    my @special_holidays;
-    while(my $row = $sth->fetchrow_hashref){
-        push @special_holidays, $row
-    }
-
-    is( scalar(@special_holidays), 0, "None of the special holidays were doubled");
-
-    $schema->storage->txn_rollback;
-
-};
-
-# Clear cache
-Koha::Caches->get_instance->flush_all;
diff --git a/t/lib/TestBuilder.pm b/t/lib/TestBuilder.pm
index 26de949fb3..da7aa3db05 100644
--- a/t/lib/TestBuilder.pm
+++ b/t/lib/TestBuilder.pm
@@ -442,6 +442,7 @@ sub _gen_type {
         decimal          => \&_gen_real,
         double_precision => \&_gen_real,
 
+        time      => \&_gen_time,
         timestamp => \&_gen_datetime,
         datetime  => \&_gen_datetime,
         date      => \&_gen_date,
@@ -506,6 +507,11 @@ sub _gen_datetime {
     return $self->schema->storage->datetime_parser->format_datetime(dt_from_string);
 }
 
+sub _gen_time {
+    my ($self, $params) = @_;
+    return $self->schema->storage->datetime_parser->format_time(DateTime->now());
+}
+
 sub _gen_text {
     my ($self, $params) = @_;
     # From perldoc String::Random
-- 
2.17.1