diff -r 4cfb35dca5e8 Doc/library/datetime.rst --- a/Doc/library/datetime.rst Mon Sep 19 11:56:06 2016 +0200 +++ b/Doc/library/datetime.rst Tue Sep 20 09:54:45 2016 +0200 @@ -562,7 +562,7 @@ .. method:: date.isocalendar() - Return a 3-tuple, (ISO year, ISO week number, ISO weekday). + Return a :term:`named tuple` ``IsoCalendarDate(year, week, weekday)``. The ISO calendar is a widely used variant of the Gregorian calendar. See https://www.staff.science.uu.nl/~gent0113/calendar/isocalendar.htm for a good @@ -578,6 +578,9 @@ ``date(2003, 12, 29).isocalendar() == (2004, 1, 1)`` and ``date(2004, 1, 4).isocalendar() == (2004, 1, 7)``. + .. versionchanged:: 3.7 + Result changed from a tuple to a :term:`named tuple`. + .. method:: date.isoformat() @@ -655,11 +658,8 @@ 70 # 70th day in the year -1 >>> ic = d.isocalendar() - >>> for i in ic: # doctest: +SKIP - ... print(i) - 2002 # ISO year - 11 # ISO week number - 1 # ISO day number ( 1 = Monday ) + >>> print(ic.year, ic.week, i.weekday) + 2002 11 1 >>> d.isoformat() '2002-03-11' >>> d.strftime("%d/%m/%y") @@ -1174,8 +1174,8 @@ .. method:: datetime.isocalendar() - Return a 3-tuple, (ISO year, ISO week number, ISO weekday). The same as - ``self.date().isocalendar()``. + Return a :term:`named tuple` ``IsoCalendarDate(year, week, weekday)``. The + same as ``self.date().isocalendar()``. .. method:: datetime.isoformat(sep='T', timespec='auto') diff -r 4cfb35dca5e8 Lib/datetime.py --- a/Lib/datetime.py Mon Sep 19 11:56:06 2016 +0200 +++ b/Lib/datetime.py Tue Sep 20 09:54:45 2016 +0200 @@ -4,6 +4,7 @@ time zone and DST data sources. """ +from collections import namedtuple as _namedtuple import time as _time import math as _math @@ -655,6 +656,8 @@ microseconds=999999) timedelta.resolution = timedelta(microseconds=1) +IsoCalendarDate = _namedtuple('IsoCalendarDate', 'year week weekday') + class date: """Concrete date type. @@ -903,7 +906,7 @@ return self.toordinal() % 7 or 7 def isocalendar(self): - """Return a 3-tuple containing ISO year, week number, and weekday. + """Return a 3-named tuple containing ISO year, week number, and weekday. The first ISO week of the year is the (Mon-Sun) week containing the year's first Thursday; everything else derives @@ -928,7 +931,7 @@ if today >= _isoweek1monday(year+1): year += 1 week = 0 - return year, week+1, day+1 + return IsoCalendarDate(year, week+1, day+1) # Pickle support. diff -r 4cfb35dca5e8 Lib/test/datetimetester.py --- a/Lib/test/datetimetester.py Mon Sep 19 11:56:06 2016 +0200 +++ b/Lib/test/datetimetester.py Tue Sep 20 09:54:45 2016 +0200 @@ -1168,6 +1168,13 @@ d = self.theclass(2010, 1, 4+i) self.assertEqual(d.isocalendar(), (2010, 1, i+1)) + def test_isocalendar_named(self): + d = self.theclass(2008, 12, 3) + t = d.isocalendar() + self.assertEqual(t.year, 2008) + self.assertEqual(t.week, 49) + self.assertEqual(t.weekday, 3) + def test_iso_long_years(self): # Calculate long ISO years and compare to table from # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm diff -r 4cfb35dca5e8 Misc/NEWS --- a/Misc/NEWS Mon Sep 19 11:56:06 2016 +0200 +++ b/Misc/NEWS Tue Sep 20 09:54:45 2016 +0200 @@ -82,6 +82,8 @@ - Issue #27759: Fix selectors incorrectly retain invalid file descriptors. Patch by Mark Williams. +- Issue #24416: date.isocalendar() now returns a named tuple. + Windows ------- diff -r 4cfb35dca5e8 Modules/_datetimemodule.c --- a/Modules/_datetimemodule.c Mon Sep 19 11:56:06 2016 +0200 +++ b/Modules/_datetimemodule.c Tue Sep 20 09:54:45 2016 +0200 @@ -2768,6 +2768,27 @@ return PyLong_FromLong(dow + 1); } +static PyStructSequence_Field struct_iso_calendar_date_fields[] = { + {"year", "year, for example, 1993"}, + {"week", "week, range [1, 53]"}, + {"weekday", "week day, range [1, 7]"}, + {0} +}; + +static PyStructSequence_Desc struct_iso_calendar_date_desc = { + "datetime.IsoCalendarDate", + "The result of date.isocalendar() or datetime.isocalendar().\n\n" + "This object may be accessed either as a tuple of\n" + " (year,week, weekday)\n" + "or via the object attributes as named in the above tuple.", + struct_iso_calendar_date_fields, + 3, +}; + +static int initialized; +static PyTypeObject StructIsoCalendarDateType; + + static PyObject * date_isocalendar(PyDateTime_Date *self) { @@ -2777,6 +2798,10 @@ int week; int day; + PyObject *v = PyStructSequence_New(&StructIsoCalendarDateType); + if (v == NULL) + return NULL; + week = divmod(today - week1_monday, 7, &day); if (week < 0) { --year; @@ -2787,7 +2812,21 @@ ++year; week = 0; } - return Py_BuildValue("iii", year, week + 1, day + 1); + +#define SET(i,val) PyStructSequence_SET_ITEM(v, i, PyLong_FromLong((long) val)) + + SET(0, year); + SET(1, week + 1); + SET(2, day + 1); + +#undef SET + + if (PyErr_Occurred()) { + Py_XDECREF(v); + return NULL; + } + + return v; } /* Miscellaneous methods. */ @@ -2920,7 +2959,7 @@ PyDoc_STR("Return time tuple, compatible with time.localtime().")}, {"isocalendar", (PyCFunction)date_isocalendar, METH_NOARGS, - PyDoc_STR("Return a 3-tuple containing ISO year, week number, and " + PyDoc_STR("Return a 3-named tuple containing ISO year, week number, and " "weekday.")}, {"isoformat", (PyCFunction)date_isoformat, METH_NOARGS, @@ -5783,6 +5822,16 @@ PyModule_AddIntMacro(m, MINYEAR); PyModule_AddIntMacro(m, MAXYEAR); + /* IsoCalendarDate */ + if (!initialized) { + if (PyStructSequence_InitType2(&StructIsoCalendarDateType, + &struct_iso_calendar_date_desc) < 0) + return NULL; + initialized = 1; + } + Py_INCREF((PyObject *) &StructIsoCalendarDateType); + PyModule_AddObject(m, "IsoCalendarDate", (PyObject *) &StructIsoCalendarDateType); + Py_INCREF(&PyDateTime_DateType); PyModule_AddObject(m, "date", (PyObject *) &PyDateTime_DateType);