Closes #27661: Added tzinfo keyword argument to datetime.combine.

This commit is contained in:
Alexander Belopolsky 2016-08-02 17:49:30 -04:00
parent 711120d8fd
commit 43746c3770
5 changed files with 50 additions and 24 deletions

View File

@ -794,16 +794,23 @@ Other constructors, all class methods:
microsecond of the result are all 0, and :attr:`.tzinfo` is ``None``. 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 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` given :class:`date` object's, and whose time components
attributes are equal to the given :class:`.time` object's. For any are equal to the given :class:`.time` object's. If the *tzinfo*
:class:`.datetime` object *d*, argument is provided, its value is used to set the :attr:`.tzinfo` attribute
``d == datetime.combine(d.date(), d.timetz())``. If date is a of the result, otherwise the :attr:`~.time.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 :class:`.datetime` object, its time components and :attr:`.tzinfo` attributes
are ignored. are ignored.
.. versionchanged:: 3.6
Added the *tzinfo* argument.
.. classmethod:: datetime.strptime(date_string, format) .. classmethod:: datetime.strptime(date_string, format)

View File

@ -1479,15 +1479,17 @@ class datetime(date):
return cls.utcfromtimestamp(t) return cls.utcfromtimestamp(t)
@classmethod @classmethod
def combine(cls, date, time): def combine(cls, date, time, tzinfo=True):
"Construct a datetime from a given date and a given time." "Construct a datetime from a given date and a given time."
if not isinstance(date, _date_class): if not isinstance(date, _date_class):
raise TypeError("date argument must be a date instance") raise TypeError("date argument must be a date instance")
if not isinstance(time, _time_class): if not isinstance(time, _time_class):
raise TypeError("time argument must be a time instance") raise TypeError("time argument must be a time instance")
if tzinfo is True:
tzinfo = time.tzinfo
return cls(date.year, date.month, date.day, return cls(date.year, date.month, date.day,
time.hour, time.minute, time.second, time.microsecond, time.hour, time.minute, time.second, time.microsecond,
time.tzinfo, fold=time.fold) tzinfo, fold=time.fold)
def timetuple(self): def timetuple(self):
"Return local time tuple compatible with time.localtime()." "Return local time tuple compatible with time.localtime()."

View File

@ -2117,11 +2117,22 @@ class TestDateTime(TestDate):
self.assertRaises(TypeError, combine) # need an arg self.assertRaises(TypeError, combine) # need an arg
self.assertRaises(TypeError, combine, d) # need two args self.assertRaises(TypeError, combine, d) # need two args
self.assertRaises(TypeError, combine, t, d) # args reversed 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, "date", "time") # wrong types
self.assertRaises(TypeError, combine, d, "time") # wrong type self.assertRaises(TypeError, combine, d, "time") # wrong type
self.assertRaises(TypeError, combine, "date", t) # 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)
t = time()
dt = combine(dt, t)
self.assertEqual(dt.date(), d)
self.assertEqual(dt.time(), t)
def test_replace(self): def test_replace(self):
cls = self.theclass cls = self.theclass
args = [1, 2, 3, 4, 5, 6, 7] args = [1, 2, 3, 4, 5, 6, 7]

View File

@ -40,6 +40,8 @@ Core and Builtins
Library Library
------- -------
- Issue #27661: Added tzinfo keyword argument to datetime.combine.
- Issue #27568: Prevent HTTPoxy attack (CVE-2016-1000110). Ignore the - Issue #27568: Prevent HTTPoxy attack (CVE-2016-1000110). Ignore the
HTTP_PROXY variable when REQUEST_METHOD environment is set, which indicates HTTP_PROXY variable when REQUEST_METHOD environment is set, which indicates
that the script is in CGI mode. that the script is in CGI mode.

View File

@ -4430,18 +4430,21 @@ datetime_strptime(PyObject *cls, PyObject *args)
static PyObject * static PyObject *
datetime_combine(PyObject *cls, PyObject *args, PyObject *kw) datetime_combine(PyObject *cls, PyObject *args, PyObject *kw)
{ {
static char *keywords[] = {"date", "time", NULL}; static char *keywords[] = {"date", "time", "tzinfo", NULL};
PyObject *date; PyObject *date;
PyObject *time; PyObject *time;
PyObject *tzinfo = NULL;
PyObject *result = 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_DateType, &date,
&PyDateTime_TimeType, &time)) { &PyDateTime_TimeType, &time, &tzinfo)) {
PyObject *tzinfo = Py_None; if (tzinfo == NULL) {
if (HASTZINFO(time)) if (HASTZINFO(time))
tzinfo = ((PyDateTime_Time *)time)->tzinfo; tzinfo = ((PyDateTime_Time *)time)->tzinfo;
else
tzinfo = Py_None;
}
result = PyObject_CallFunction(cls, "iiiiiiiO", result = PyObject_CallFunction(cls, "iiiiiiiO",
GET_YEAR(date), GET_YEAR(date),
GET_MONTH(date), GET_MONTH(date),
@ -4451,6 +4454,7 @@ datetime_combine(PyObject *cls, PyObject *args, PyObject *kw)
TIME_GET_SECOND(time), TIME_GET_SECOND(time),
TIME_GET_MICROSECOND(time), TIME_GET_MICROSECOND(time),
tzinfo); tzinfo);
if (result)
DATE_SET_FOLD(result, TIME_GET_FOLD(time)); DATE_SET_FOLD(result, TIME_GET_FOLD(time));
} }
return result; return result;