diff -r 6c2e2de5ab8e Doc/library/datetime.rst --- a/Doc/library/datetime.rst Sat Jul 30 23:51:13 2016 -0700 +++ b/Doc/library/datetime.rst Mon Aug 01 11:54:37 2016 -0400 @@ -794,16 +794,23 @@ microsecond of the result are all 0, and :attr:`.tzinfo` is ``None``. -.. classmethod:: datetime.combine(date, time) +.. classmethod:: datetime.combine(date, time[, tzinfo]) Return a new :class:`.datetime` object whose date components are equal to the - given :class:`date` object's, and whose time components and :attr:`.tzinfo` - attributes are equal to the given :class:`.time` object's. For any - :class:`.datetime` object *d*, - ``d == datetime.combine(d.date(), d.timetz())``. If date is a + given :class:`date` object's, and whose time components + attributes are equal to the given :class:`.time` object'. If the *tzinfo* + argument is provided, its value is used to set the :attr:`.tzinfo` attribute + of the result, otherwise the :attr:`.tzinfo` attribute of the *time* argument + is used. + + For any :class:`.datetime` object *d*, + ``d == datetime.combine(d.date(), d.time(), d.tzinfo)``. If date is a :class:`.datetime` object, its time components and :attr:`.tzinfo` attributes are ignored. + .. versionchanged:: 3.6 + Added the *tzinfo* argument. + .. classmethod:: datetime.strptime(date_string, format) diff -r 6c2e2de5ab8e Lib/datetime.py --- a/Lib/datetime.py Sat Jul 30 23:51:13 2016 -0700 +++ b/Lib/datetime.py Mon Aug 01 11:54:37 2016 -0400 @@ -1479,15 +1479,17 @@ return cls.utcfromtimestamp(t) @classmethod - def combine(cls, date, time): + def combine(cls, date, time, tzinfo=True): "Construct a datetime from a given date and a given time." if not isinstance(date, _date_class): raise TypeError("date argument must be a date instance") if not isinstance(time, _time_class): raise TypeError("time argument must be a time instance") + if tzinfo is True: + tzinfo = time.tzinfo return cls(date.year, date.month, date.day, time.hour, time.minute, time.second, time.microsecond, - time.tzinfo, fold=time.fold) + tzinfo, fold=time.fold) def timetuple(self): "Return local time tuple compatible with time.localtime()." diff -r 6c2e2de5ab8e Lib/test/datetimetester.py --- a/Lib/test/datetimetester.py Sat Jul 30 23:51:13 2016 -0700 +++ b/Lib/test/datetimetester.py Mon Aug 01 11:54:37 2016 -0400 @@ -2117,11 +2117,18 @@ self.assertRaises(TypeError, combine) # need an arg self.assertRaises(TypeError, combine, d) # need two args self.assertRaises(TypeError, combine, t, d) # args reversed - self.assertRaises(TypeError, combine, d, t, 1) # too many args + self.assertRaises(TypeError, combine, d, t, 1) # wrong tzinfo type + self.assertRaises(TypeError, combine, d, t, 1, 2) # too many args self.assertRaises(TypeError, combine, "date", "time") # wrong types self.assertRaises(TypeError, combine, d, "time") # wrong type self.assertRaises(TypeError, combine, "date", t) # wrong type + # tzinfo= argument + dt = combine(d, t, timezone.utc) + self.assertIs(dt.tzinfo, timezone.utc) + dt = combine(d, t, tzinfo=timezone.utc) + self.assertIs(dt.tzinfo, timezone.utc) + def test_replace(self): cls = self.theclass args = [1, 2, 3, 4, 5, 6, 7] diff -r 6c2e2de5ab8e Misc/NEWS --- a/Misc/NEWS Sat Jul 30 23:51:13 2016 -0700 +++ b/Misc/NEWS Mon Aug 01 11:54:37 2016 -0400 @@ -38,6 +38,8 @@ Library ------- +- Issue 27661: Added tzinfo= argument to datetime.combine. + - Issue #27568: Prevent HTTPoxy attack (CVE-2016-1000110). Ignore the HTTP_PROXY variable when REQUEST_METHOD environment is set, which indicates that the script is in CGI mode. diff -r 6c2e2de5ab8e Modules/_datetimemodule.c --- a/Modules/_datetimemodule.c Sat Jul 30 23:51:13 2016 -0700 +++ b/Modules/_datetimemodule.c Mon Aug 01 11:54:37 2016 -0400 @@ -4430,28 +4430,32 @@ static PyObject * datetime_combine(PyObject *cls, PyObject *args, PyObject *kw) { - static char *keywords[] = {"date", "time", NULL}; + static char *keywords[] = {"date", "time", "tzinfo", NULL}; PyObject *date; PyObject *time; + PyObject *tzinfo = NULL; PyObject *result = NULL; - if (PyArg_ParseTupleAndKeywords(args, kw, "O!O!:combine", keywords, + if (PyArg_ParseTupleAndKeywords(args, kw, "O!O!|O:combine", keywords, &PyDateTime_DateType, &date, - &PyDateTime_TimeType, &time)) { - PyObject *tzinfo = Py_None; - - if (HASTZINFO(time)) - tzinfo = ((PyDateTime_Time *)time)->tzinfo; + &PyDateTime_TimeType, &time, &tzinfo)) { + if (tzinfo == NULL) { + if (HASTZINFO(time)) + tzinfo = ((PyDateTime_Time *)time)->tzinfo; + else + tzinfo = Py_None; + } result = PyObject_CallFunction(cls, "iiiiiiiO", - GET_YEAR(date), - GET_MONTH(date), - GET_DAY(date), - TIME_GET_HOUR(time), - TIME_GET_MINUTE(time), - TIME_GET_SECOND(time), - TIME_GET_MICROSECOND(time), - tzinfo); - DATE_SET_FOLD(result, TIME_GET_FOLD(time)); + GET_YEAR(date), + GET_MONTH(date), + GET_DAY(date), + TIME_GET_HOUR(time), + TIME_GET_MINUTE(time), + TIME_GET_SECOND(time), + TIME_GET_MICROSECOND(time), + tzinfo); + if (result) + DATE_SET_FOLD(result, TIME_GET_FOLD(time)); } return result; }