Skip to content

Commit

Permalink
Fixes for New Zealand calendar, added Auckland
Browse files Browse the repository at this point in the history
  • Loading branch information
lballabio committed Dec 16, 2024
1 parent 324b391 commit 54580b9
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 25 deletions.
61 changes: 49 additions & 12 deletions ql/time/calendars/newzealand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,7 @@

namespace QuantLib {

NewZealand::NewZealand() {
// all calendar instances share the same implementation instance
static ext::shared_ptr<Calendar::Impl> impl(new NewZealand::Impl);
impl_ = impl;
}

bool NewZealand::Impl::isBusinessDay(const Date& date) const {
bool NewZealand::CommonImpl::isBusinessDay(const Date& date) const {
Weekday w = date.weekday();
Day d = date.dayOfMonth(), dd = date.dayOfYear();
Month m = date.month();
Expand All @@ -40,16 +34,14 @@ namespace QuantLib {
// Day after New Year's Day (possibly moved to Mon or Tuesday)
|| ((d == 2 || (d == 4 && (w == Monday || w == Tuesday))) &&
m == January)
// Anniversary Day, Monday nearest January 22nd
|| ((d >= 19 && d <= 25) && w == Monday && m == January)
// Waitangi Day. February 6th ("Mondayised" since 2013)
// Waitangi Day. February 6th (possibly moved to Monday since 2013)
|| (d == 6 && m == February)
|| ((d == 7 || d == 8) && w == Monday && m == February && y > 2013)
// Good Friday
|| (dd == em-3)
// Easter Monday
|| (dd == em)
// ANZAC Day. April 25th ("Mondayised" since 2013)
// ANZAC Day. April 25th (possibly moved to Monday since 2013)
|| (d == 25 && m == April)
|| ((d == 26 || d == 27) && w == Monday && m == April && y > 2013)
// Queen's Birthday, first Monday in June
Expand Down Expand Up @@ -81,9 +73,54 @@ namespace QuantLib {
|| (d == 14 && m == July && (y == 2023 || y == 2028))
|| (d == 15 && m == July && (y == 2039 || y == 2050))
|| (d == 18 && m == July && y == 2036)
|| (d == 19 && m == July && (y == 2041 || y == 2047)))
|| (d == 19 && m == July && (y == 2041 || y == 2047))
// Queen Elizabeth's funeral
|| (d == 26 && m == September && y == 2022))
return false; // NOLINT(readability-simplify-boolean-expr)
return true;
}

bool NewZealand::WellingtonImpl::isBusinessDay(const Date& date) const {
if (!NewZealand::CommonImpl::isBusinessDay(date))
return false;
Weekday w = date.weekday();
Day d = date.dayOfMonth();
Month m = date.month();
// Anniversary Day, Monday nearest January 22nd
if ((d >= 19 && d <= 25) && w == Monday && m == January)
return false; // NOLINT(readability-simplify-boolean-expr)
return true;
}

bool NewZealand::AucklandImpl::isBusinessDay(const Date& date) const {
if (!NewZealand::CommonImpl::isBusinessDay(date))
return false;
Weekday w = date.weekday();
Day d = date.dayOfMonth();
Month m = date.month();
// Anniversary Day, Monday nearest January 29nd
if ((d >= 26 && w == Monday && m == January)
|| (d == 1 && w == Monday && m == February))
return false; // NOLINT(readability-simplify-boolean-expr)
return true;
}


NewZealand::NewZealand(Market market) {
// all calendar instances for a given market share the same implementation instance
static auto wellingtonImpl = ext::make_shared<NewZealand::WellingtonImpl>();
static auto aucklandImpl = ext::make_shared<NewZealand::AucklandImpl>();

switch (market) {
case Wellington:
impl_ = wellingtonImpl;
break;
case Auckland:
impl_ = aucklandImpl;
break;
default:
QL_FAIL("unknown market");
}
}

}
44 changes: 31 additions & 13 deletions ql/time/calendars/newzealand.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,33 @@
namespace QuantLib {

//! New Zealand calendar
/*! Holidays:
/*! Common holidays:
<ul>
<li>Saturdays</li>
<li>Sundays</li>
<li>New Year's Day, January 1st (possibly moved to Monday or
Tuesday)</li>
<li>Day after New Year's Day, January 2st (possibly moved to
Monday or Tuesday)</li>
<li>Anniversary Day, Monday nearest January 22nd</li>
<li>Waitangi Day. February 6th</li>
<li>New Year's Day, January 1st (possibly moved to Monday or Tuesday)</li>
<li>Day after New Year's Day, January 2st (possibly moved to Monday or Tuesday)</li>
<li>Waitangi Day. February 6th (possibly moved to Monday since 2013)</li>
<li>Good Friday</li>
<li>Easter Monday</li>
<li>ANZAC Day. April 25th</li>
<li>ANZAC Day. April 25th (possibly moved to Monday since 2013)</li>
<li>Queen's Birthday, first Monday in June</li>
<li>Labour Day, fourth Monday in October</li>
<li>Christmas, December 25th (possibly moved to Monday or Tuesday)</li>
<li>Boxing Day, December 26th (possibly moved to Monday or
Tuesday)</li>
<li>Boxing Day, December 26th (possibly moved to Monday or Tuesday)</li>
<li>Matariki, in June or July, official calendar released for years 2022-2052</li>
</ul>
Additional holidays for Wellington:
<ul>
<li>Anniversary Day, Monday nearest January 22nd</li>
</ul>
Additional holidays for Auckland:
<ul>
<li>Anniversary Day, Monday nearest January 29nd</li>
</ul>
\note The holiday rules for New Zealand were documented by
David Gilbert for IDB (http://www.jrefinery.com/ibd/)
The Matariki holiday calendar has been released by the NZ Government
Expand All @@ -58,13 +65,24 @@ namespace QuantLib {
*/
class NewZealand : public Calendar {
private:
class Impl final : public Calendar::WesternImpl {
class CommonImpl : public Calendar::WesternImpl {
public:
bool isBusinessDay(const Date&) const override;
};
class WellingtonImpl final : public CommonImpl {
public:
std::string name() const override { return "New Zealand (Wellington)"; }
bool isBusinessDay(const Date&) const override;
};
class AucklandImpl final : public CommonImpl {
public:
std::string name() const override { return "New Zealand"; }
std::string name() const override { return "New Zealand (Auckland)"; }
bool isBusinessDay(const Date&) const override;
};
public:
NewZealand();
//! NZ calendars
enum Market { Wellington, Auckland };
NewZealand(Market market = Wellington);

Check notice on line 85 in ql/time/calendars/newzealand.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

ql/time/calendars/newzealand.hpp#L85

Class 'NewZealand' has a constructor with 1 argument that is not explicit.
};

}
Expand Down
82 changes: 82 additions & 0 deletions test-suite/calendars.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include <ql/time/calendars/japan.hpp>
#include <ql/time/calendars/jointcalendar.hpp>
#include <ql/time/calendars/mexico.hpp>
#include <ql/time/calendars/newzealand.hpp>
#include <ql/time/calendars/russia.hpp>
#include <ql/time/calendars/southkorea.hpp>
#include <ql/time/calendars/target.hpp>
Expand Down Expand Up @@ -3371,6 +3372,87 @@ BOOST_AUTO_TEST_CASE(testMexicoInaugurationDay) {
}
}


BOOST_AUTO_TEST_CASE(testNewZealand) {
BOOST_TEST_MESSAGE("Testing a few holiday rules for New Zealand...");

auto auckland = NewZealand(NewZealand::Auckland);
auto wellington = NewZealand(NewZealand::Wellington);

for (auto calendar: { auckland, wellington }) {
// mid-week New Year's day
BOOST_TEST(calendar.isHoliday({1, January, 2025}));
BOOST_TEST(calendar.isHoliday({2, January, 2025}));
BOOST_TEST(calendar.isBusinessDay({3, January, 2025}));
// New Year's day on Sunday
BOOST_TEST(calendar.isHoliday({1, January, 2023}));
BOOST_TEST(calendar.isHoliday({2, January, 2023}));
BOOST_TEST(calendar.isHoliday({3, January, 2023}));
BOOST_TEST(calendar.isBusinessDay({4, January, 2023}));
// New Year's day on Saturday
BOOST_TEST(calendar.isHoliday({1, January, 2022}));
BOOST_TEST(calendar.isHoliday({2, January, 2022}));
BOOST_TEST(calendar.isHoliday({3, January, 2022}));
BOOST_TEST(calendar.isHoliday({4, January, 2022}));
BOOST_TEST(calendar.isBusinessDay({5, January, 2022}));
// New Year's day on Friday
BOOST_TEST(calendar.isHoliday({1, January, 2027}));
BOOST_TEST(calendar.isHoliday({2, January, 2027}));
BOOST_TEST(calendar.isHoliday({3, January, 2027}));
BOOST_TEST(calendar.isHoliday({4, January, 2027}));
BOOST_TEST(calendar.isBusinessDay({5, January, 2027}));

// mid-week Christmas day
BOOST_TEST(calendar.isHoliday({25, December, 2024}));
BOOST_TEST(calendar.isHoliday({26, December, 2024}));
BOOST_TEST(calendar.isBusinessDay({27, December, 2024}));
// Christmas day on Sunday
BOOST_TEST(calendar.isHoliday({25, December, 2022}));
BOOST_TEST(calendar.isHoliday({26, December, 2022}));
BOOST_TEST(calendar.isHoliday({27, December, 2022}));
BOOST_TEST(calendar.isBusinessDay({28, December, 2022}));
// Christmas day on Saturday
BOOST_TEST(calendar.isHoliday({25, December, 2021}));
BOOST_TEST(calendar.isHoliday({26, December, 2021}));
BOOST_TEST(calendar.isHoliday({27, December, 2021}));
BOOST_TEST(calendar.isHoliday({28, December, 2021}));
BOOST_TEST(calendar.isBusinessDay({29, December, 2021}));
// Christmas day on Friday
BOOST_TEST(calendar.isHoliday({25, December, 2026}));
BOOST_TEST(calendar.isHoliday({26, December, 2026}));
BOOST_TEST(calendar.isHoliday({27, December, 2026}));
BOOST_TEST(calendar.isHoliday({28, December, 2026}));
BOOST_TEST(calendar.isBusinessDay({29, December, 2026}));

// Waitangi Day is moved to Monday but only since 2013
BOOST_TEST(calendar.isHoliday({8, February, 2021}));
BOOST_TEST(calendar.isHoliday({7, February, 2022}));
BOOST_TEST(calendar.isBusinessDay({8, February, 2010}));
BOOST_TEST(calendar.isBusinessDay({7, February, 2011}));

// The same goes for ANZAC Day
BOOST_TEST(calendar.isHoliday({27, April, 2020}));
BOOST_TEST(calendar.isHoliday({26, April, 2021}));
BOOST_TEST(calendar.isBusinessDay({27, April, 2009}));
BOOST_TEST(calendar.isBusinessDay({26, April, 2010}));
}

// different Anniversary Day for the two calendars
BOOST_TEST(auckland.isBusinessDay({22, January, 2024}));
BOOST_TEST(wellington.isHoliday({22, January, 2024}));
BOOST_TEST(auckland.isHoliday({29, January, 2024}));
BOOST_TEST(wellington.isBusinessDay({29, January, 2024}));
BOOST_TEST(auckland.isBusinessDay({19, January, 2026}));
BOOST_TEST(wellington.isHoliday({19, January, 2026}));
BOOST_TEST(auckland.isHoliday({26, January, 2026}));
BOOST_TEST(wellington.isBusinessDay({26, January, 2026}));
BOOST_TEST(auckland.isBusinessDay({25, January, 2027}));
BOOST_TEST(wellington.isHoliday({25, January, 2027}));
BOOST_TEST(auckland.isHoliday({1, February, 2027}));
BOOST_TEST(wellington.isBusinessDay({1, February, 2027}));
}


BOOST_AUTO_TEST_CASE(testStartOfMonth) {
BOOST_TEST_MESSAGE("Testing start-of-month calculation...");

Expand Down

0 comments on commit 54580b9

Please sign in to comment.