Fixed #32366 -- Updated datetime module usage to recommended approach.

- Replaced datetime.utcnow() with datetime.now().
- Replaced datetime.utcfromtimestamp() with datetime.fromtimestamp().
- Replaced datetime.utctimetuple() with datetime.timetuple().
- Replaced calendar.timegm() and datetime.utctimetuple() with datetime.timestamp().
This commit is contained in:
Nick Pope 2021-05-07 10:42:59 +01:00 committed by Carlton Gibson
parent 69ffb1acf3
commit d06c5b3581
17 changed files with 75 additions and 77 deletions

View File

@ -59,10 +59,8 @@ class SessionStore(SessionBase):
Return the modification time of the file storing the session's content.
"""
modification = os.stat(self._key_to_file()).st_mtime
if settings.USE_TZ:
modification = datetime.datetime.utcfromtimestamp(modification)
return modification.replace(tzinfo=timezone.utc)
return datetime.datetime.fromtimestamp(modification)
tz = timezone.utc if settings.USE_TZ else None
return datetime.datetime.fromtimestamp(modification, tz=tz)
def _expiry_date(self, session_data):
"""

View File

@ -1,5 +1,4 @@
import datetime
from calendar import timegm
from functools import wraps
from django.contrib.sites.shortcuts import get_current_site
@ -7,6 +6,7 @@ from django.core.paginator import EmptyPage, PageNotAnInteger
from django.http import Http404
from django.template.response import TemplateResponse
from django.urls import reverse
from django.utils import timezone
from django.utils.http import http_date
@ -72,10 +72,10 @@ def sitemap(request, sitemaps, section=None,
if all_sites_lastmod:
site_lastmod = getattr(site, 'latest_lastmod', None)
if site_lastmod is not None:
site_lastmod = (
site_lastmod.utctimetuple() if isinstance(site_lastmod, datetime.datetime)
else site_lastmod.timetuple()
)
if not isinstance(site_lastmod, datetime.datetime):
site_lastmod = datetime.datetime.combine(site_lastmod, datetime.time.min)
if timezone.is_naive(site_lastmod):
site_lastmod = timezone.make_aware(site_lastmod, timezone.utc)
lastmod = site_lastmod if lastmod is None else max(lastmod, site_lastmod)
else:
all_sites_lastmod = False
@ -88,5 +88,5 @@ def sitemap(request, sitemaps, section=None,
if all_sites_lastmod and lastmod is not None:
# if lastmod is defined for all sites, set header so as
# ConditionalGetMiddleware is able to send 304 NOT MODIFIED
response.headers['Last-Modified'] = http_date(timegm(lastmod))
response.headers['Last-Modified'] = http_date(lastmod.timestamp())
return response

View File

@ -1,5 +1,3 @@
from calendar import timegm
from django.contrib.sites.shortcuts import get_current_site
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
from django.http import Http404, HttpResponse
@ -42,8 +40,7 @@ class Feed:
if hasattr(self, 'item_pubdate') or hasattr(self, 'item_updateddate'):
# if item_pubdate or item_updateddate is defined for the feed, set
# header so as ConditionalGetMiddleware is able to send 304 NOT MODIFIED
response.headers['Last-Modified'] = http_date(
timegm(feedgen.latest_post_date().utctimetuple()))
response.headers['Last-Modified'] = http_date(feedgen.latest_post_date().timestamp())
feedgen.write(response, 'utf-8')
return response

View File

@ -123,10 +123,9 @@ class DatabaseCache(BaseDatabaseCache):
now = now.replace(microsecond=0)
if timeout is None:
exp = datetime.max
elif settings.USE_TZ:
exp = datetime.utcfromtimestamp(timeout)
else:
exp = datetime.fromtimestamp(timeout)
tz = timezone.utc if settings.USE_TZ else None
exp = datetime.fromtimestamp(timeout, tz=tz)
exp = exp.replace(microsecond=0)
if num > self._max_entries:
self._cull(db, cursor, now)
@ -235,11 +234,7 @@ class DatabaseCache(BaseDatabaseCache):
connection = connections[db]
quote_name = connection.ops.quote_name
if settings.USE_TZ:
now = datetime.utcnow()
else:
now = datetime.now()
now = now.replace(microsecond=0)
now = timezone.now().replace(microsecond=0, tzinfo=None)
with connection.cursor() as cursor:
cursor.execute(

View File

@ -347,11 +347,8 @@ class FileSystemStorage(Storage):
If timezone support is enabled, make an aware datetime object in UTC;
otherwise make a naive one in the local timezone.
"""
if settings.USE_TZ:
# Safe to use .replace() because UTC doesn't have DST
return datetime.utcfromtimestamp(ts).replace(tzinfo=timezone.utc)
else:
return datetime.fromtimestamp(ts)
tz = timezone.utc if settings.USE_TZ else None
return datetime.fromtimestamp(ts, tz=tz)
def get_accessed_time(self, name):
return self._datetime_from_timestamp(os.path.getatime(self.path(name)))

View File

@ -203,9 +203,9 @@ class HttpResponseBase:
self.cookies[key] = value
if expires is not None:
if isinstance(expires, datetime.datetime):
if timezone.is_aware(expires):
expires = timezone.make_naive(expires, timezone.utc)
delta = expires - expires.utcnow()
if timezone.is_naive(expires):
expires = timezone.make_aware(expires, timezone.utc)
delta = expires - datetime.datetime.now(tz=timezone.utc)
# Add one second so the date matches exactly (a fraction of
# time gets lost between converting to a timedelta and
# then the date string).

View File

@ -12,7 +12,6 @@ Usage:
"""
import calendar
import datetime
import time
from email.utils import format_datetime as format_datetime_rfc5322
from django.utils.dates import (
@ -20,7 +19,7 @@ from django.utils.dates import (
)
from django.utils.regex_helper import _lazy_re_compile
from django.utils.timezone import (
_datetime_ambiguous_or_imaginary, get_default_timezone, is_aware, is_naive,
_datetime_ambiguous_or_imaginary, get_default_timezone, is_naive,
make_aware,
)
from django.utils.translation import gettext as _
@ -295,10 +294,10 @@ class DateFormat(TimeFormat):
def U(self):
"Seconds since the Unix epoch (January 1 1970 00:00:00 GMT)"
if isinstance(self.data, datetime.datetime) and is_aware(self.data):
return int(calendar.timegm(self.data.utctimetuple()))
else:
return int(time.mktime(self.data.timetuple()))
value = self.data
if not isinstance(value, datetime.datetime):
value = datetime.datetime.combine(value, datetime.time.min)
return int(value.timestamp())
def w(self):
"Day of the week, numeric, i.e. '0' (Sunday) to '6' (Saturday)"

View File

@ -172,8 +172,7 @@ class SyndicationFeed:
if latest_date is None or item_date > latest_date:
latest_date = item_date
# datetime.now(tz=utc) is slower, as documented in django.utils.timezone.now
return latest_date or datetime.datetime.utcnow().replace(tzinfo=utc)
return latest_date or datetime.datetime.now(tz=utc)
class Enclosure:

View File

@ -1,5 +1,4 @@
import base64
import calendar
import datetime
import re
import unicodedata
@ -112,9 +111,10 @@ def parse_http_date(date):
else:
raise ValueError("%r is not in a valid HTTP date format" % date)
try:
tz = datetime.timezone.utc
year = int(m['year'])
if year < 100:
current_year = datetime.datetime.utcnow().year
current_year = datetime.datetime.now(tz=tz).year
current_century = current_year - (current_year % 100)
if year - (current_year % 100) > 50:
# year that appears to be more than 50 years in the future are
@ -127,8 +127,8 @@ def parse_http_date(date):
hour = int(m['hour'])
min = int(m['min'])
sec = int(m['sec'])
result = datetime.datetime(year, month, day, hour, min, sec)
return calendar.timegm(result.utctimetuple())
result = datetime.datetime(year, month, day, hour, min, sec, tzinfo=tz)
return int(result.timestamp())
except Exception as exc:
raise ValueError("%r is not a valid date" % date) from exc

View File

@ -194,11 +194,7 @@ def now():
"""
Return an aware or naive datetime.datetime, depending on settings.USE_TZ.
"""
if settings.USE_TZ:
# timeit shows that datetime.now(tz=utc) is 24% slower
return datetime.utcnow().replace(tzinfo=utc)
else:
return datetime.now()
return datetime.now(tz=utc if settings.USE_TZ else None)
# By design, these four functions don't perform any checks on their arguments.

View File

@ -89,8 +89,9 @@ def get_git_changeset():
shell=True, cwd=repo_dir, universal_newlines=True,
)
timestamp = git_log.stdout
tz = datetime.timezone.utc
try:
timestamp = datetime.datetime.utcfromtimestamp(int(timestamp))
timestamp = datetime.datetime.fromtimestamp(int(timestamp), tz=tz)
except ValueError:
return None
return timestamp.strftime('%Y%m%d%H%M%S')

View File

@ -2,11 +2,11 @@
Decorators for views based on HTTP headers.
"""
from calendar import timegm
from functools import wraps
from django.http import HttpResponseNotAllowed
from django.middleware.http import ConditionalGetMiddleware
from django.utils import timezone
from django.utils.cache import get_conditional_response
from django.utils.decorators import decorator_from_middleware
from django.utils.http import http_date, quote_etag
@ -82,7 +82,9 @@ def condition(etag_func=None, last_modified_func=None):
if last_modified_func:
dt = last_modified_func(request, *args, **kwargs)
if dt:
return timegm(dt.utctimetuple())
if not timezone.is_aware(dt):
dt = timezone.make_aware(dt, timezone.utc)
return int(dt.timestamp())
# The value from etag_func() could be quoted or unquoted.
res_etag = etag_func(request, *args, **kwargs) if etag_func else None

View File

@ -92,7 +92,7 @@ class MultiColumnFKTests(TestCase):
def test_reverse_query_filters_correctly(self):
timemark = datetime.datetime.utcnow()
timemark = datetime.datetime.now(tz=datetime.timezone.utc).replace(tzinfo=None)
timedelta = datetime.timedelta(days=1)
# Creating a to valid memberships

View File

@ -479,8 +479,8 @@ class WriterTests(SimpleTestCase):
self.serialize_round_trip(models.SET(42))
def test_serialize_datetime(self):
self.assertSerializedEqual(datetime.datetime.utcnow())
self.assertSerializedEqual(datetime.datetime.utcnow)
self.assertSerializedEqual(datetime.datetime.now())
self.assertSerializedEqual(datetime.datetime.now)
self.assertSerializedEqual(datetime.datetime.today())
self.assertSerializedEqual(datetime.datetime.today)
self.assertSerializedEqual(datetime.date.today())
@ -662,8 +662,8 @@ class WriterTests(SimpleTestCase):
Tests serializing a simple migration.
"""
fields = {
'charfield': models.DateTimeField(default=datetime.datetime.utcnow),
'datetimefield': models.DateTimeField(default=datetime.datetime.utcnow),
'charfield': models.DateTimeField(default=datetime.datetime.now),
'datetimefield': models.DateTimeField(default=datetime.datetime.now),
}
options = {

View File

@ -19,7 +19,7 @@ class SetCookieTests(SimpleTestCase):
# evaluated expiration time and the time evaluated in set_cookie(). If
# this difference doesn't exist, the cookie time will be 1 second
# larger. The sleep guarantees that there will be a time difference.
expires = datetime.utcnow() + timedelta(seconds=10)
expires = datetime.now(tz=utc).replace(tzinfo=None) + timedelta(seconds=10)
time.sleep(0.001)
response.set_cookie('datetime', expires=expires)
datetime_cookie = response.cookies['datetime']
@ -28,7 +28,7 @@ class SetCookieTests(SimpleTestCase):
def test_aware_expiration(self):
"""set_cookie() accepts an aware datetime as expiration time."""
response = HttpResponse()
expires = (datetime.utcnow() + timedelta(seconds=10)).replace(tzinfo=utc)
expires = datetime.now(tz=utc) + timedelta(seconds=10)
time.sleep(0.001)
response.set_cookie('datetime', expires=expires)
datetime_cookie = response.cookies['datetime']

View File

@ -54,8 +54,8 @@ class DateFormatTests(SimpleTestCase):
self.assertEqual(datetime.fromtimestamp(int(format(dt, 'U')), ltz), dt)
# astimezone() is safe here because the target timezone doesn't have DST
self.assertEqual(datetime.fromtimestamp(int(format(dt, 'U'))), dt.astimezone(ltz).replace(tzinfo=None))
self.assertEqual(datetime.fromtimestamp(int(format(dt, 'U')), tz).utctimetuple(), dt.utctimetuple())
self.assertEqual(datetime.fromtimestamp(int(format(dt, 'U')), ltz).utctimetuple(), dt.utctimetuple())
self.assertEqual(datetime.fromtimestamp(int(format(dt, 'U')), tz).timetuple(), dt.astimezone(tz).timetuple())
self.assertEqual(datetime.fromtimestamp(int(format(dt, 'U')), ltz).timetuple(), dt.astimezone(ltz).timetuple())
def test_epoch(self):
udt = datetime(1970, 1, 1, tzinfo=utc)

View File

@ -1,6 +1,6 @@
import platform
import unittest
from datetime import datetime
from datetime import datetime, timezone
from unittest import mock
from django.test import SimpleTestCase
@ -288,38 +288,52 @@ class HttpDateProcessingTests(unittest.TestCase):
def test_parsing_rfc1123(self):
parsed = parse_http_date('Sun, 06 Nov 1994 08:49:37 GMT')
self.assertEqual(datetime.utcfromtimestamp(parsed), datetime(1994, 11, 6, 8, 49, 37))
self.assertEqual(
datetime.fromtimestamp(parsed, timezone.utc),
datetime(1994, 11, 6, 8, 49, 37, tzinfo=timezone.utc),
)
@unittest.skipIf(platform.architecture()[0] == '32bit', 'The Year 2038 problem.')
@mock.patch('django.utils.http.datetime.datetime')
def test_parsing_rfc850(self, mocked_datetime):
mocked_datetime.side_effect = datetime
mocked_datetime.utcnow = mock.Mock()
utcnow_1 = datetime(2019, 11, 6, 8, 49, 37)
utcnow_2 = datetime(2020, 11, 6, 8, 49, 37)
utcnow_3 = datetime(2048, 11, 6, 8, 49, 37)
mocked_datetime.now = mock.Mock()
now_1 = datetime(2019, 11, 6, 8, 49, 37, tzinfo=timezone.utc)
now_2 = datetime(2020, 11, 6, 8, 49, 37, tzinfo=timezone.utc)
now_3 = datetime(2048, 11, 6, 8, 49, 37, tzinfo=timezone.utc)
tests = (
(utcnow_1, 'Tuesday, 31-Dec-69 08:49:37 GMT', datetime(2069, 12, 31, 8, 49, 37)),
(utcnow_1, 'Tuesday, 10-Nov-70 08:49:37 GMT', datetime(1970, 11, 10, 8, 49, 37)),
(utcnow_1, 'Sunday, 06-Nov-94 08:49:37 GMT', datetime(1994, 11, 6, 8, 49, 37)),
(utcnow_2, 'Wednesday, 31-Dec-70 08:49:37 GMT', datetime(2070, 12, 31, 8, 49, 37)),
(utcnow_2, 'Friday, 31-Dec-71 08:49:37 GMT', datetime(1971, 12, 31, 8, 49, 37)),
(utcnow_3, 'Sunday, 31-Dec-00 08:49:37 GMT', datetime(2000, 12, 31, 8, 49, 37)),
(utcnow_3, 'Friday, 31-Dec-99 08:49:37 GMT', datetime(1999, 12, 31, 8, 49, 37)),
(now_1, 'Tuesday, 31-Dec-69 08:49:37 GMT', datetime(2069, 12, 31, 8, 49, 37, tzinfo=timezone.utc)),
(now_1, 'Tuesday, 10-Nov-70 08:49:37 GMT', datetime(1970, 11, 10, 8, 49, 37, tzinfo=timezone.utc)),
(now_1, 'Sunday, 06-Nov-94 08:49:37 GMT', datetime(1994, 11, 6, 8, 49, 37, tzinfo=timezone.utc)),
(now_2, 'Wednesday, 31-Dec-70 08:49:37 GMT', datetime(2070, 12, 31, 8, 49, 37, tzinfo=timezone.utc)),
(now_2, 'Friday, 31-Dec-71 08:49:37 GMT', datetime(1971, 12, 31, 8, 49, 37, tzinfo=timezone.utc)),
(now_3, 'Sunday, 31-Dec-00 08:49:37 GMT', datetime(2000, 12, 31, 8, 49, 37, tzinfo=timezone.utc)),
(now_3, 'Friday, 31-Dec-99 08:49:37 GMT', datetime(1999, 12, 31, 8, 49, 37, tzinfo=timezone.utc)),
)
for utcnow, rfc850str, expected_date in tests:
for now, rfc850str, expected_date in tests:
with self.subTest(rfc850str=rfc850str):
mocked_datetime.utcnow.return_value = utcnow
mocked_datetime.now.return_value = now
parsed = parse_http_date(rfc850str)
self.assertEqual(datetime.utcfromtimestamp(parsed), expected_date)
mocked_datetime.now.assert_called_once_with(tz=timezone.utc)
self.assertEqual(
datetime.fromtimestamp(parsed, timezone.utc),
expected_date,
)
mocked_datetime.reset_mock()
def test_parsing_asctime(self):
parsed = parse_http_date('Sun Nov 6 08:49:37 1994')
self.assertEqual(datetime.utcfromtimestamp(parsed), datetime(1994, 11, 6, 8, 49, 37))
self.assertEqual(
datetime.fromtimestamp(parsed, timezone.utc),
datetime(1994, 11, 6, 8, 49, 37, tzinfo=timezone.utc),
)
def test_parsing_year_less_than_70(self):
parsed = parse_http_date('Sun Nov 6 08:49:37 0037')
self.assertEqual(datetime.utcfromtimestamp(parsed), datetime(2037, 11, 6, 8, 49, 37))
self.assertEqual(
datetime.fromtimestamp(parsed, timezone.utc),
datetime(2037, 11, 6, 8, 49, 37, tzinfo=timezone.utc),
)
class EscapeLeadingSlashesTests(unittest.TestCase):