diff --git a/django/utils/timezone.py b/django/utils/timezone.py index c1f0d70bc1..25767e6047 100644 --- a/django/utils/timezone.py +++ b/django/utils/timezone.py @@ -3,13 +3,15 @@ Timezone-related classes and functions. """ import functools +import warnings from contextlib import ContextDecorator -from datetime import datetime, timedelta, tzinfo +from datetime import datetime, timedelta, timezone, tzinfo from threading import local import pytz from django.conf import settings +from django.utils.deprecation import RemovedInDjango31Warning __all__ = [ 'utc', 'get_fixed_timezone', @@ -36,6 +38,10 @@ class FixedOffset(tzinfo): """ def __init__(self, offset=None, name=None): + warnings.warn( + 'FixedOffset is deprecated in favor of datetime.timezone', + RemovedInDjango31Warning, stacklevel=2, + ) if offset is not None: self.__offset = timedelta(minutes=offset) if name is not None: @@ -62,7 +68,7 @@ def get_fixed_timezone(offset): sign = '-' if offset < 0 else '+' hhmm = '%02d%02d' % divmod(abs(offset), 60) name = sign + hhmm - return FixedOffset(offset, name) + return timezone(timedelta(minutes=offset), name) # In order to avoid accessing settings at compile time, diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 1cc4a2486c..9514153e94 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -15,6 +15,8 @@ about each item can often be found in the release notes of two versions prior. See the :ref:`Django 2.2 release notes ` for more details on these changes. +* ``django.utils.timezone.FixedOffset`` will be removed. + .. _deprecation-removed-in-3.0: 3.0 diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index 51530ab845..a0bdf424ad 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -125,8 +125,12 @@ The functions defined in this module share the following properties: Parses a string and returns a :class:`datetime.datetime`. UTC offsets are supported; if ``value`` describes one, the result's - ``tzinfo`` attribute is a :class:`~django.utils.timezone.FixedOffset` - instance. + ``tzinfo`` attribute is a :class:`datetime.timezone` instance. + + .. versionchanged:: 2.2 + + In older versions, the ``tzinfo`` attribute is a + :class:`~django.utils.timezone.FixedOffset` instance. .. function:: parse_duration(value) @@ -856,6 +860,10 @@ appropriate entities. A :class:`~datetime.tzinfo` subclass modeling a fixed offset from UTC. ``offset`` is an integer number of minutes east of UTC. + .. deprecated:: 2.2 + + Use :class:`datetime.timezone` instead. + .. function:: get_fixed_timezone(offset) Returns a :class:`~datetime.tzinfo` instance that represents a time zone diff --git a/docs/releases/2.2.txt b/docs/releases/2.2.txt index dffb5bd12b..0fdd57e340 100644 --- a/docs/releases/2.2.txt +++ b/docs/releases/2.2.txt @@ -239,4 +239,5 @@ Features deprecated in 2.2 Miscellaneous ------------- -* ... +* ``django.utils.timezone.FixedOffset`` is deprecated in favor of + :class:`datetime.timezone`. diff --git a/tests/migrations/test_writer.py b/tests/migrations/test_writer.py index f3012181fa..946306c325 100644 --- a/tests/migrations/test_writer.py +++ b/tests/migrations/test_writer.py @@ -22,7 +22,7 @@ from django.test import SimpleTestCase from django.utils import datetime_safe from django.utils.deconstruct import deconstructible from django.utils.functional import SimpleLazyObject -from django.utils.timezone import FixedOffset, get_default_timezone, utc +from django.utils.timezone import get_default_timezone, get_fixed_timezone, utc from django.utils.translation import gettext_lazy as _ from django.utils.version import PY36 @@ -351,7 +351,7 @@ class WriterTests(SimpleTestCase): self.assertSerializedEqual(datetime.date.today) self.assertSerializedEqual(datetime.datetime.now().time()) self.assertSerializedEqual(datetime.datetime(2014, 1, 1, 1, 1, tzinfo=get_default_timezone())) - self.assertSerializedEqual(datetime.datetime(2013, 12, 31, 22, 1, tzinfo=FixedOffset(180))) + self.assertSerializedEqual(datetime.datetime(2013, 12, 31, 22, 1, tzinfo=get_fixed_timezone(180))) self.assertSerializedResultEqual( datetime.datetime(2014, 1, 1, 1, 1), ("datetime.datetime(2014, 1, 1, 1, 1)", {'import datetime'}) diff --git a/tests/timezones/tests.py b/tests/timezones/tests.py index 3f7f70c7fb..8b9069f7d6 100644 --- a/tests/timezones/tests.py +++ b/tests/timezones/tests.py @@ -35,7 +35,8 @@ from .models import ( # These tests use the EAT (Eastern Africa Time) and ICT (Indochina Time) # who don't have Daylight Saving Time, so we can represent them easily -# with FixedOffset, and use them directly as tzinfo in the constructors. +# with fixed offset timezones and use them directly as tzinfo in the +# constructors. # settings.TIME_ZONE is forced to EAT. Most tests use a variant of # datetime.datetime(2011, 9, 1, 13, 20, 30), which translates to diff --git a/tests/utils_tests/test_timezone.py b/tests/utils_tests/test_timezone.py index 5069b82e5b..11c754f1e7 100644 --- a/tests/utils_tests/test_timezone.py +++ b/tests/utils_tests/test_timezone.py @@ -4,8 +4,9 @@ from unittest import mock import pytz -from django.test import SimpleTestCase, override_settings +from django.test import SimpleTestCase, ignore_warnings, override_settings from django.utils import timezone +from django.utils.deprecation import RemovedInDjango31Warning CET = pytz.timezone("Europe/Paris") EAT = timezone.get_fixed_timezone(180) # Africa/Nairobi @@ -97,7 +98,7 @@ class TimezoneTests(SimpleTestCase): self.assertEqual(timezone.get_current_timezone_name(), 'Asia/Bangkok') def test_override_fixed_offset(self): - with timezone.override(timezone.FixedOffset(0, 'tzname')): + with timezone.override(datetime.timezone(datetime.timedelta(), 'tzname')): self.assertEqual(timezone.get_current_timezone_name(), 'tzname') def test_activate_invalid_timezone(self): @@ -196,11 +197,18 @@ class TimezoneTests(SimpleTestCase): def test_fixedoffset_timedelta(self): delta = datetime.timedelta(hours=1) - self.assertEqual(timezone.get_fixed_timezone(delta).utcoffset(''), delta) + self.assertEqual(timezone.get_fixed_timezone(delta).utcoffset(None), delta) def test_fixedoffset_negative_timedelta(self): delta = datetime.timedelta(hours=-2) - self.assertEqual(timezone.get_fixed_timezone(delta).utcoffset(''), delta) + self.assertEqual(timezone.get_fixed_timezone(delta).utcoffset(None), delta) + @ignore_warnings(category=RemovedInDjango31Warning) def test_fixedoffset_pickle(self): self.assertEqual(pickle.loads(pickle.dumps(timezone.FixedOffset(0, 'tzname'))).tzname(''), 'tzname') + + def test_fixedoffset_deprecation(self): + msg = 'FixedOffset is deprecated in favor of datetime.timezone' + with self.assertWarnsMessage(RemovedInDjango31Warning, msg) as cm: + timezone.FixedOffset() + self.assertEqual(cm.filename, __file__)