Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-103857: Deprecate utcnow and utcfromtimestamp #103858

Merged
merged 5 commits into from
Apr 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Doc/library/datetime.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)

Expand Down
16 changes: 15 additions & 1 deletion Lib/datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -1801,6 +1801,13 @@ def fromtimestamp(cls, timestamp, tz=None):
@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
Expand All @@ -1812,8 +1819,15 @@ def now(cls, tz=None):
@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):
Expand Down
8 changes: 4 additions & 4 deletions Lib/email/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
8 changes: 4 additions & 4 deletions Lib/http/cookiejar.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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)
Expand Down
68 changes: 47 additions & 21 deletions Lib/test/datetimetester.py
Original file line number Diff line number Diff line change
Expand Up @@ -2437,7 +2437,8 @@ def test_utcfromtimestamp(self):

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
Expand Down Expand Up @@ -2483,8 +2484,12 @@ def test_timestamp_aware(self):

@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)
Expand Down Expand Up @@ -2581,10 +2586,11 @@ def test_fromtimestamp_limits(self):
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()
Expand All @@ -2597,10 +2603,11 @@ def test_utcfromtimestamp_limits(self):
("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)

Expand Down Expand Up @@ -2645,7 +2652,8 @@ def test_negative_float_fromtimestamp(self):

@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):
Expand All @@ -2655,8 +2663,11 @@ def test_utcnow(self):
# 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.
Expand Down Expand Up @@ -2956,7 +2967,11 @@ def __new__(cls, *args, **kwargs):
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)
Expand Down Expand Up @@ -2986,7 +3001,11 @@ def __new__(cls, *args, **kwargs):
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)
Expand Down Expand Up @@ -4642,7 +4661,8 @@ def test_tzinfo_now(self):
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
Expand Down Expand Up @@ -4676,7 +4696,8 @@ def test_tzinfo_fromtimestamp(self):

# 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
Expand All @@ -4690,7 +4711,8 @@ def test_tzinfo_fromtimestamp(self):
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")
Expand All @@ -4702,7 +4724,8 @@ def test_tzinfo_utcfromtimestamp(self):
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")
Expand Down Expand Up @@ -5309,7 +5332,7 @@ def dst(self, dt):

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
Expand Down Expand Up @@ -5411,9 +5434,11 @@ def test_bug_1028306(self):
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())]:
Expand Down Expand Up @@ -6073,6 +6098,7 @@ def stats(cls, start_year=1):
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):
Expand Down
7 changes: 5 additions & 2 deletions Lib/test/support/testresult.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ def __init__(self, stream, descriptions, verbosity):
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

Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_plistlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,7 @@ def test_large_timestamp(self):
# 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)
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_sqlite3/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@ def test_sqlite_timestamp(self):
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"):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Deprecated :meth:`datetime.datetime.utcnow` and
:meth:`datetime.datetime.utcfromtimestamp`. (Patch by Paul Ganssle)
14 changes: 14 additions & 0 deletions Modules/_datetimemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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;

Expand Down