diff -r c53d24ee3963 Lib/datetime.py --- a/Lib/datetime.py Sat Jul 25 02:45:18 2015 +0200 +++ b/Lib/datetime.py Sat Jul 25 15:46:05 2015 +0200 @@ -152,13 +152,19 @@ dnum = _days_before_month(y, m) + d return _time.struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag)) -def _format_time(hh, mm, ss, us): + +def _format_time(hh, mm=None, ss=None, us=None): # Skip trailing microseconds when us==0. - result = "%02d:%02d:%02d" % (hh, mm, ss) + result = "%02d" % hh + if mm: + result += ":%02d" % mm + if ss: + result += ":%02d" % ss if us: result += ".%06d" % us return result + # Correctly substitute for %z and %Z escapes in strftime formats. def _wrap_strftime(object, format, timetuple): # Don't call utcoffset() or tzname() unless actually needed. @@ -1554,7 +1560,7 @@ self._hour, self._minute, self._second, self._year) - def isoformat(self, sep='T'): + def isoformat(self, sep='T', timespec='auto'): """Return the time formatted according to ISO. This is 'YYYY-MM-DD HH:MM:SS.mmmmmm', or 'YYYY-MM-DD HH:MM:SS' if @@ -1565,10 +1571,28 @@ Optional argument sep specifies the separator between date and time, default 'T'. + + The optional argument timespec specifies the number of additional terms of the time to include. It can be one of the following: + + ‘auto’: Default behaviour. + ‘hours’: Append the hour of the day to the date. + ‘minutes’: Append the hours and minutes. + ‘seconds’: Append the hours, minutes and seconds. + ‘microseconds’: Append the hours, minutes, seconds and microseconds. """ - s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day, sep) + - _format_time(self._hour, self._minute, self._second, - self._microsecond)) + if timespec == 'hours': + s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day, sep) + + _format_time(self._hour)) + elif timespec == 'minutes': + s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day, sep) + + _format_time(self._hour, self._minute)) + elif timespec == 'seconds': + s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day, sep) + + _format_time(self._hour, self._minute, self._second)) + else: + s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day, sep) + + _format_time(self._hour, self._minute, self._second, + self._microsecond)) off = self.utcoffset() if off is not None: if off.days < 0: diff -r c53d24ee3963 Lib/test/datetimetester.py --- a/Lib/test/datetimetester.py Sat Jul 25 02:45:18 2015 +0200 +++ b/Lib/test/datetimetester.py Sat Jul 25 15:46:05 2015 +0200 @@ -1506,6 +1506,12 @@ self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123") self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123") self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123") + self.assertEqual(t.isoformat(timespec='hours'), "0002-03-02\x0004") + self.assertEqual(t.isoformat(timespec='minutes'), "0002-03-02\x0004:05") + self.assertEqual(t.isoformat(timespec='seconds'), "0002-03-02\x0004:05:01") + self.assertEqual(t.isoformat(timespec='microseconds'), "0002-03-02\x0004:05:01.000123") + self.assertEqual(t.isoformat(timespec='auto'), "0002-03-02\x0004:05:01.000123") + self.assertEqual(t.isoformat(' ', ' '), "0002-03-02 04:05:01.000123") # str is ISO format with the separator forced to a blank. self.assertEqual(str(t), "0002-03-02 04:05:01.000123") diff -r c53d24ee3963 Modules/_datetimemodule.c --- a/Modules/_datetimemodule.c Sat Jul 25 02:45:18 2015 +0200 +++ b/Modules/_datetimemodule.c Sat Jul 25 15:46:05 2015 +0200 @@ -4483,25 +4483,46 @@ datetime_isoformat(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) { int sep = 'T'; - static char *keywords[] = {"sep", NULL}; + char *timespec = "auto"; + PyObject *hours = Py_BuildValue("s", "hours"); + PyObject *minutes = Py_BuildValue("s", "minutes"); + PyObject *seconds = Py_BuildValue("s", "seconds"); + PyObject *microseconds = Py_BuildValue("s", "microseconds"); + static char *keywords[] = {"sep", "timespec", NULL}; char buffer[100]; - PyObject *result; + PyObject *result = NULL; int us = DATE_GET_MICROSECOND(self); - if (!PyArg_ParseTupleAndKeywords(args, kw, "|C:isoformat", keywords, &sep)) - return NULL; - if (us) - result = PyUnicode_FromFormat("%04d-%02d-%02d%c%02d:%02d:%02d.%06d", - GET_YEAR(self), GET_MONTH(self), - GET_DAY(self), (int)sep, - DATE_GET_HOUR(self), DATE_GET_MINUTE(self), - DATE_GET_SECOND(self), us); - else - result = PyUnicode_FromFormat("%04d-%02d-%02d%c%02d:%02d:%02d", - GET_YEAR(self), GET_MONTH(self), - GET_DAY(self), (int)sep, - DATE_GET_HOUR(self), DATE_GET_MINUTE(self), - DATE_GET_SECOND(self)); + if (PyArg_ParseTupleAndKeywords(args, kw, "|Cs:isoformat", keywords, &sep, ×pec)){ + if (PyUnicode_Compare(Py_BuildValue("s", timespec), hours) == 0) + result = PyUnicode_FromFormat("%04d-%02d-%02d%c%02d", + GET_YEAR(self), GET_MONTH(self), + GET_DAY(self), (int)sep, + DATE_GET_HOUR(self)); + if (PyUnicode_Compare(Py_BuildValue("s", timespec), minutes) == 0) + result = PyUnicode_FromFormat("%04d-%02d-%02d%c%02d:%02d", + GET_YEAR(self), GET_MONTH(self), + GET_DAY(self), (int)sep, + DATE_GET_HOUR(self), DATE_GET_MINUTE(self)); + if (PyUnicode_Compare(Py_BuildValue("s", timespec), seconds) == 0) + result = PyUnicode_FromFormat("%04d-%02d-%02d%c%02d:%02d:%02d", + GET_YEAR(self), GET_MONTH(self), + GET_DAY(self), (int)sep, + DATE_GET_HOUR(self), DATE_GET_MINUTE(self), + DATE_GET_SECOND(self)); + if (PyUnicode_Compare(Py_BuildValue("s", timespec), microseconds) == 0 && us) + result = PyUnicode_FromFormat("%04d-%02d-%02d%c%02d:%02d:%02d.%06d", + GET_YEAR(self), GET_MONTH(self), + GET_DAY(self), (int)sep, + DATE_GET_HOUR(self), DATE_GET_MINUTE(self), + DATE_GET_SECOND(self), us); + if (!result) + result = PyUnicode_FromFormat("%04d-%02d-%02d%c%02d:%02d:%02d", + GET_YEAR(self), GET_MONTH(self), + GET_DAY(self), (int)sep, + DATE_GET_HOUR(self), DATE_GET_MINUTE(self), + DATE_GET_SECOND(self)); + } if (!result || !HASTZINFO(self)) return result;