Fix issue 2782: be less strict about the format string type in strftime.
authorGregory P. Smith <greg@mad-scientist.com>
Mon, 2 Jun 2008 04:05:52 +0000 (04:05 +0000)
committerGregory P. Smith <greg@mad-scientist.com>
Mon, 2 Jun 2008 04:05:52 +0000 (04:05 +0000)
Accept unicode and anything else ParseTuple "s#" can deal with.  This
matches the time.strftime behavior.

Lib/test/test_datetime.py
Misc/NEWS
Modules/datetimemodule.c

index 84f45b46824d8e4144d43f3f722365aab1170260..cdc9eed6b9ce64b81f4cdcebad73ea97b1d7efeb 100644 (file)
@@ -850,9 +850,13 @@ class TestDate(HarmlessMixedComparison, unittest.TestCase):
         self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
         self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
 
+        # test that unicode input is allowed (issue 2782)
+        self.assertEqual(t.strftime(u"%m"), "03")
+
         # 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)
         self.assertEqual(dt.__format__(''), str(dt))
index b3789568b0df2865cd40e70c3112280beb36878e..d78e993df5ba73ab057e4fabda35356a8c6b05ac 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -72,6 +72,9 @@ Extension Modules
 Library
 -------
 
+- Issue #2782: The datetime module's strftime methods now accept
+  unicode format strings just as time.strftime always has.
+
 - The sgmllib and htmllib modules have been deprecated for removal
   in Python 3.0.
 
index be6e43aa9d8035d90ef227bd9aba0f741a7b67bc..49bd6568c7792b8113765c40495c6e5ebfdc8777 100644 (file)
@@ -2,6 +2,8 @@
  *  http://www.zope.org/Members/fdrake/DateTimeWiki/FrontPage
  */
 
+#define PY_SSIZE_T_CLEAN
+
 #include "Python.h"
 #include "modsupport.h"
 #include "structmember.h"
@@ -1152,8 +1154,8 @@ make_freplacement(PyObject *object)
  * needed.
  */
 static PyObject *
-wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
-             PyObject *tzinfoarg)
+wrap_strftime(PyObject *object, const char *format, size_t format_len,
+               PyObject *timetuple, PyObject *tzinfoarg)
 {
        PyObject *result = NULL;        /* guilty until proved innocent */
 
@@ -1161,20 +1163,19 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
        PyObject *Zreplacement = NULL;  /* py string, replacement for %Z */
        PyObject *freplacement = NULL;  /* py string, replacement for %f */
 
-       char *pin;      /* pointer to next char in input format */
-       char ch;        /* next char in input format */
+       const char *pin;        /* pointer to next char in input format */
+       char ch;                /* next char in input format */
 
        PyObject *newfmt = NULL;        /* py string, the output format */
        char *pnew;     /* pointer to available byte in output format */
-       int totalnew;   /* number bytes total in output format buffer,
-                          exclusive of trailing \0 */
-       int usednew;    /* number bytes used so far in output format buffer */
+       size_t totalnew;        /* number bytes total in output format buffer,
+                                  exclusive of trailing \0 */
+       size_t usednew; /* number bytes used so far in output format buffer */
 
-       char *ptoappend; /* pointer to string to append to output buffer */
-       int ntoappend;  /* # of bytes to append to output buffer */
+       const char *ptoappend;  /* ptr to string to append to output buffer */
+       size_t ntoappend;       /* # of bytes to append to output buffer */
 
        assert(object && format && timetuple);
-       assert(PyBytes_Check(format));
 
        /* Give up if the year is before 1900.
         * Python strftime() plays games with the year, and different
@@ -1205,13 +1206,13 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
         * a new format.  Since computing the replacements for those codes
         * is expensive, don't unless they're actually used.
         */
-       totalnew = PyBytes_Size(format) + 1;    /* realistic if no %z/%Z/%f */
+       totalnew = format_len + 1;      /* realistic if no %z/%Z/%f */
        newfmt = PyBytes_FromStringAndSize(NULL, totalnew);
        if (newfmt == NULL) goto Done;
        pnew = PyBytes_AsString(newfmt);
        usednew = 0;
 
-       pin = PyBytes_AsString(format);
+       pin = format;
        while ((ch = *pin++) != '\0') {
                if (ch != '%') {
                        ptoappend = pin - 1;
@@ -1313,7 +1314,7 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
                if (ntoappend == 0)
                        continue;
                while (usednew + ntoappend > totalnew) {
-                       int bigger = totalnew << 1;
+                       size_t bigger = totalnew << 1;
                        if ((bigger >> 1) != totalnew) { /* overflow */
                                PyErr_NoMemory();
                                goto Done;
@@ -2480,18 +2481,19 @@ date_strftime(PyDateTime_Date *self, PyObject *args, PyObject *kw)
         * timetuple() method appropriate to self's class.
         */
        PyObject *result;
-       PyObject *format;
        PyObject *tuple;
+       const char *format;
+       Py_ssize_t format_len;
        static char *keywords[] = {"format", NULL};
 
-       if (! PyArg_ParseTupleAndKeywords(args, kw, "O!:strftime", keywords,
-                                         &PyBytes_Type, &format))
+       if (! PyArg_ParseTupleAndKeywords(args, kw, "s#:strftime", keywords,
+                                         &format, &format_len))
                return NULL;
 
        tuple = PyObject_CallMethod((PyObject *)self, "timetuple", "()");
        if (tuple == NULL)
                return NULL;
-       result = wrap_strftime((PyObject *)self, format, tuple,
+       result = wrap_strftime((PyObject *)self, format, format_len, tuple,
                               (PyObject *)self);
        Py_DECREF(tuple);
        return result;
@@ -3256,12 +3258,13 @@ static PyObject *
 time_strftime(PyDateTime_Time *self, PyObject *args, PyObject *kw)
 {
        PyObject *result;
-       PyObject *format;
        PyObject *tuple;
+       const char *format;
+       Py_ssize_t format_len;
        static char *keywords[] = {"format", NULL};
 
-       if (! PyArg_ParseTupleAndKeywords(args, kw, "O!:strftime", keywords,
-                                         &PyBytes_Type, &format))
+       if (! PyArg_ParseTupleAndKeywords(args, kw, "s#:strftime", keywords,
+                                         &format, &format_len))
                return NULL;
 
        /* Python's strftime does insane things with the year part of the
@@ -3277,7 +3280,8 @@ time_strftime(PyDateTime_Time *self, PyObject *args, PyObject *kw)
        if (tuple == NULL)
                return NULL;
        assert(PyTuple_Size(tuple) == 9);
-       result = wrap_strftime((PyObject *)self, format, tuple, Py_None);
+       result = wrap_strftime((PyObject *)self, format, format_len, tuple,
+                              Py_None);
        Py_DECREF(tuple);
        return result;
 }