diff --git a/chrono/period.cpp b/chrono/period.cpp index 9be51cb..ab4dbda 100644 --- a/chrono/period.cpp +++ b/chrono/period.cpp @@ -35,14 +35,15 @@ namespace ChronoUtilities { */ Period::Period(const DateTime &begin, const DateTime &end) { - m_years = end.year() - beg.year(); - m_months = end.month() - beg.month(); - m_days = end.day() - beg.day(); - if (end.hour() < beg.hour()) { - --m_days; + m_years = end.year() - begin.year(); + m_months = end.month() - begin.month(); + if (m_months < 0) { + m_months += 12; + --m_years; } + m_days = end.day() - begin.day(); if (m_days < 0) { - m_days += DateTime::daysInMonth(beg.year(), beg.month()); + m_days += end.month() > 1 ? DateTime::daysInMonth(end.year(), end.month() - 1) : 31; --m_months; } if (m_months < 0) { @@ -50,4 +51,33 @@ Period::Period(const DateTime &begin, const DateTime &end) --m_years; } } + +/*! + * \brief Adds the specified \a period to the specified date. + * \throws Might throw ConversionException if resulting DateTime would be out-of-range. + * \remarks + * - The order in which the years(), month() and days() are added matters. See the overall class description. + * - Since the accuracy of Period is only one day, the DateTime::timeOfDay() of the result always equals begin.timeOfDay(). + */ +DateTime operator+(DateTime begin, Period period) +{ + auto year = begin.year() + period.years(); + auto month = begin.month() + period.months(); + if (month > 12) { + month -= 12; + ++year; + } + auto day = begin.day() + period.days(); + const auto maxDays = DateTime::daysInMonth(year, month); + if (day > maxDays) { + day -= maxDays; + ++month; + } + if (month > 12) { + month -= 12; + ++year; + } + return DateTime::fromDate(year, month, day) + begin.timeOfDay(); +} + } // namespace ChronoUtilities diff --git a/chrono/period.h b/chrono/period.h index 1829d28..6d6e0a9 100644 --- a/chrono/period.h +++ b/chrono/period.h @@ -41,6 +41,9 @@ inline int Period::days() const { return m_days; } + +DateTime operator+(DateTime begin, Period period); + } // namespace ChronoUtilities #endif // CHRONO_UTILITIES_PERIOD_H diff --git a/tests/chronotests.cpp b/tests/chronotests.cpp index 302b842..cb07ddc 100644 --- a/tests/chronotests.cpp +++ b/tests/chronotests.cpp @@ -29,6 +29,7 @@ class ChronoTests : public TestFixture { CPPUNIT_TEST(testDateTime); CPPUNIT_TEST(testTimeSpan); CPPUNIT_TEST(testOperators); + CPPUNIT_TEST(testPeriod); CPPUNIT_TEST(testHashing); CPPUNIT_TEST_SUITE_END(); @@ -43,6 +44,7 @@ public: void testDateTime(); void testTimeSpan(); void testOperators(); + void testPeriod(); void testHashing(); }; @@ -159,7 +161,26 @@ void ChronoTests::testOperators() dateTime += TimeSpan::fromDays(365); CPPUNIT_ASSERT_EQUAL(2000, dateTime.year()); CPPUNIT_ASSERT_EQUAL(5, dateTime.day()); - CPPUNIT_ASSERT_EQUAL(2, Period(dateTime, dateTime + TimeSpan::fromDays(62)).months()); +} + +/*! + * \brief Tests Period. + */ +void ChronoTests::testPeriod() +{ + const auto begin(DateTime::fromDateAndTime(1994, 7, 18, 15, 30, 21)), end(DateTime::fromDateAndTime(2017, 12, 2, 15, 30, 21)); + const Period period(begin, end); + CPPUNIT_ASSERT_EQUAL(23, period.years()); + CPPUNIT_ASSERT_EQUAL(4, period.months()); + CPPUNIT_ASSERT_EQUAL(14, period.days()); + CPPUNIT_ASSERT_EQUAL(end.toString(), (begin + period).toString()); + + const auto end2(DateTime::fromDateAndTime(2018, 1, 2, 15, 30, 21)); + const Period period2(begin, end2); + CPPUNIT_ASSERT_EQUAL(23, period2.years()); + CPPUNIT_ASSERT_EQUAL(5, period2.months()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("one more day, because December has 31 days", 15, period2.days()); + CPPUNIT_ASSERT_EQUAL(end2.toString(), (begin + period2).toString()); } /*!