summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Ganssle <1377457+pganssle@users.noreply.github.com>2023-04-27 17:32:30 (GMT)
committerGitHub <noreply@github.com>2023-04-27 17:32:30 (GMT)
commit0b7fd8ffc5df187edf8b5d926cee359924462df5 (patch)
treef88f8ba6dea519974bc2da3a4e18d69d07aefc8f
parenta5308e188b810e5cc69c1570bdc9b21ed6c87805 (diff)
downloadcpython-0b7fd8ffc5df187edf8b5d926cee359924462df5.zip
cpython-0b7fd8ffc5df187edf8b5d926cee359924462df5.tar.gz
cpython-0b7fd8ffc5df187edf8b5d926cee359924462df5.tar.bz2
GH-103857: Deprecate utcnow and utcfromtimestamp (#103858)
Using `datetime.datetime.utcnow()` and `datetime.datetime.utcfromtimestamp()` will now raise a `DeprecationWarning`. We also have removed our internal uses of these functions and documented the change.
-rw-r--r--Doc/library/datetime.rst8
-rw-r--r--Lib/datetime.py16
-rw-r--r--Lib/email/utils.py8
-rw-r--r--Lib/http/cookiejar.py8
-rw-r--r--Lib/test/datetimetester.py68
-rw-r--r--Lib/test/support/testresult.py7
-rw-r--r--Lib/test/test_plistlib.py2
-rw-r--r--Lib/test/test_sqlite3/test_types.py2
-rw-r--r--Misc/NEWS.d/next/Library/2023-04-25-17-03-18.gh-issue-103857.Mr2Cak.rst2
-rw-r--r--Modules/_datetimemodule.c14
10 files changed, 101 insertions, 34 deletions
diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst
index 7889dd7..bed19ad 100644
--- a/Doc/library/datetime.rst
+++ b/Doc/library/datetime.rst
@@ -896,6 +896,10 @@ Other constructors, all class methods:
in UTC. As such, the recommended way to create an object representing the
current time in UTC is by calling ``datetime.now(timezone.utc)``.
+ .. deprecated:: 3.12
+
+ Use :meth:`datetime.now` with :attr:`UTC` instead.
+
.. classmethod:: datetime.fromtimestamp(timestamp, tz=None)
@@ -964,6 +968,10 @@ Other constructors, all class methods:
:c:func:`gmtime` function. Raise :exc:`OSError` instead of
:exc:`ValueError` on :c:func:`gmtime` failure.
+ .. deprecated:: 3.12
+
+ Use :meth:`datetime.fromtimestamp` with :attr:`UTC` instead.
+
.. classmethod:: datetime.fromordinal(ordinal)
diff --git a/Lib/datetime.py b/Lib/datetime.py
index 09a2d2d..b0eb1c2 100644
--- a/Lib/datetime.py
+++ b/Lib/datetime.py
@@ -1801,6 +1801,13 @@ class datetime(date):
@classmethod
def utcfromtimestamp(cls, t):
"""Construct a naive UTC datetime from a POSIX timestamp."""
+ import warnings
+ warnings.warn("datetime.utcfromtimestamp() is deprecated and scheduled "
+ "for removal in a future version. Use timezone-aware "
+ "objects to represent datetimes in UTC: "
+ "datetime.fromtimestamp(t, datetime.UTC).",
+ DeprecationWarning,
+ stacklevel=2)
return cls._fromtimestamp(t, True, None)
@classmethod
@@ -1812,8 +1819,15 @@ class datetime(date):
@classmethod
def utcnow(cls):
"Construct a UTC datetime from time.time()."
+ import warnings
+ warnings.warn("datetime.utcnow() is deprecated and scheduled for "
+ "removal in a future version. Instead, Use timezone-aware "
+ "objects to represent datetimes in UTC: "
+ "datetime.now(datetime.UTC).",
+ DeprecationWarning,
+ stacklevel=2)
t = _time.time()
- return cls.utcfromtimestamp(t)
+ return cls._fromtimestamp(t, True, None)
@classmethod
def combine(cls, date, time, tzinfo=True):
diff --git a/Lib/email/utils.py b/Lib/email/utils.py
index 4d014ba..81da539 100644
--- a/Lib/email/utils.py
+++ b/Lib/email/utils.py
@@ -143,13 +143,13 @@ def formatdate(timeval=None, localtime=False, usegmt=False):
# 2822 requires that day and month names be the English abbreviations.
if timeval is None:
timeval = time.time()
- if localtime or usegmt:
- dt = datetime.datetime.fromtimestamp(timeval, datetime.timezone.utc)
- else:
- dt = datetime.datetime.utcfromtimestamp(timeval)
+ dt = datetime.datetime.fromtimestamp(timeval, datetime.timezone.utc)
+
if localtime:
dt = dt.astimezone()
usegmt = False
+ elif not usegmt:
+ dt = dt.replace(tzinfo=None)
return format_datetime(dt, usegmt)
def format_datetime(dt, usegmt=False):
diff --git a/Lib/http/cookiejar.py b/Lib/http/cookiejar.py
index 93b10d2..bd89370 100644
--- a/Lib/http/cookiejar.py
+++ b/Lib/http/cookiejar.py
@@ -104,9 +104,9 @@ def time2isoz(t=None):
"""
if t is None:
- dt = datetime.datetime.utcnow()
+ dt = datetime.datetime.now(tz=datetime.UTC)
else:
- dt = datetime.datetime.utcfromtimestamp(t)
+ dt = datetime.datetime.fromtimestamp(t, tz=datetime.UTC)
return "%04d-%02d-%02d %02d:%02d:%02dZ" % (
dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second)
@@ -122,9 +122,9 @@ def time2netscape(t=None):
"""
if t is None:
- dt = datetime.datetime.utcnow()
+ dt = datetime.datetime.now(tz=datetime.UTC)
else:
- dt = datetime.datetime.utcfromtimestamp(t)
+ dt = datetime.datetime.fromtimestamp(t, tz=datetime.UTC)
return "%s, %02d-%s-%04d %02d:%02d:%02d GMT" % (
DAYS[dt.weekday()], dt.day, MONTHS[dt.month-1],
dt.year, dt.hour, dt.minute, dt.second)
diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py
index 477f16f..c5eb6e7 100644
--- a/Lib/test/datetimetester.py
+++ b/Lib/test/datetimetester.py
@@ -2437,7 +2437,8 @@ class TestDateTime(TestDate):
ts = time.time()
expected = time.gmtime(ts)
- got = self.theclass.utcfromtimestamp(ts)
+ with self.assertWarns(DeprecationWarning):
+ got = self.theclass.utcfromtimestamp(ts)
self.verify_field_equality(expected, got)
# Run with US-style DST rules: DST begins 2 a.m. on second Sunday in
@@ -2483,8 +2484,12 @@ class TestDateTime(TestDate):
@support.run_with_tz('MSK-03') # Something east of Greenwich
def test_microsecond_rounding(self):
+ def utcfromtimestamp(*args, **kwargs):
+ with self.assertWarns(DeprecationWarning):
+ return self.theclass.utcfromtimestamp(*args, **kwargs)
+
for fts in [self.theclass.fromtimestamp,
- self.theclass.utcfromtimestamp]:
+ utcfromtimestamp]:
zero = fts(0)
self.assertEqual(zero.second, 0)
self.assertEqual(zero.microsecond, 0)
@@ -2581,10 +2586,11 @@ class TestDateTime(TestDate):
self.theclass.fromtimestamp(ts)
def test_utcfromtimestamp_limits(self):
- try:
- self.theclass.utcfromtimestamp(-2**32 - 1)
- except (OSError, OverflowError):
- self.skipTest("Test not valid on this platform")
+ with self.assertWarns(DeprecationWarning):
+ try:
+ self.theclass.utcfromtimestamp(-2**32 - 1)
+ except (OSError, OverflowError):
+ self.skipTest("Test not valid on this platform")
min_dt = self.theclass.min.replace(tzinfo=timezone.utc)
min_ts = min_dt.timestamp()
@@ -2597,10 +2603,11 @@ class TestDateTime(TestDate):
("maximum", max_ts, max_dt.replace(tzinfo=None)),
]:
with self.subTest(test_name, ts=ts, expected=expected):
- try:
- actual = self.theclass.utcfromtimestamp(ts)
- except (OSError, OverflowError) as exc:
- self.skipTest(str(exc))
+ with self.assertWarns(DeprecationWarning):
+ try:
+ actual = self.theclass.utcfromtimestamp(ts)
+ except (OSError, OverflowError) as exc:
+ self.skipTest(str(exc))
self.assertEqual(actual, expected)
@@ -2645,7 +2652,8 @@ class TestDateTime(TestDate):
@unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
def test_negative_float_utcfromtimestamp(self):
- d = self.theclass.utcfromtimestamp(-1.05)
+ with self.assertWarns(DeprecationWarning):
+ d = self.theclass.utcfromtimestamp(-1.05)
self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
def test_utcnow(self):
@@ -2655,8 +2663,11 @@ class TestDateTime(TestDate):
# a second of each other.
tolerance = timedelta(seconds=1)
for dummy in range(3):
- from_now = self.theclass.utcnow()
- from_timestamp = self.theclass.utcfromtimestamp(time.time())
+ with self.assertWarns(DeprecationWarning):
+ from_now = self.theclass.utcnow()
+
+ with self.assertWarns(DeprecationWarning):
+ from_timestamp = self.theclass.utcfromtimestamp(time.time())
if abs(from_timestamp - from_now) <= tolerance:
break
# Else try again a few times.
@@ -2956,7 +2967,11 @@ class TestDateTime(TestDate):
constr_name=constr_name):
constructor = getattr(base_obj, constr_name)
- dt = constructor(*constr_args)
+ if constr_name == "utcfromtimestamp":
+ with self.assertWarns(DeprecationWarning):
+ dt = constructor(*constr_args)
+ else:
+ dt = constructor(*constr_args)
# Test that it creates the right subclass
self.assertIsInstance(dt, DateTimeSubclass)
@@ -2986,7 +3001,11 @@ class TestDateTime(TestDate):
for name, meth_name, kwargs in test_cases:
with self.subTest(name):
constr = getattr(DateTimeSubclass, meth_name)
- dt = constr(**kwargs)
+ if constr == "utcnow":
+ with self.assertWarns(DeprecationWarning):
+ dt = constr(**kwargs)
+ else:
+ dt = constr(**kwargs)
self.assertIsInstance(dt, DateTimeSubclass)
self.assertEqual(dt.extra, 7)
@@ -4642,7 +4661,8 @@ class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
for dummy in range(3):
now = datetime.now(weirdtz)
self.assertIs(now.tzinfo, weirdtz)
- utcnow = datetime.utcnow().replace(tzinfo=utc)
+ with self.assertWarns(DeprecationWarning):
+ utcnow = datetime.utcnow().replace(tzinfo=utc)
now2 = utcnow.astimezone(weirdtz)
if abs(now - now2) < timedelta(seconds=30):
break
@@ -4676,7 +4696,8 @@ class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
# Try to make sure tz= actually does some conversion.
timestamp = 1000000000
- utcdatetime = datetime.utcfromtimestamp(timestamp)
+ with self.assertWarns(DeprecationWarning):
+ utcdatetime = datetime.utcfromtimestamp(timestamp)
# In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
# But on some flavor of Mac, it's nowhere near that. So we can't have
# any idea here what time that actually is, we can only test that
@@ -4690,7 +4711,8 @@ class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
def test_tzinfo_utcnow(self):
meth = self.theclass.utcnow
# Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
- base = meth()
+ with self.assertWarns(DeprecationWarning):
+ base = meth()
# Try with and without naming the keyword; for whatever reason,
# utcnow() doesn't accept a tzinfo argument.
off42 = FixedOffset(42, "42")
@@ -4702,7 +4724,8 @@ class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
meth = self.theclass.utcfromtimestamp
ts = time.time()
# Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
- base = meth(ts)
+ with self.assertWarns(DeprecationWarning):
+ base = meth(ts)
# Try with and without naming the keyword; for whatever reason,
# utcfromtimestamp() doesn't accept a tzinfo argument.
off42 = FixedOffset(42, "42")
@@ -5309,7 +5332,7 @@ class TestTimezoneConversions(unittest.TestCase):
def test_fromutc(self):
self.assertRaises(TypeError, Eastern.fromutc) # not enough args
- now = datetime.utcnow().replace(tzinfo=utc_real)
+ now = datetime.now(tz=utc_real)
self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
now = now.replace(tzinfo=Eastern) # insert correct tzinfo
enow = Eastern.fromutc(now) # doesn't blow up
@@ -5411,9 +5434,11 @@ class Oddballs(unittest.TestCase):
self.assertEqual(datetime_sc, as_datetime)
def test_extra_attributes(self):
+ with self.assertWarns(DeprecationWarning):
+ utcnow = datetime.utcnow()
for x in [date.today(),
time(),
- datetime.utcnow(),
+ utcnow,
timedelta(),
tzinfo(),
timezone(timedelta())]:
@@ -6073,6 +6098,7 @@ class ZoneInfo(tzinfo):
def transitions(self):
for (_, prev_ti), (t, ti) in pairs(zip(self.ut, self.ti)):
shift = ti[0] - prev_ti[0]
+ # TODO: Remove this use of utcfromtimestamp
yield datetime.utcfromtimestamp(t), shift
def nondst_folds(self):
diff --git a/Lib/test/support/testresult.py b/Lib/test/support/testresult.py
index 2cd1366..14474be 100644
--- a/Lib/test/support/testresult.py
+++ b/Lib/test/support/testresult.py
@@ -18,10 +18,13 @@ class RegressionTestResult(unittest.TextTestResult):
self.buffer = True
if self.USE_XML:
from xml.etree import ElementTree as ET
- from datetime import datetime
+ from datetime import datetime, UTC
self.__ET = ET
self.__suite = ET.Element('testsuite')
- self.__suite.set('start', datetime.utcnow().isoformat(' '))
+ self.__suite.set('start',
+ datetime.now(UTC)
+ .replace(tzinfo=None)
+ .isoformat(' '))
self.__e = None
self.__start_time = None
diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py
index 6b45744..b08abab 100644
--- a/Lib/test/test_plistlib.py
+++ b/Lib/test/test_plistlib.py
@@ -925,7 +925,7 @@ class TestBinaryPlistlib(unittest.TestCase):
# Issue #26709: 32-bit timestamp out of range
for ts in -2**31-1, 2**31:
with self.subTest(ts=ts):
- d = (datetime.datetime.utcfromtimestamp(0) +
+ d = (datetime.datetime(1970, 1, 1, 0, 0) +
datetime.timedelta(seconds=ts))
data = plistlib.dumps(d, fmt=plistlib.FMT_BINARY)
self.assertEqual(plistlib.loads(data), d)
diff --git a/Lib/test/test_sqlite3/test_types.py b/Lib/test/test_sqlite3/test_types.py
index 5e0ff35..fde5f88 100644
--- a/Lib/test/test_sqlite3/test_types.py
+++ b/Lib/test/test_sqlite3/test_types.py
@@ -517,7 +517,7 @@ class DateTimeTests(unittest.TestCase):
self.assertEqual(ts, ts2)
def test_sql_timestamp(self):
- now = datetime.datetime.utcnow()
+ now = datetime.datetime.now(tz=datetime.UTC)
self.cur.execute("insert into test(ts) values (current_timestamp)")
self.cur.execute("select ts from test")
with self.assertWarnsRegex(DeprecationWarning, "converter"):
diff --git a/Misc/NEWS.d/next/Library/2023-04-25-17-03-18.gh-issue-103857.Mr2Cak.rst b/Misc/NEWS.d/next/Library/2023-04-25-17-03-18.gh-issue-103857.Mr2Cak.rst
new file mode 100644
index 0000000..3bd370d
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-04-25-17-03-18.gh-issue-103857.Mr2Cak.rst
@@ -0,0 +1,2 @@
+Deprecated :meth:`datetime.datetime.utcnow` and
+:meth:`datetime.datetime.utcfromtimestamp`. (Patch by Paul Ganssle)
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
index f317dc1..d392d38 100644
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -5144,6 +5144,13 @@ datetime_datetime_now_impl(PyTypeObject *type, PyObject *tz)
static PyObject *
datetime_utcnow(PyObject *cls, PyObject *dummy)
{
+ PyErr_WarnEx(
+ PyExc_DeprecationWarning,
+ "datetime.utcnow() is deprecated and scheduled for removal in a future "
+ "version. Use timezone-aware objects to represent datetimes in UTC: "
+ "datetime.now(datetime.UTC).",
+ 2
+ );
return datetime_best_possible(cls, _PyTime_gmtime, Py_None);
}
@@ -5180,6 +5187,13 @@ datetime_fromtimestamp(PyObject *cls, PyObject *args, PyObject *kw)
static PyObject *
datetime_utcfromtimestamp(PyObject *cls, PyObject *args)
{
+ PyErr_WarnEx(
+ PyExc_DeprecationWarning,
+ "datetime.utcfromtimestamp() is deprecated and scheduled for removal "
+ "in a future version. Use timezone-aware objects to represent "
+ "datetimes in UTC: datetime.now(datetime.UTC).",
+ 2
+ );
PyObject *timestamp;
PyObject *result = NULL;