diff -r ddec854843f1 Lib/datetime.py --- a/Lib/datetime.py Thu Sep 20 09:47:41 2012 +0300 +++ b/Lib/datetime.py Thu Sep 20 15:16:28 2012 +0300 @@ -176,8 +176,9 @@ freplace = None # the string to use for %f zreplace = None # the string to use for %z Zreplace = None # the string to use for %Z + sreplace = None # the string to use for %s - # Scan format for %z and %Z escapes, replacing as needed. + # Scan format for %z %Z %f and %s escapes, replacing as needed. newformat = [] push = newformat.append i, n = 0, len(format) @@ -218,6 +219,10 @@ # strftime is going to have at this: escape % Zreplace = s.replace('%', '%%') newformat.append(Zreplace) + elif ch == 's' and isinstance(object, datetime): + if sreplace is None: + sreplace = '%d' % object.timestamp() + newformat.append(sreplace) else: push('%') push(ch) diff -r ddec854843f1 Lib/test/datetimetester.py --- a/Lib/test/datetimetester.py Thu Sep 20 09:47:41 2012 +0300 +++ b/Lib/test/datetimetester.py Thu Sep 20 15:16:28 2012 +0300 @@ -1135,6 +1135,7 @@ for fmt in ["m:%m d:%d y:%y", "m:%m d:%d y:%y H:%H M:%M S:%S", "%z %Z", + "%s", ]: self.assertEqual(dt.__format__(fmt), dt.strftime(fmt)) self.assertEqual(a.__format__(fmt), dt.strftime(fmt)) @@ -1518,6 +1519,30 @@ t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name')) self.assertRaises(TypeError, t.strftime, '%Z') + # Run with US-style DST rules: DST begins 2 a.m. on second Sunday in + # March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0). + @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0') + def test_strftime_naive(self): + t = self.theclass(1970, 1, 1) + self.assertEqual(t.strftime('%s'), '18000') + t = self.theclass(1970, 1, 1, 1, 2, 3, 4) + self.assertEqual(t.strftime('%s'), + str(18000 + 3600 + 2*60 + 3)) + + def test_strftime_respect_timezone(self): + # Issue 12750: datetime.strftime('%s') should respect tzinfo + t = self.theclass(1970, 1, 1, tzinfo=timezone.utc) + self.assertEqual(t.strftime('%s'), '%d' % 0.0) + + t = self.theclass(1970, 1, 1, 1, 2, 3, 4, tzinfo=timezone.utc) + self.assertEqual(t.strftime('%s'), + '%d' % (3600 + 2*60 + 3 + 4*1e-6)) + + t = self.theclass(1970, 1, 1, 1, 2, 3, 4, + tzinfo=timezone(timedelta(hours=-5), 'EST')) + self.assertEqual(t.strftime('%s'), + '%d' % (18000 + 3600 + 2*60 + 3 + 4*1e-6)) + def test_bad_constructor_arguments(self): # bad years self.theclass(MINYEAR, 1, 1) # no exception diff -r ddec854843f1 Modules/_datetimemodule.c --- a/Modules/_datetimemodule.c Thu Sep 20 09:47:41 2012 +0300 +++ b/Modules/_datetimemodule.c Thu Sep 20 15:16:28 2012 +0300 @@ -1120,6 +1120,13 @@ } static PyObject * +make_sreplacement(PyObject *object) +{ + _Py_IDENTIFIER(timestamp); + return PyObject_Str(PyNumber_Long(_PyObject_CallMethodId(object, &PyId_timestamp, ""))); +} + +static PyObject * make_freplacement(PyObject *object) { char freplacement[64]; @@ -1149,6 +1156,7 @@ PyObject *zreplacement = NULL; /* py string, replacement for %z */ PyObject *Zreplacement = NULL; /* py string, replacement for %Z */ PyObject *freplacement = NULL; /* py string, replacement for %f */ + PyObject *sreplacement = NULL; /* py string, replacement for %s */ const char *pin; /* pointer to next char in input format */ Py_ssize_t flen; /* length of input format */ @@ -1239,6 +1247,18 @@ if (ptoappend == NULL) goto Done; } + else if (ch == 's' && PyDateTime_Check(object)) { + /* format timestamp */ + if (sreplacement == NULL) { + sreplacement = make_sreplacement(object); + if (sreplacement == NULL) + goto Done; + } + assert(sreplacement != NULL); + assert(PyUnicode_Check(sreplacement)); + ptoappend = _PyUnicode_AsStringAndSize(sreplacement, + &ntoappend); + } else if (ch == 'f') { /* format microseconds */ if (freplacement == NULL) { @@ -4761,7 +4781,7 @@ static char *keywords[] = {"tz", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kw, "|O:astimezone", keywords, - &tzinfo)) + &tzinfo)) return NULL; if (check_tzinfo_subclass(tzinfo) == -1)