Issue #1777412: strftime() accepts year >= 1 instead of year >= 1900

* With Visual Studio, year have to be in [1; 9999]
 * Add more tests on the year field
This commit is contained in:
Victor Stinner 2011-01-08 01:56:31 +00:00
parent 0dd06f4082
commit 73ea29cb03
2 changed files with 82 additions and 36 deletions

View File

@ -43,14 +43,8 @@ class TimeTestCase(unittest.TestCase):
# Make sure that strftime() checks the bounds of the various parts # Make sure that strftime() checks the bounds of the various parts
#of the time tuple (0 is valid for *all* values). #of the time tuple (0 is valid for *all* values).
# Check year [1900, max(int)] # The year field is tested by other test cases above
self.assertRaises(ValueError, func,
(999, 1, 1, 0, 0, 0, 0, 1, -1))
if time.accept2dyear:
self.assertRaises(ValueError, func,
(-1, 1, 1, 0, 0, 0, 0, 1, -1))
self.assertRaises(ValueError, func,
(100, 1, 1, 0, 0, 0, 0, 1, -1))
# Check month [1, 12] + zero support # Check month [1, 12] + zero support
self.assertRaises(ValueError, func, self.assertRaises(ValueError, func,
(1900, -1, 1, 0, 0, 0, 0, 1, -1)) (1900, -1, 1, 0, 0, 0, 0, 1, -1))
@ -267,8 +261,10 @@ class TestLocale(unittest.TestCase):
# This should not cause an exception # This should not cause an exception
time.strftime("%B", (2009,2,1,0,0,0,0,0,0)) time.strftime("%B", (2009,2,1,0,0,0,0,0,0))
class TestAccept2Year(unittest.TestCase):
accept2dyear = 1 class _BaseYearTest(unittest.TestCase):
accept2dyear = None
def setUp(self): def setUp(self):
self.saved_accept2dyear = time.accept2dyear self.saved_accept2dyear = time.accept2dyear
time.accept2dyear = self.accept2dyear time.accept2dyear = self.accept2dyear
@ -277,10 +273,37 @@ class TestAccept2Year(unittest.TestCase):
time.accept2dyear = self.saved_accept2dyear time.accept2dyear = self.saved_accept2dyear
def yearstr(self, y): def yearstr(self, y):
# return time.strftime('%Y', (y,) + (0,) * 8) raise NotImplementedError()
class _TestAsctimeYear:
def yearstr(self, y):
return time.asctime((y,) + (0,) * 8).split()[-1] return time.asctime((y,) + (0,) * 8).split()[-1]
def test_2dyear(self): def test_large_year(self):
# Check that it doesn't crash with year > 9999
self.assertEqual(self.yearstr(12345), '12345')
self.assertEqual(self.yearstr(123456789), '123456789')
class _TestStrftimeYear:
def yearstr(self, y):
return time.strftime('%Y', (y,) + (0,) * 8).split()[-1]
def test_large_year(self):
# Just check that it doesn't crash with year > 9999: it may or may not
# raise an error depending on the OS and compiler
try:
self.yearstr(12345)
except ValueError:
pass
try:
self.yearstr(123456)
except ValueError:
pass
class _Test2dYear(_BaseYearTest):
accept2dyear = 1
def test_year(self):
with support.check_warnings(): with support.check_warnings():
self.assertEqual(self.yearstr(0), '2000') self.assertEqual(self.yearstr(0), '2000')
self.assertEqual(self.yearstr(69), '1969') self.assertEqual(self.yearstr(69), '1969')
@ -288,27 +311,41 @@ class TestAccept2Year(unittest.TestCase):
self.assertEqual(self.yearstr(99), '1999') self.assertEqual(self.yearstr(99), '1999')
def test_invalid(self): def test_invalid(self):
self.assertRaises(ValueError, self.yearstr, 999)
self.assertRaises(ValueError, self.yearstr, 100)
self.assertRaises(ValueError, self.yearstr, -1) self.assertRaises(ValueError, self.yearstr, -1)
self.assertRaises(ValueError, self.yearstr, 100)
self.assertRaises(ValueError, self.yearstr, 999)
class TestAccept2YearBool(TestAccept2Year): class _Test4dYear(_BaseYearTest):
accept2dyear = True
class TestDontAccept2Year(TestAccept2Year):
accept2dyear = 0 accept2dyear = 0
def test_2dyear(self):
self.assertEqual(self.yearstr(0), '0') def test_year(self):
self.assertEqual(self.yearstr(1), '1')
self.assertEqual(self.yearstr(69), '69') self.assertEqual(self.yearstr(69), '69')
self.assertEqual(self.yearstr(68), '68') self.assertEqual(self.yearstr(68), '68')
self.assertEqual(self.yearstr(99), '99') self.assertEqual(self.yearstr(99), '99')
self.assertEqual(self.yearstr(999), '999') self.assertEqual(self.yearstr(999), '999')
self.assertEqual(self.yearstr(9999), '9999') self.assertEqual(self.yearstr(9999), '9999')
def test_invalid(self): class TestAsctimeAccept2dYear(_TestAsctimeYear, _Test2dYear):
pass pass
class TestAccept2YearBad(TestAccept2Year): class TestStrftimeAccept2dYear(_TestStrftimeYear, _Test2dYear):
pass
class TestAsctime4dyear(_TestAsctimeYear, _Test4dYear):
pass
class TestStrftime4dyear(_TestStrftimeYear, _Test4dYear):
def test_bounds(self):
self.assertRaises(ValueError, self.yearstr, 0)
class Test2dyearBool(_TestAsctimeYear, _Test2dYear):
accept2dyear = True
class Test4dyearBool(_TestAsctimeYear, _Test4dYear):
accept2dyear = False
class TestAccept2YearBad(_TestAsctimeYear, _BaseYearTest):
class X: class X:
def __bool__(self): def __bool__(self):
raise RuntimeError('boo') raise RuntimeError('boo')
@ -319,14 +356,17 @@ class TestAccept2YearBad(TestAccept2Year):
self.assertRaises(RuntimeError, self.yearstr, 200) self.assertRaises(RuntimeError, self.yearstr, 200)
class TestDontAccept2YearBool(TestDontAccept2Year):
accept2dyear = False
def test_main(): def test_main():
support.run_unittest(TimeTestCase, TestLocale, support.run_unittest(
TestAccept2Year, TestAccept2YearBool, TestAccept2YearBad, TimeTestCase,
TestDontAccept2Year, TestDontAccept2YearBool) TestLocale,
TestAsctimeAccept2dYear,
TestStrftimeAccept2dYear,
TestAsctime4dyear,
TestStrftime4dyear,
Test2dyearBool,
Test4dyearBool,
TestAccept2YearBad)
if __name__ == "__main__": if __name__ == "__main__":
test_main() test_main()

View File

@ -474,15 +474,21 @@ time_strftime(PyObject *self, PyObject *args)
else if (!gettmarg(tup, &buf) || !checktm(&buf)) else if (!gettmarg(tup, &buf) || !checktm(&buf))
return NULL; return NULL;
/* XXX: Reportedly, some systems have issues formating dates prior to year #ifdef _MSC_VER
* 1000. These systems should be identified and this check should be if (buf.tm_year + 1900 < 1 || 9999 < buf.tm_year + 1900) {
* moved to appropriate system specific section below. */ PyErr_Format(PyExc_ValueError,
if (buf.tm_year < -900) { "strftime() requires year in [1; 9999]",
PyErr_Format(PyExc_ValueError, "year=%d is before 1900; "
"the strftime() method requires year >= 1900",
buf.tm_year + 1900); buf.tm_year + 1900);
return NULL; return NULL;
} }
#else
if (buf.tm_year + 1900 < 1) {
PyErr_Format(PyExc_ValueError,
"strftime() requires year >= 1",
buf.tm_year + 1900);
return NULL;
}
#endif
/* Normalize tm_isdst just in case someone foolishly implements %Z /* Normalize tm_isdst just in case someone foolishly implements %Z
based on the assumption that tm_isdst falls within the range of based on the assumption that tm_isdst falls within the range of