mirror of
https://github.com/pocoproject/poco.git
synced 2024-12-12 18:20:26 +01:00
* Poco::DateTime uses assertions for validation #1540 * fix(DateTime): fix ASAN buf overflow * fix(DateTime): allow negative year (TODO: 0 Julian day Gregorian year value)
This commit is contained in:
parent
64c751f3a3
commit
af3b3b1902
@ -52,7 +52,8 @@ class Foundation_API DateTime
|
|||||||
/// Notes:
|
/// Notes:
|
||||||
/// * Zero is a valid year (in accordance with ISO 8601 and astronomical year numbering)
|
/// * Zero is a valid year (in accordance with ISO 8601 and astronomical year numbering)
|
||||||
/// * Year zero (0) is a leap year
|
/// * 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:
|
/// For more information, please see:
|
||||||
/// * http://en.wikipedia.org/wiki/Gregorian_Calendar
|
/// * http://en.wikipedia.org/wiki/Gregorian_Calendar
|
||||||
@ -254,6 +255,12 @@ public:
|
|||||||
/// Returns true if all arguments are valid, false otherwise.
|
/// Returns true if all arguments are valid, false otherwise.
|
||||||
|
|
||||||
protected:
|
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);
|
static double toJulianDay(Timestamp::UtcTimeVal utcTime);
|
||||||
/// Computes the Julian day for an UTC time.
|
/// Computes the Julian day for an UTC time.
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ DateTime::DateTime()
|
|||||||
_utcTime = now.utcTime();
|
_utcTime = now.utcTime();
|
||||||
computeGregorian(julianDay());
|
computeGregorian(julianDay());
|
||||||
computeDaytime();
|
computeDaytime();
|
||||||
|
checkValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -43,14 +44,9 @@ DateTime::DateTime(const tm& tmStruct):
|
|||||||
_millisecond(0),
|
_millisecond(0),
|
||||||
_microsecond(0)
|
_microsecond(0)
|
||||||
{
|
{
|
||||||
poco_assert (_year >= 0 && _year <= 9999);
|
checkValid();
|
||||||
poco_assert (_month >= 1 && _month <= 12);
|
_utcTime = toUtcTime(toJulianDay(_year, _month, _day)) +
|
||||||
poco_assert (_day >= 1 && _day <= daysOfMonth(_year, _month));
|
10*(_hour*Timespan::HOURS + _minute*Timespan::MINUTES + _second*Timespan::SECONDS);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -59,6 +55,7 @@ DateTime::DateTime(const Timestamp& timestamp):
|
|||||||
{
|
{
|
||||||
computeGregorian(julianDay());
|
computeGregorian(julianDay());
|
||||||
computeDaytime();
|
computeDaytime();
|
||||||
|
checkValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -72,35 +69,18 @@ DateTime::DateTime(int year, int month, int day, int hour, int minute, int secon
|
|||||||
_millisecond(millisecond),
|
_millisecond(millisecond),
|
||||||
_microsecond(microsecond)
|
_microsecond(microsecond)
|
||||||
{
|
{
|
||||||
if (isValid(_year, _month, _day, _hour, _minute, _second, _millisecond, _microsecond))
|
checkValid();
|
||||||
{
|
|
||||||
_utcTime = toUtcTime(toJulianDay(year, month, day)) +
|
_utcTime = toUtcTime(toJulianDay(year, month, day)) +
|
||||||
10 * (hour*Timespan::HOURS + minute*Timespan::MINUTES + second*Timespan::SECONDS +
|
10 * (hour*Timespan::HOURS + minute*Timespan::MINUTES + second*Timespan::SECONDS +
|
||||||
millisecond*Timespan::MILLISECONDS + microsecond);
|
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)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
DateTime::DateTime(double julianDay):
|
DateTime::DateTime(double julianDay):
|
||||||
_utcTime(toUtcTime(julianDay))
|
_utcTime(toUtcTime(julianDay))
|
||||||
{
|
{
|
||||||
computeGregorian(julianDay);
|
computeGregorian(julianDay);
|
||||||
|
checkValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -109,6 +89,7 @@ DateTime::DateTime(Timestamp::UtcTimeVal utcTime, Timestamp::TimeDiff diff):
|
|||||||
{
|
{
|
||||||
computeGregorian(julianDay());
|
computeGregorian(julianDay());
|
||||||
computeDaytime();
|
computeDaytime();
|
||||||
|
checkValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -154,6 +135,7 @@ DateTime& DateTime::operator = (const Timestamp& timestamp)
|
|||||||
_utcTime = timestamp.utcTime();
|
_utcTime = timestamp.utcTime();
|
||||||
computeGregorian(julianDay());
|
computeGregorian(julianDay());
|
||||||
computeDaytime();
|
computeDaytime();
|
||||||
|
checkValid();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,21 +144,13 @@ DateTime& DateTime::operator = (double julianDay)
|
|||||||
{
|
{
|
||||||
_utcTime = toUtcTime(julianDay);
|
_utcTime = toUtcTime(julianDay);
|
||||||
computeGregorian(julianDay);
|
computeGregorian(julianDay);
|
||||||
|
checkValid();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DateTime& DateTime::assign(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond)
|
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);
|
_utcTime = toUtcTime(toJulianDay(year, month, day)) + 10*(hour*Timespan::HOURS + minute*Timespan::MINUTES + second*Timespan::SECONDS + millisecond*Timespan::MILLISECONDS + microsecond);
|
||||||
_year = year;
|
_year = year;
|
||||||
_month = month;
|
_month = month;
|
||||||
@ -186,6 +160,7 @@ DateTime& DateTime::assign(int year, int month, int day, int hour, int minute, i
|
|||||||
_second = second;
|
_second = second;
|
||||||
_millisecond = millisecond;
|
_millisecond = millisecond;
|
||||||
_microsecond = microsecond;
|
_microsecond = microsecond;
|
||||||
|
checkValid();
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@ -223,21 +198,39 @@ int DateTime::dayOfYear() const
|
|||||||
|
|
||||||
int DateTime::daysOfMonth(int year, int month)
|
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};
|
static int daysOfMonthTable[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||||
|
|
||||||
if (month == 2 && isLeapYear(year))
|
if (month == 2 && isLeapYear(year))
|
||||||
return 29;
|
return 29;
|
||||||
else
|
else if (month < 1 || month > 12)
|
||||||
|
return 0;
|
||||||
return daysOfMonthTable[month];
|
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)
|
bool DateTime::isValid(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
(year >= 0 && year <= 9999) &&
|
(year >= -4713 && year <= 9999) &&
|
||||||
(month >= 1 && month <= 12) &&
|
(month >= 1 && month <= 12) &&
|
||||||
(day >= 1 && day <= daysOfMonth(year, month)) &&
|
(day >= 1 && day <= daysOfMonth(year, month)) &&
|
||||||
(hour >= 0 && hour <= 23) &&
|
(hour >= 0 && hour <= 23) &&
|
||||||
@ -294,6 +287,7 @@ DateTime& DateTime::operator += (const Timespan& span)
|
|||||||
_utcTime += span.totalMicroseconds()*10;
|
_utcTime += span.totalMicroseconds()*10;
|
||||||
computeGregorian(julianDay());
|
computeGregorian(julianDay());
|
||||||
computeDaytime();
|
computeDaytime();
|
||||||
|
checkValid();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,6 +297,7 @@ DateTime& DateTime::operator -= (const Timespan& span)
|
|||||||
_utcTime -= span.totalMicroseconds()*10;
|
_utcTime -= span.totalMicroseconds()*10;
|
||||||
computeGregorian(julianDay());
|
computeGregorian(julianDay());
|
||||||
computeDaytime();
|
computeDaytime();
|
||||||
|
checkValid();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,14 +416,6 @@ void DateTime::computeGregorian(double julianDay)
|
|||||||
_microsecond = short(r + 0.5);
|
_microsecond = short(r + 0.5);
|
||||||
|
|
||||||
normalize();
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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()
|
void DateTimeTest::setUp()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -909,6 +1026,7 @@ CppUnit::Test* DateTimeTest::suite()
|
|||||||
CppUnit_addTest(pSuite, DateTimeTest, testUTC);
|
CppUnit_addTest(pSuite, DateTimeTest, testUTC);
|
||||||
CppUnit_addTest(pSuite, DateTimeTest, testLeapSeconds);
|
CppUnit_addTest(pSuite, DateTimeTest, testLeapSeconds);
|
||||||
CppUnit_addTest(pSuite, DateTimeTest, testTM);
|
CppUnit_addTest(pSuite, DateTimeTest, testTM);
|
||||||
|
CppUnit_addTest(pSuite, DateTimeTest, testInvalid);
|
||||||
|
|
||||||
return pSuite;
|
return pSuite;
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ public:
|
|||||||
void testUTC();
|
void testUTC();
|
||||||
void testLeapSeconds();
|
void testLeapSeconds();
|
||||||
void testTM();
|
void testTM();
|
||||||
|
void testInvalid();
|
||||||
|
|
||||||
void setUp();
|
void setUp();
|
||||||
void tearDown();
|
void tearDown();
|
||||||
|
Loading…
Reference in New Issue
Block a user