diff --git a/ql/time/calendar.hpp b/ql/time/calendar.hpp index 35b603260f2..be35499301e 100644 --- a/ql/time/calendar.hpp +++ b/ql/time/calendar.hpp @@ -108,6 +108,12 @@ namespace QuantLib { weekend for the given market. */ bool isWeekend(Weekday w) const; + /*! Returns true iff in the given market, the date is on + or before the first business day for that month. + */ + bool isStartOfMonth(const Date& d) const; + //! first business day of the month to which the given date belongs + Date startOfMonth(const Date& d) const; /*! Returns true iff in the given market, the date is on or after the last business day for that month. */ @@ -240,6 +246,14 @@ namespace QuantLib { return impl_->isBusinessDay(_d); } + inline bool Calendar::isStartOfMonth(const Date& d) const { + return (d.month() != adjust(d-1, Preceding).month()); + } + + inline Date Calendar::startOfMonth(const Date& d) const { + return adjust(Date::startOfMonth(d), Following); + } + inline bool Calendar::isEndOfMonth(const Date& d) const { return (d.month() != adjust(d+1).month()); } diff --git a/ql/time/date.hpp b/ql/time/date.hpp index 55c477bf4e5..63ae49212ac 100644 --- a/ql/time/date.hpp +++ b/ql/time/date.hpp @@ -207,6 +207,10 @@ namespace QuantLib { static Date maxDate(); //! whether the given year is a leap one static bool isLeap(Year y); + //! first day of the month to which the given date belongs + static Date startOfMonth(const Date& d); + //! whether a date is the first day of its month + static bool isStartOfMonth(const Date& d); //! last day of the month to which the given date belongs static Date endOfMonth(const Date& d); //! whether a date is the last day of its month @@ -425,6 +429,16 @@ namespace QuantLib { return advance(*this,-p.length(),p.units()); } + inline Date Date::startOfMonth(const Date& d) { + Month m = d.month(); + Year y = d.year(); + return Date(1, m, y); + } + + inline bool Date::isStartOfMonth(const Date& d) { + return (d.dayOfMonth() == 1); + } + inline Date Date::endOfMonth(const Date& d) { Month m = d.month(); Year y = d.year(); diff --git a/test-suite/calendars.cpp b/test-suite/calendars.cpp index 47eeb387d34..ea7a816894f 100644 --- a/test-suite/calendars.cpp +++ b/test-suite/calendars.cpp @@ -3369,6 +3369,26 @@ BOOST_AUTO_TEST_CASE(testMexicoInaugurationDay) { } } +BOOST_AUTO_TEST_CASE(testStartOfMonth) { + BOOST_TEST_MESSAGE("Testing start-of-month calculation..."); + + Calendar c = TARGET(); // any calendar would be OK + + Date som, counter = Date::minDate() + 2 * Months; + Date last = Date::maxDate(); + + while (counter < last) { + som = c.startOfMonth(counter); + // check that som is som + if (!c.isStartOfMonth(som)) + BOOST_FAIL("\n " << som.weekday() << " " << som << " is not the first business day in " + << som.month() << " " << som.year() << " according to " << c.name()); + // check that som is in the same month as counter + if (som.month() != counter.month()) + BOOST_FAIL("\n " << som << " is not in the same month as " << counter); + counter = counter + 1; + } +} BOOST_AUTO_TEST_CASE(testEndOfMonth) { BOOST_TEST_MESSAGE("Testing end-of-month calculation...");