Skip to content

Commit 43746c3

Browse files
committed
Closes #27661: Added tzinfo keyword argument to datetime.combine.
1 parent 711120d commit 43746c3

5 files changed

Lines changed: 50 additions & 24 deletions

File tree

Doc/library/datetime.rst

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -794,16 +794,23 @@ Other constructors, all class methods:
794794
microsecond of the result are all 0, and :attr:`.tzinfo` is ``None``.
795795

796796

797-
.. classmethod:: datetime.combine(date, time)
797+
.. classmethod:: datetime.combine(date, time[, tzinfo])
798798

799799
Return a new :class:`.datetime` object whose date components are equal to the
800-
given :class:`date` object's, and whose time components and :attr:`.tzinfo`
801-
attributes are equal to the given :class:`.time` object's. For any
802-
:class:`.datetime` object *d*,
803-
``d == datetime.combine(d.date(), d.timetz())``. If date is a
800+
given :class:`date` object's, and whose time components
801+
are equal to the given :class:`.time` object's. If the *tzinfo*
802+
argument is provided, its value is used to set the :attr:`.tzinfo` attribute
803+
of the result, otherwise the :attr:`~.time.tzinfo` attribute of the *time* argument
804+
is used.
805+
806+
For any :class:`.datetime` object *d*,
807+
``d == datetime.combine(d.date(), d.time(), d.tzinfo)``. If date is a
804808
:class:`.datetime` object, its time components and :attr:`.tzinfo` attributes
805809
are ignored.
806810

811+
.. versionchanged:: 3.6
812+
Added the *tzinfo* argument.
813+
807814

808815
.. classmethod:: datetime.strptime(date_string, format)
809816

Lib/datetime.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,15 +1479,17 @@ def utcnow(cls):
14791479
return cls.utcfromtimestamp(t)
14801480

14811481
@classmethod
1482-
def combine(cls, date, time):
1482+
def combine(cls, date, time, tzinfo=True):
14831483
"Construct a datetime from a given date and a given time."
14841484
if not isinstance(date, _date_class):
14851485
raise TypeError("date argument must be a date instance")
14861486
if not isinstance(time, _time_class):
14871487
raise TypeError("time argument must be a time instance")
1488+
if tzinfo is True:
1489+
tzinfo = time.tzinfo
14881490
return cls(date.year, date.month, date.day,
14891491
time.hour, time.minute, time.second, time.microsecond,
1490-
time.tzinfo, fold=time.fold)
1492+
tzinfo, fold=time.fold)
14911493

14921494
def timetuple(self):
14931495
"Return local time tuple compatible with time.localtime()."

Lib/test/datetimetester.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2117,11 +2117,22 @@ def test_combine(self):
21172117
self.assertRaises(TypeError, combine) # need an arg
21182118
self.assertRaises(TypeError, combine, d) # need two args
21192119
self.assertRaises(TypeError, combine, t, d) # args reversed
2120-
self.assertRaises(TypeError, combine, d, t, 1) # too many args
2120+
self.assertRaises(TypeError, combine, d, t, 1) # wrong tzinfo type
2121+
self.assertRaises(TypeError, combine, d, t, 1, 2) # too many args
21212122
self.assertRaises(TypeError, combine, "date", "time") # wrong types
21222123
self.assertRaises(TypeError, combine, d, "time") # wrong type
21232124
self.assertRaises(TypeError, combine, "date", t) # wrong type
21242125

2126+
# tzinfo= argument
2127+
dt = combine(d, t, timezone.utc)
2128+
self.assertIs(dt.tzinfo, timezone.utc)
2129+
dt = combine(d, t, tzinfo=timezone.utc)
2130+
self.assertIs(dt.tzinfo, timezone.utc)
2131+
t = time()
2132+
dt = combine(dt, t)
2133+
self.assertEqual(dt.date(), d)
2134+
self.assertEqual(dt.time(), t)
2135+
21252136
def test_replace(self):
21262137
cls = self.theclass
21272138
args = [1, 2, 3, 4, 5, 6, 7]

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ Core and Builtins
4040
Library
4141
-------
4242

43+
- Issue #27661: Added tzinfo keyword argument to datetime.combine.
44+
4345
- Issue #27568: Prevent HTTPoxy attack (CVE-2016-1000110). Ignore the
4446
HTTP_PROXY variable when REQUEST_METHOD environment is set, which indicates
4547
that the script is in CGI mode.

Modules/_datetimemodule.c

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4430,28 +4430,32 @@ datetime_strptime(PyObject *cls, PyObject *args)
44304430
static PyObject *
44314431
datetime_combine(PyObject *cls, PyObject *args, PyObject *kw)
44324432
{
4433-
static char *keywords[] = {"date", "time", NULL};
4433+
static char *keywords[] = {"date", "time", "tzinfo", NULL};
44344434
PyObject *date;
44354435
PyObject *time;
4436+
PyObject *tzinfo = NULL;
44364437
PyObject *result = NULL;
44374438

4438-
if (PyArg_ParseTupleAndKeywords(args, kw, "O!O!:combine", keywords,
4439+
if (PyArg_ParseTupleAndKeywords(args, kw, "O!O!|O:combine", keywords,
44394440
&PyDateTime_DateType, &date,
4440-
&PyDateTime_TimeType, &time)) {
4441-
PyObject *tzinfo = Py_None;
4442-
4443-
if (HASTZINFO(time))
4444-
tzinfo = ((PyDateTime_Time *)time)->tzinfo;
4441+
&PyDateTime_TimeType, &time, &tzinfo)) {
4442+
if (tzinfo == NULL) {
4443+
if (HASTZINFO(time))
4444+
tzinfo = ((PyDateTime_Time *)time)->tzinfo;
4445+
else
4446+
tzinfo = Py_None;
4447+
}
44454448
result = PyObject_CallFunction(cls, "iiiiiiiO",
4446-
GET_YEAR(date),
4447-
GET_MONTH(date),
4448-
GET_DAY(date),
4449-
TIME_GET_HOUR(time),
4450-
TIME_GET_MINUTE(time),
4451-
TIME_GET_SECOND(time),
4452-
TIME_GET_MICROSECOND(time),
4453-
tzinfo);
4454-
DATE_SET_FOLD(result, TIME_GET_FOLD(time));
4449+
GET_YEAR(date),
4450+
GET_MONTH(date),
4451+
GET_DAY(date),
4452+
TIME_GET_HOUR(time),
4453+
TIME_GET_MINUTE(time),
4454+
TIME_GET_SECOND(time),
4455+
TIME_GET_MICROSECOND(time),
4456+
tzinfo);
4457+
if (result)
4458+
DATE_SET_FOLD(result, TIME_GET_FOLD(time));
44554459
}
44564460
return result;
44574461
}

0 commit comments

Comments
 (0)