diff --git a/Foundation/include/Poco/DateTime.h b/Foundation/include/Poco/DateTime.h index b95af9039..65ef8851f 100644 --- a/Foundation/include/Poco/DateTime.h +++ b/Foundation/include/Poco/DateTime.h @@ -52,7 +52,8 @@ class Foundation_API DateTime /// Notes: /// * Zero is a valid year (in accordance with ISO 8601 and astronomical year numbering) /// * Year zero (0) is a leap year - /// * Negative years (years preceding 1 BC) are not supported + /// * Minimum date/time that can be represented is 12:00:00 UTC Monday, 1 January 4713 BC + /// (Julian Day 0, Gregorian -4713-11-24 12:00:00) /// /// For more information, please see: /// * http://en.wikipedia.org/wiki/Gregorian_Calendar @@ -254,6 +255,12 @@ public: /// Returns true if all arguments are valid, false otherwise. protected: + void checkValid(); + /// Checks if the given date and time is valid (all arguments are within a proper range). + /// Expects all members to be set. + /// + /// Throws Poco::InvalidArgumentException if any of the arguments is not valid. + static double toJulianDay(Timestamp::UtcTimeVal utcTime); /// Computes the Julian day for an UTC time. diff --git a/Foundation/src/DateTime.cpp b/Foundation/src/DateTime.cpp index 36ec1c1aa..ea8108098 100644 --- a/Foundation/src/DateTime.cpp +++ b/Foundation/src/DateTime.cpp @@ -30,6 +30,7 @@ DateTime::DateTime() _utcTime = now.utcTime(); computeGregorian(julianDay()); computeDaytime(); + checkValid(); } @@ -43,14 +44,9 @@ DateTime::DateTime(const tm& tmStruct): _millisecond(0), _microsecond(0) { - poco_assert (_year >= 0 && _year <= 9999); - poco_assert (_month >= 1 && _month <= 12); - poco_assert (_day >= 1 && _day <= daysOfMonth(_year, _month)); - poco_assert (_hour >= 0 && _hour <= 23); - poco_assert (_minute >= 0 && _minute <= 59); - poco_assert (_second >= 0 && _second <= 60); - - _utcTime = toUtcTime(toJulianDay(_year, _month, _day)) + 10*(_hour*Timespan::HOURS + _minute*Timespan::MINUTES + _second*Timespan::SECONDS); + checkValid(); + _utcTime = toUtcTime(toJulianDay(_year, _month, _day)) + + 10*(_hour*Timespan::HOURS + _minute*Timespan::MINUTES + _second*Timespan::SECONDS); } @@ -59,6 +55,7 @@ DateTime::DateTime(const Timestamp& timestamp): { computeGregorian(julianDay()); computeDaytime(); + checkValid(); } @@ -72,28 +69,10 @@ DateTime::DateTime(int year, int month, int day, int hour, int minute, int secon _millisecond(millisecond), _microsecond(microsecond) { - if (isValid(_year, _month, _day, _hour, _minute, _second, _millisecond, _microsecond)) - { - _utcTime = toUtcTime(toJulianDay(year, month, day)) + + checkValid(); + _utcTime = toUtcTime(toJulianDay(year, month, day)) + 10 * (hour*Timespan::HOURS + minute*Timespan::MINUTES + second*Timespan::SECONDS + millisecond*Timespan::MILLISECONDS + microsecond); - } - else - { - throw Poco::InvalidArgumentException(Poco::format("Date time is %d-%d-%dT%d:%d:%d.%d.%d\n" - "Valid values:\n" - "0 <= year <= 9999\n" - "1 <= month <= 12\n" - "1 <= day <= %d\n" - "0 <= hour <= 23\n" - "0 <= minute <= 59\n" - "0 <= second <= 59\n" - "0 <= millisecond <= 999\n" - "0 <= microsecond <= 999", - _year, _month, _day, _hour, _minute, - _second, _millisecond, _microsecond, - daysOfMonth(_year, _month))); - } } @@ -101,6 +80,7 @@ DateTime::DateTime(double julianDay): _utcTime(toUtcTime(julianDay)) { computeGregorian(julianDay); + checkValid(); } @@ -109,6 +89,7 @@ DateTime::DateTime(Timestamp::UtcTimeVal utcTime, Timestamp::TimeDiff diff): { computeGregorian(julianDay()); computeDaytime(); + checkValid(); } @@ -154,6 +135,7 @@ DateTime& DateTime::operator = (const Timestamp& timestamp) _utcTime = timestamp.utcTime(); computeGregorian(julianDay()); computeDaytime(); + checkValid(); return *this; } @@ -162,21 +144,13 @@ DateTime& DateTime::operator = (double julianDay) { _utcTime = toUtcTime(julianDay); computeGregorian(julianDay); + checkValid(); return *this; } DateTime& DateTime::assign(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond) { - poco_assert (year >= 0 && year <= 9999); - poco_assert (month >= 1 && month <= 12); - poco_assert (day >= 1 && day <= daysOfMonth(year, month)); - poco_assert (hour >= 0 && hour <= 23); - poco_assert (minute >= 0 && minute <= 59); - poco_assert (second >= 0 && second <= 60); // allow leap seconds - poco_assert (millisecond >= 0 && millisecond <= 999); - poco_assert (microsecond >= 0 && microsecond <= 999); - _utcTime = toUtcTime(toJulianDay(year, month, day)) + 10*(hour*Timespan::HOURS + minute*Timespan::MINUTES + second*Timespan::SECONDS + millisecond*Timespan::MILLISECONDS + microsecond); _year = year; _month = month; @@ -186,6 +160,7 @@ DateTime& DateTime::assign(int year, int month, int day, int hour, int minute, i _second = second; _millisecond = millisecond; _microsecond = microsecond; + checkValid(); return *this; } @@ -223,21 +198,39 @@ int DateTime::dayOfYear() const int DateTime::daysOfMonth(int year, int month) { - poco_assert (month >= 1 && month <= 12); - static int daysOfMonthTable[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; if (month == 2 && isLeapYear(year)) return 29; - else - return daysOfMonthTable[month]; + else if (month < 1 || month > 12) + return 0; + return daysOfMonthTable[month]; +} + + +void DateTime::checkValid() +{ + if (!isValid(_year, _month, _day, _hour, _minute, _second, _millisecond, _microsecond)) + throw Poco::InvalidArgumentException(Poco::format("Date time is %hd-%hd-%hdT%hd:%hd:%hd.%hd.%hd\n" + "Valid values:\n" + "-4713 <= year <= 9999\n" + "1 <= month <= 12\n" + "1 <= day <= %d\n" + "0 <= hour <= 23\n" + "0 <= minute <= 59\n" + "0 <= second <= 60\n" + "0 <= millisecond <= 999\n" + "0 <= microsecond <= 999", + _year, _month, _day, _hour, _minute, + _second, _millisecond, _microsecond, + daysOfMonth(_year, _month))); } bool DateTime::isValid(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond) { return - (year >= 0 && year <= 9999) && + (year >= -4713 && year <= 9999) && (month >= 1 && month <= 12) && (day >= 1 && day <= daysOfMonth(year, month)) && (hour >= 0 && hour <= 23) && @@ -294,6 +287,7 @@ DateTime& DateTime::operator += (const Timespan& span) _utcTime += span.totalMicroseconds()*10; computeGregorian(julianDay()); computeDaytime(); + checkValid(); return *this; } @@ -303,6 +297,7 @@ DateTime& DateTime::operator -= (const Timespan& span) _utcTime -= span.totalMicroseconds()*10; computeGregorian(julianDay()); computeDaytime(); + checkValid(); return *this; } @@ -421,14 +416,6 @@ void DateTime::computeGregorian(double julianDay) _microsecond = short(r + 0.5); normalize(); - - poco_assert_dbg (_month >= 1 && _month <= 12); - poco_assert_dbg (_day >= 1 && _day <= daysOfMonth(_year, _month)); - poco_assert_dbg (_hour >= 0 && _hour <= 23); - poco_assert_dbg (_minute >= 0 && _minute <= 59); - poco_assert_dbg (_second >= 0 && _second <= 59); - poco_assert_dbg (_millisecond >= 0 && _millisecond <= 999); - poco_assert_dbg (_microsecond >= 0 && _microsecond <= 999); } diff --git a/Foundation/testsuite/src/DateTimeTest.cpp b/Foundation/testsuite/src/DateTimeTest.cpp index fe902924f..45b0cb402 100644 --- a/Foundation/testsuite/src/DateTimeTest.cpp +++ b/Foundation/testsuite/src/DateTimeTest.cpp @@ -876,6 +876,123 @@ void DateTimeTest::testTM() } +void DateTimeTest::testInvalid() +{ + try + { + DateTime dt(-4714, 1, 1); + failmsg("Invalid year, must throw"); + } + catch(const Poco::InvalidArgumentException&) { } + + try + { + DateTime dt(10000, 1, 1); + failmsg("Invalid year, must throw"); + } + catch(const Poco::InvalidArgumentException&) { } + + try + { + DateTime dt(0, 0, 1); + failmsg("Invalid month, must throw"); + } + catch(const Poco::InvalidArgumentException&) { } + + try + { + DateTime dt(0, 13, 1); + failmsg("Invalid month, must throw"); + } + catch(const Poco::InvalidArgumentException&) { } + + try + { + DateTime dt(0, 1, 0); + failmsg("Invalid day, must throw"); + } + catch(const Poco::InvalidArgumentException&) { } + + try + { + DateTime dt(0, 1, DateTime::daysOfMonth(0, 1)+1); + failmsg("Invalid day, must throw"); + } + catch(const Poco::InvalidArgumentException&) { } + + try + { + DateTime dt(1, 1, 1, -1); + failmsg("Invalid hour, must throw"); + } + catch(const Poco::InvalidArgumentException&) { } + + try + { + DateTime dt(1, 1, 1, 24); + failmsg("Invalid hour, must throw"); + } + catch(const Poco::InvalidArgumentException&) { } + + try + { + DateTime dt(1, 1, 1, 1, -1); + failmsg("Invalid minute, must throw"); + } + catch(const Poco::InvalidArgumentException&) { } + + try + { + DateTime dt(1, 1, 1, 1, 60); + failmsg("Invalid minute, must throw"); + } + catch(const Poco::InvalidArgumentException&) { } + + try + { + DateTime dt(1, 1, 1, 1, 1, -1); + failmsg("Invalid second, must throw"); + } + catch(const Poco::InvalidArgumentException&) { } + + try + { + DateTime dt(1, 1, 1, 1, 1, 61); + failmsg("Invalid second, must throw"); + } + catch(const Poco::InvalidArgumentException&) { } + + try + { + DateTime dt(1, 1, 1, 1, 1, 1, -1); + failmsg("Invalid millisecond, must throw"); + } + catch(const Poco::InvalidArgumentException&) { } + + try + { + DateTime dt(1, 1, 1, 1, 1, 1, 1000); + failmsg("Invalid millisecond, must throw"); + } + catch(const Poco::InvalidArgumentException&) { } + + try + { + DateTime dt(1, 1, 1, 1, 1, 1, 1, -1); + failmsg("Invalid microsecond, must throw"); + } + catch(const Poco::InvalidArgumentException&) { } + + try + { + DateTime dt(1, 1, 1, 1, 1, 1, 1, 1000); + failmsg("Invalid microsecond, must throw"); + } + catch(const Poco::InvalidArgumentException&) { } + +} + + void DateTimeTest::setUp() { } @@ -909,6 +1026,7 @@ CppUnit::Test* DateTimeTest::suite() CppUnit_addTest(pSuite, DateTimeTest, testUTC); CppUnit_addTest(pSuite, DateTimeTest, testLeapSeconds); CppUnit_addTest(pSuite, DateTimeTest, testTM); + CppUnit_addTest(pSuite, DateTimeTest, testInvalid); return pSuite; } diff --git a/Foundation/testsuite/src/DateTimeTest.h b/Foundation/testsuite/src/DateTimeTest.h index 6c62a564b..9b32c9019 100644 --- a/Foundation/testsuite/src/DateTimeTest.h +++ b/Foundation/testsuite/src/DateTimeTest.h @@ -42,6 +42,7 @@ public: void testUTC(); void testLeapSeconds(); void testTM(); + void testInvalid(); void setUp(); void tearDown();