summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Doc/library/datetime.rst17
-rw-r--r--Lib/datetime.py6
-rw-r--r--Lib/test/datetimetester.py13
-rw-r--r--Misc/NEWS2
-rw-r--r--Modules/_datetimemodule.c36
5 files changed, 50 insertions, 24 deletions
diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst
index cd78750a94..a286fbe866 100644
--- a/Doc/library/datetime.rst
+++ b/Doc/library/datetime.rst
@@ -794,16 +794,23 @@ Other constructors, all class methods:
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
+ are equal to the given :class:`.time` object's. If the *tzinfo*
+ argument is provided, its value is used to set the :attr:`.tzinfo` attribute
+ 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
are ignored.
+ .. versionchanged:: 3.6
+ Added the *tzinfo* argument.
+
.. classmethod:: datetime.strptime(date_string, format)
diff --git a/Lib/datetime.py b/Lib/datetime.py
index df8eb666a0..9f942a207e 100644
--- a/Lib/datetime.py
+++ b/Lib/datetime.py
@@ -1479,15 +1479,17 @@ class datetime(date):
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 --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py
index 02deb7c616..e71f3aa9b4 100644
--- a/Lib/test/datetimetester.py
+++ b/Lib/test/datetimetester.py
@@ -2117,11 +2117,22 @@ class TestDateTime(TestDate):
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)
+ t = time()
+ dt = combine(dt, t)
+ self.assertEqual(dt.date(), d)
+ self.assertEqual(dt.time(), t)
+
def test_replace(self):
cls = self.theclass
args = [1, 2, 3, 4, 5, 6, 7]
diff --git a/Misc/NEWS b/Misc/NEWS
index cd73ecd82c..04fb4b4c44 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -40,6 +40,8 @@ Core and Builtins
Library
-------
+- Issue #27661: Added tzinfo keyword 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 --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
index 7dfb0c2684..3048762034 100644
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -4430,28 +4430,32 @@ datetime_strptime(PyObject *cls, PyObject *args)
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;
}