bpo-35066: _dateime.datetime.strftime copies trailing '%' (GH-10692) · python/cpython@26122de

3 files changed

lines changed

Original file line numberDiff line numberDiff line change

@@ -1351,6 +1351,17 @@ def test_strftime(self):

13511351

#check that this standard extension works

13521352

t.strftime("%f")

13531353
1354+

def test_strftime_trailing_percent(self):

1355+

# bpo-35066: make sure trailing '%' doesn't cause

1356+

# datetime's strftime to complain

1357+

t = self.theclass(2005, 3, 2)

1358+

try:

1359+

_time.strftime('%')

1360+

except ValueError:

1361+

self.skipTest('time module does not support trailing %')

1362+

self.assertEqual(t.strftime('%'), '%')

1363+

self.assertEqual(t.strftime("m:%m d:%d y:%y %"), "m:03 d:02 y:05 %")

1364+
13541365

def test_format(self):

13551366

dt = self.theclass(2007, 9, 10)

13561367

self.assertEqual(dt.__format__(''), str(dt))

Original file line numberDiff line numberDiff line change

@@ -0,0 +1,5 @@

1+

Previously, calling the strftime() method on a datetime object with a

2+

trailing '%' in the format string would result in an exception. However,

3+

this only occured when the datetime C module was being used; the python

4+

implementation did not match this behavior. Datetime is now PEP-399

5+

compliant, and will not throw an exception on a trailing '%'.

Original file line numberDiff line numberDiff line change

@@ -1514,10 +1514,13 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,

15141514

ntoappend = 1;

15151515

}

15161516

else if ((ch = *pin++) == '\0') {

1517-

/* There's a lone trailing %; doesn't make sense. */

1518-

PyErr_SetString(PyExc_ValueError, "strftime format "

1519-

"ends with raw %");

1520-

goto Done;

1517+

/* Null byte follows %, copy only '%'.

1518+

*

1519+

* Back the pin up one char so that we catch the null check

1520+

* the next time through the loop.*/

1521+

pin--;

1522+

ptoappend = pin - 1;

1523+

ntoappend = 1;

15211524

}

15221525

/* A % has been seen and ch is the character after it. */

15231526

else if (ch == 'z') {

@@ -1602,7 +1605,7 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,

16021605

usednew += ntoappend;

16031606

assert(usednew <= totalnew);

16041607

} /* end while() */

1605-
1608+
16061609

if (_PyBytes_Resize(&newfmt, usednew) < 0)

16071610

goto Done;

16081611

{