Improve period compution

This commit is contained in:
Martchus 2017-12-03 01:45:54 +01:00
parent a89f6f9ce4
commit 26cd303422
3 changed files with 61 additions and 7 deletions

View File

@ -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

View File

@ -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

View File

@ -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());
}
/*!