diff -ur Python-2.6.6/Lib/test/test_datetime.py Python-2.6.6-stftime/Lib/test/test_datetime.py --- Lib/test/test_datetime.py 2010-05-31 16:00:34.000000000 +0000 +++ Lib/test/test_datetime.py 2010-10-08 04:28:50.000000000 +0000 @@ -846,6 +846,7 @@ self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05") self.assertEqual(t.strftime(""), "") # SF bug #761337 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784 + self.assertEqual(t.strftime("Y:%Y"), "Y:2005") self.assertRaises(TypeError, t.strftime) # needs an arg self.assertRaises(TypeError, t.strftime, "one", "two") # too many args @@ -874,6 +875,33 @@ #check that this standard extension works t.strftime("%f") + def test_strftime_early(self): + t = self.theclass(1805, 3, 2) + self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05") + self.assertEqual(t.strftime("Y:%Y"), "Y:1805") + self.assertEqual(t.strftime(""), "") # SF bug #761337 + self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784 + + self.assertRaises(TypeError, t.strftime) # needs an arg + self.assertRaises(TypeError, t.strftime, "one", "two") # too many args + self.assertRaises(TypeError, t.strftime, 42) # arg wrong type + + # A naive object replaces %z and %Z w/ empty strings. + self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''") + + def test_strftime_very_early(self): + t = self.theclass(85, 3, 2) + self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:85") + self.assertEqual(t.strftime("Y:%Y"), "Y:85") + self.assertEqual(t.strftime(""), "") # SF bug #761337 + self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784 + + self.assertRaises(TypeError, t.strftime) # needs an arg + self.assertRaises(TypeError, t.strftime, "one", "two") # too many args + self.assertRaises(TypeError, t.strftime, 42) # arg wrong type + + # A naive object replaces %z and %Z w/ empty strings. + self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''") def test_format(self): dt = self.theclass(2007, 9, 10) @@ -1038,11 +1066,11 @@ self.failUnless(self.theclass.max) def test_strftime_out_of_range(self): - # For nasty technical reasons, we can't handle years before 1900. + # For nasty technical reasons, we couldn't handle years before 1900. Now we can. cls = self.theclass self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900") for y in 1, 49, 51, 99, 100, 1000, 1899: - self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y") + self.assertEqual(cls(y, 1, 1).strftime("%Y"), str(y)) def test_replace(self): cls = self.theclass diff -ur Python-2.6.6/Modules/datetimemodule.c Python-2.6.6-stftime/Modules/datetimemodule.c --- Modules/datetimemodule.c 2010-05-27 21:53:16.000000000 +0000 +++ Modules/datetimemodule.c 2010-10-08 04:33:35.000000000 +0000 @@ -1151,6 +1151,32 @@ return PyString_FromStringAndSize(freplacement, strlen(freplacement)); } +/* copied straight out of timemodule.c */ +static int +gettmarg(PyObject *args, struct tm *p) +{ + int y; + memset((void *) p, '\0', sizeof(struct tm)); + + if (!PyArg_Parse(args, "(iiiiiiiii)", + &y, + &p->tm_mon, + &p->tm_mday, + &p->tm_hour, + &p->tm_min, + &p->tm_sec, + &p->tm_wday, + &p->tm_yday, + &p->tm_isdst)) + return 0; + p->tm_year = y - 1900; + p->tm_mon--; + p->tm_wday = (p->tm_wday + 1) % 7; + p->tm_yday--; + return 1; +} + + /* I sure don't want to reproduce the strftime code from the time module, * so this imports the module and calls it. All the hair is due to * giving special meanings to the %z, %Z and %f format codes via a @@ -1182,31 +1208,6 @@ assert(object && format && timetuple); - /* Give up if the year is before 1900. - * Python strftime() plays games with the year, and different - * games depending on whether envar PYTHON2K is set. This makes - * years before 1900 a nightmare, even if the platform strftime - * supports them (and not all do). - * We could get a lot farther here by avoiding Python's strftime - * wrapper and calling the C strftime() directly, but that isn't - * an option in the Python implementation of this module. - */ - { - long year; - PyObject *pyyear = PySequence_GetItem(timetuple, 0); - if (pyyear == NULL) return NULL; - assert(PyInt_Check(pyyear)); - year = PyInt_AsLong(pyyear); - Py_DECREF(pyyear); - if (year < 1900) { - PyErr_Format(PyExc_ValueError, "year=%ld is before " - "1900; the datetime strftime() " - "methods require year >= 1900", - year); - return NULL; - } - } - /* Scan the input format, looking for %z/%Z/%f escapes, building * a new format. Since computing the replacements for those codes * is expensive, don't unless they're actually used. @@ -1343,12 +1344,26 @@ if (_PyString_Resize(&newfmt, usednew) < 0) goto Done; { - PyObject *time = PyImport_ImportModuleNoBlock("time"); - if (time == NULL) - goto Done; - result = PyObject_CallMethod(time, "strftime", "OO", - newfmt, timetuple); - Py_DECREF(time); + char *outbuf = 0; + char *c_newfmt = PyString_AsString(newfmt); + size_t i; + int buflen; + int fmtlen; + memset((void *)(&buf), '\0', sizeof(struct tm)); + gettmarg(timetuple, &buf); + fmtlen = strlen(c_newfmt); + for (i = 1024; ; i += i) { + outbuf = (char *)malloc(i); + if (outbuf == NULL) { + return PyErr_NoMemory(); + } + buflen = strftime(outbuf, 1024, c_newfmt, &buf); + if (buflen > 0 || i >= 256 * fmtlen) { + result = PyString_FromStringAndSize(outbuf, buflen); + free(outbuf); + break; + } + } } Done: Py_XDECREF(freplacement);