summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2023-01-09 09:03:38 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2023-01-17 11:49:15 +0100
commite6f82438d4e3750e8d299bfd79dac98eebe9f1e0 (patch)
tree4ee0cbf2c0be9822416aa3d65105f35a9784fd94
parent8d98f99a4ab5de6f2c730399f53eba8bf6bea470 (diff)
Refs #32365 -- Removed support for pytz timezones per deprecation timeline.
-rw-r--r--django/conf/__init__.py11
-rw-r--r--django/conf/global_settings.py5
-rw-r--r--django/contrib/admin/templatetags/admin_list.py15
-rw-r--r--django/db/backends/base/base.py11
-rw-r--r--django/db/backends/sqlite3/_functions.py10
-rw-r--r--django/forms/utils.py4
-rw-r--r--django/templatetags/tz.py29
-rw-r--r--django/utils/timezone.py72
-rw-r--r--docs/ref/settings.txt15
-rw-r--r--docs/releases/4.0.txt4
-rw-r--r--docs/releases/5.0.txt4
-rw-r--r--docs/topics/i18n/timezones.txt2
-rw-r--r--tests/admin_views/tests.py8
-rw-r--r--tests/datetimes/tests.py49
-rw-r--r--tests/db_functions/datetime/test_extract_trunc.py498
-rw-r--r--tests/migrations/test_writer.py15
-rw-r--r--tests/requirements/py3.txt1
-rw-r--r--tests/settings_tests/tests.py27
-rw-r--r--tests/timezones/tests.py200
-rw-r--r--tests/utils_tests/test_dateformat.py3
-rw-r--r--tests/utils_tests/test_timezone.py110
21 files changed, 313 insertions, 780 deletions
diff --git a/django/conf/__init__.py b/django/conf/__init__.py
index 3d1d65c44d..ea63a0dfb2 100644
--- a/django/conf/__init__.py
+++ b/django/conf/__init__.py
@@ -24,12 +24,6 @@ DEFAULT_STORAGE_ALIAS = "default"
STATICFILES_STORAGE_ALIAS = "staticfiles"
# RemovedInDjango50Warning
-USE_DEPRECATED_PYTZ_DEPRECATED_MSG = (
- "The USE_DEPRECATED_PYTZ setting, and support for pytz timezones is "
- "deprecated in favor of the stdlib zoneinfo module. Please update your "
- "code to use zoneinfo and remove the USE_DEPRECATED_PYTZ setting."
-)
-
CSRF_COOKIE_MASKED_DEPRECATED_MSG = (
"The CSRF_COOKIE_MASKED transitional setting is deprecated. Support for "
"it will be removed in Django 5.0."
@@ -217,9 +211,6 @@ class Settings:
setattr(self, setting, setting_value)
self._explicit_settings.add(setting)
- if self.is_overridden("USE_DEPRECATED_PYTZ"):
- warnings.warn(USE_DEPRECATED_PYTZ_DEPRECATED_MSG, RemovedInDjango50Warning)
-
if self.is_overridden("CSRF_COOKIE_MASKED"):
warnings.warn(CSRF_COOKIE_MASKED_DEPRECATED_MSG, RemovedInDjango50Warning)
@@ -294,8 +285,6 @@ class UserSettingsHolder:
}
warnings.warn(STATICFILES_STORAGE_DEPRECATED_MSG, RemovedInDjango51Warning)
super().__setattr__(name, value)
- if name == "USE_DEPRECATED_PYTZ":
- warnings.warn(USE_DEPRECATED_PYTZ_DEPRECATED_MSG, RemovedInDjango50Warning)
# RemovedInDjango51Warning.
if name == "STORAGES":
self.STORAGES.setdefault(
diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py
index 411a5a4fe2..6d4ea3db5c 100644
--- a/django/conf/global_settings.py
+++ b/django/conf/global_settings.py
@@ -43,11 +43,6 @@ TIME_ZONE = "America/Chicago"
# If you set this to True, Django will use timezone-aware datetimes.
USE_TZ = True
-# RemovedInDjango50Warning: It's a transitional setting helpful in migrating
-# from pytz tzinfo to ZoneInfo(). Set True to continue using pytz tzinfo
-# objects during the Django 4.x release cycle.
-USE_DEPRECATED_PYTZ = False
-
# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = "en-us"
diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py
index 5865843dce..ee6f3a7666 100644
--- a/django/contrib/admin/templatetags/admin_list.py
+++ b/django/contrib/admin/templatetags/admin_list.py
@@ -1,6 +1,5 @@
import datetime
-from django.conf import settings
from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
from django.contrib.admin.utils import (
display_for_field,
@@ -357,10 +356,8 @@ def date_hierarchy(cl):
field = get_fields_from_path(cl.model, field_name)[-1]
if isinstance(field, models.DateTimeField):
dates_or_datetimes = "datetimes"
- qs_kwargs = {"is_dst": True} if settings.USE_DEPRECATED_PYTZ else {}
else:
dates_or_datetimes = "dates"
- qs_kwargs = {}
year_field = "%s__year" % field_name
month_field = "%s__month" % field_name
day_field = "%s__day" % field_name
@@ -401,9 +398,7 @@ def date_hierarchy(cl):
],
}
elif year_lookup and month_lookup:
- days = getattr(cl.queryset, dates_or_datetimes)(
- field_name, "day", **qs_kwargs
- )
+ days = getattr(cl.queryset, dates_or_datetimes)(field_name, "day")
return {
"show": True,
"back": {
@@ -425,9 +420,7 @@ def date_hierarchy(cl):
],
}
elif year_lookup:
- months = getattr(cl.queryset, dates_or_datetimes)(
- field_name, "month", **qs_kwargs
- )
+ months = getattr(cl.queryset, dates_or_datetimes)(field_name, "month")
return {
"show": True,
"back": {"link": link({}), "title": _("All dates")},
@@ -444,9 +437,7 @@ def date_hierarchy(cl):
],
}
else:
- years = getattr(cl.queryset, dates_or_datetimes)(
- field_name, "year", **qs_kwargs
- )
+ years = getattr(cl.queryset, dates_or_datetimes)(field_name, "year")
return {
"show": True,
"back": None,
diff --git a/django/db/backends/base/base.py b/django/db/backends/base/base.py
index 3b845ec9b3..5f2e7bcd4d 100644
--- a/django/db/backends/base/base.py
+++ b/django/db/backends/base/base.py
@@ -32,15 +32,6 @@ RAN_DB_VERSION_CHECK = set()
logger = logging.getLogger("django.db.backends.base")
-# RemovedInDjango50Warning
-def timezone_constructor(tzname):
- if settings.USE_DEPRECATED_PYTZ:
- import pytz
-
- return pytz.timezone(tzname)
- return zoneinfo.ZoneInfo(tzname)
-
-
class BaseDatabaseWrapper:
"""Represent a database connection."""
@@ -166,7 +157,7 @@ class BaseDatabaseWrapper:
elif self.settings_dict["TIME_ZONE"] is None:
return datetime.timezone.utc
else:
- return timezone_constructor(self.settings_dict["TIME_ZONE"])
+ return zoneinfo.ZoneInfo(self.settings_dict["TIME_ZONE"])
@cached_property
def timezone_name(self):
diff --git a/django/db/backends/sqlite3/_functions.py b/django/db/backends/sqlite3/_functions.py
index b590111ec5..c60549f8af 100644
--- a/django/db/backends/sqlite3/_functions.py
+++ b/django/db/backends/sqlite3/_functions.py
@@ -26,7 +26,6 @@ from math import (
)
from re import search as re_search
-from django.db.backends.base.base import timezone_constructor
from django.db.backends.utils import (
split_tzname_delta,
typecast_time,
@@ -36,6 +35,11 @@ from django.utils import timezone
from django.utils.crypto import md5
from django.utils.duration import duration_microseconds
+try:
+ import zoneinfo
+except ImportError:
+ from backports import zoneinfo
+
def register(connection):
create_deterministic_function = functools.partial(
@@ -111,14 +115,14 @@ def _sqlite_datetime_parse(dt, tzname=None, conn_tzname=None):
except (TypeError, ValueError):
return None
if conn_tzname:
- dt = dt.replace(tzinfo=timezone_constructor(conn_tzname))
+ dt = dt.replace(tzinfo=zoneinfo.ZoneInfo(conn_tzname))
if tzname is not None and tzname != conn_tzname:
tzname, sign, offset = split_tzname_delta(tzname)
if offset:
hours, minutes = offset.split(":")
offset_delta = timedelta(hours=int(hours), minutes=int(minutes))
dt += offset_delta if sign == "+" else -offset_delta
- dt = timezone.localtime(dt, timezone_constructor(tzname))
+ dt = timezone.localtime(dt, zoneinfo.ZoneInfo(tzname))
return dt
diff --git a/django/forms/utils.py b/django/forms/utils.py
index 905babce4d..7577c0bbda 100644
--- a/django/forms/utils.py
+++ b/django/forms/utils.py
@@ -215,9 +215,7 @@ def from_current_timezone(value):
if settings.USE_TZ and value is not None and timezone.is_naive(value):
current_timezone = timezone.get_current_timezone()
try:
- if not timezone._is_pytz_zone(
- current_timezone
- ) and timezone._datetime_ambiguous_or_imaginary(value, current_timezone):
+ if timezone._datetime_ambiguous_or_imaginary(value, current_timezone):
raise ValueError("Ambiguous or non-existent time.")
return timezone.make_aware(value, current_timezone)
except Exception as exc:
diff --git a/django/templatetags/tz.py b/django/templatetags/tz.py
index cb7d22e5f2..92240b2a39 100644
--- a/django/templatetags/tz.py
+++ b/django/templatetags/tz.py
@@ -7,34 +7,12 @@ try:
except ImportError:
from backports import zoneinfo
-from django.conf import settings
from django.template import Library, Node, TemplateSyntaxError
from django.utils import timezone
register = Library()
-# RemovedInDjango50Warning: shim to allow catching the exception in the calling
-# scope if pytz is not installed.
-class UnknownTimezoneException(BaseException):
- pass
-
-
-# RemovedInDjango50Warning
-def timezone_constructor(tzname):
- if settings.USE_DEPRECATED_PYTZ:
- import pytz
-
- try:
- return pytz.timezone(tzname)
- except pytz.UnknownTimeZoneError:
- raise UnknownTimezoneException
- try:
- return zoneinfo.ZoneInfo(tzname)
- except zoneinfo.ZoneInfoNotFoundError:
- raise UnknownTimezoneException
-
-
# HACK: datetime instances cannot be assigned new attributes. Define a subclass
# in order to define new attributes in do_timezone().
class datetimeobject(datetime):
@@ -79,8 +57,7 @@ def do_timezone(value, arg):
if timezone.is_naive(value):
default_timezone = timezone.get_default_timezone()
value = timezone.make_aware(value, default_timezone)
- # Filters must never raise exceptions, and pytz' exceptions inherit
- # Exception directly, not a specific subclass. So catch everything.
+ # Filters must never raise exceptionsm, so catch everything.
except Exception:
return ""
@@ -89,8 +66,8 @@ def do_timezone(value, arg):
tz = arg
elif isinstance(arg, str):
try:
- tz = timezone_constructor(arg)
- except UnknownTimezoneException:
+ tz = zoneinfo.ZoneInfo(arg)
+ except zoneinfo.ZoneInfoNotFoundError:
return ""
else:
return ""
diff --git a/django/utils/timezone.py b/django/utils/timezone.py
index f3eac0e7b2..73813fa20e 100644
--- a/django/utils/timezone.py
+++ b/django/utils/timezone.py
@@ -3,7 +3,6 @@ Timezone-related classes and functions.
"""
import functools
-import sys
import warnings
try:
@@ -75,10 +74,6 @@ def get_default_timezone():
This is the time zone defined by settings.TIME_ZONE.
"""
- if settings.USE_DEPRECATED_PYTZ:
- import pytz
-
- return pytz.timezone(settings.TIME_ZONE)
return zoneinfo.ZoneInfo(settings.TIME_ZONE)
@@ -125,12 +120,7 @@ def activate(timezone):
if isinstance(timezone, tzinfo):
_active.value = timezone
elif isinstance(timezone, str):
- if settings.USE_DEPRECATED_PYTZ:
- import pytz
-
- _active.value = pytz.timezone(timezone)
- else:
- _active.value = zoneinfo.ZoneInfo(timezone)
+ _active.value = zoneinfo.ZoneInfo(timezone)
else:
raise ValueError("Invalid timezone: %r" % timezone)
@@ -282,15 +272,11 @@ def make_aware(value, timezone=None, is_dst=NOT_PASSED):
)
if timezone is None:
timezone = get_current_timezone()
- if _is_pytz_zone(timezone):
- # This method is available for pytz time zones.
- return timezone.localize(value, is_dst=is_dst)
- else:
- # Check that we won't overwrite the timezone of an aware datetime.
- if is_aware(value):
- raise ValueError("make_aware expects a naive datetime, got %s" % value)
- # This may be wrong around DST changes!
- return value.replace(tzinfo=timezone)
+ # Check that we won't overwrite the timezone of an aware datetime.
+ if is_aware(value):
+ raise ValueError("make_aware expects a naive datetime, got %s" % value)
+ # This may be wrong around DST changes!
+ return value.replace(tzinfo=timezone)
def make_naive(value, timezone=None):
@@ -303,53 +289,7 @@ def make_naive(value, timezone=None):
return value.astimezone(timezone).replace(tzinfo=None)
-_PYTZ_IMPORTED = False
-
-
-def _pytz_imported():
- """
- Detects whether or not pytz has been imported without importing pytz.
-
- Copied from pytz_deprecation_shim with thanks to Paul Ganssle.
- """
- global _PYTZ_IMPORTED
-
- if not _PYTZ_IMPORTED and "pytz" in sys.modules:
- _PYTZ_IMPORTED = True
-
- return _PYTZ_IMPORTED
-
-
-def _is_pytz_zone(tz):
- """Checks if a zone is a pytz zone."""
- # See if pytz was already imported rather than checking
- # settings.USE_DEPRECATED_PYTZ to *allow* manually passing a pytz timezone,
- # which some of the test cases (at least) rely on.
- if not _pytz_imported():
- return False
-
- # If tz could be pytz, then pytz is needed here.
- import pytz
-
- _PYTZ_BASE_CLASSES = (pytz.tzinfo.BaseTzInfo, pytz._FixedOffset)
- # In releases prior to 2018.4, pytz.UTC was not a subclass of BaseTzInfo
- if not isinstance(pytz.UTC, pytz._FixedOffset):
- _PYTZ_BASE_CLASSES += (type(pytz.UTC),)
-
- return isinstance(tz, _PYTZ_BASE_CLASSES)
-
-
def _datetime_ambiguous_or_imaginary(dt, tz):
- if _is_pytz_zone(tz):
- import pytz
-
- try:
- tz.utcoffset(dt)
- except (pytz.AmbiguousTimeError, pytz.NonExistentTimeError):
- return True
- else:
- return False
-
return tz.utcoffset(dt.replace(fold=not dt.fold)) != tz.utcoffset(dt)
diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt
index f315e5ef6c..109cc887f7 100644
--- a/docs/ref/settings.txt
+++ b/docs/ref/settings.txt
@@ -2849,21 +2849,6 @@ the correct environment.
.. _list of time zones: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
-.. setting:: USE_DEPRECATED_PYTZ
-
-``USE_DEPRECATED_PYTZ``
------------------------
-
-Default: ``False``
-
-A boolean that specifies whether to use ``pytz``, rather than :mod:`zoneinfo`,
-as the default time zone implementation.
-
-.. deprecated:: 4.0
-
- This transitional setting is deprecated. Support for using ``pytz`` will be
- removed in Django 5.0.
-
.. setting:: USE_I18N
``USE_I18N``
diff --git a/docs/releases/4.0.txt b/docs/releases/4.0.txt
index d64e74941c..74dd4dbb93 100644
--- a/docs/releases/4.0.txt
+++ b/docs/releases/4.0.txt
@@ -53,7 +53,7 @@ However, if you are working with non-UTC time zones, and using the ``pytz``
<DATABASE-TIME_ZONE>` setting, you will need to audit your code, since ``pytz``
and ``zoneinfo`` are not entirely equivalent.
-To give time for such an audit, the transitional :setting:`USE_DEPRECATED_PYTZ`
+To give time for such an audit, the transitional ``USE_DEPRECATED_PYTZ``
setting allows continued use of ``pytz`` during the 4.x release cycle. This
setting will be removed in Django 5.0.
@@ -62,7 +62,7 @@ author, can be used to assist with the migration from ``pytz``. This package
provides shims to help you safely remove ``pytz``, and has a detailed
`migration guide`_ showing how to move to the new ``zoneinfo`` APIs.
-Using `pytz_deprecation_shim`_ and the :setting:`USE_DEPRECATED_PYTZ`
+Using `pytz_deprecation_shim`_ and the ``USE_DEPRECATED_PYTZ``
transitional setting is recommended if you need a gradual update path.
.. _pytz_deprecation_shim: https://pytz-deprecation-shim.readthedocs.io/en/latest/index.html
diff --git a/docs/releases/5.0.txt b/docs/releases/5.0.txt
index 07ab33c439..f137c27398 100644
--- a/docs/releases/5.0.txt
+++ b/docs/releases/5.0.txt
@@ -276,6 +276,10 @@ to remove usage of these features.
* The ``USE_L10N`` setting is removed.
+* The ``USE_DEPRECATED_PYTZ`` transitional setting is removed.
+
+* Support for ``pytz`` timezones is removed.
+
See :ref:`deprecated-features-4.1` for details on these changes, including how
to remove usage of these features.
diff --git a/docs/topics/i18n/timezones.txt b/docs/topics/i18n/timezones.txt
index c0063eb12d..10170fcdea 100644
--- a/docs/topics/i18n/timezones.txt
+++ b/docs/topics/i18n/timezones.txt
@@ -677,5 +677,3 @@ Usage
:func:`zoneinfo.available_timezones` provides the set of all valid keys for
IANA time zones available to your system. See the docs for usage
considerations.
-
-.. _pytz: http://pytz.sourceforge.net/
diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py
index 4d1506d257..f7764a7f36 100644
--- a/tests/admin_views/tests.py
+++ b/tests/admin_views/tests.py
@@ -10,11 +10,6 @@ try:
except ImportError:
from backports import zoneinfo
-try:
- import pytz
-except ImportError:
- pytz = None
-
from django.contrib import admin
from django.contrib.admin import AdminSite, ModelAdmin
from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME
@@ -158,9 +153,6 @@ def make_aware_datetimes(dt, iana_key):
"""Makes one aware datetime for each supported time zone provider."""
yield dt.replace(tzinfo=zoneinfo.ZoneInfo(iana_key))
- if pytz is not None:
- yield pytz.timezone(iana_key).localize(dt, is_dst=None)
-
class AdminFieldExtractionMixin:
"""
diff --git a/tests/datetimes/tests.py b/tests/datetimes/tests.py
index 806c28d581..3a1301a031 100644
--- a/tests/datetimes/tests.py
+++ b/tests/datetimes/tests.py
@@ -1,14 +1,7 @@
import datetime
-import unittest
-try:
- import pytz
-except ImportError:
- pytz = None
-
-from django.test import TestCase, ignore_warnings, override_settings
+from django.test import TestCase, override_settings
from django.utils import timezone
-from django.utils.deprecation import RemovedInDjango50Warning
from .models import Article, Category, Comment
@@ -102,46 +95,6 @@ class DateTimesTests(TestCase):
qs = Article.objects.datetimes("pub_date", "second")
self.assertEqual(qs[0], now)
- @unittest.skipUnless(pytz is not None, "Test requires pytz")
- @ignore_warnings(category=RemovedInDjango50Warning)
- @override_settings(USE_TZ=True, TIME_ZONE="UTC", USE_DEPRECATED_PYTZ=True)
- def test_datetimes_ambiguous_and_invalid_times(self):
- sao = pytz.timezone("America/Sao_Paulo")
- utc = pytz.UTC
- article = Article.objects.create(
- title="Article 1",
- pub_date=utc.localize(datetime.datetime(2016, 2, 21, 1)),
- )
- Comment.objects.create(
- article=article,
- pub_date=utc.localize(datetime.datetime(2016, 10, 16, 13)),
- )
- with timezone.override(sao):
- with self.assertRaisesMessage(
- pytz.AmbiguousTimeError, "2016-02-20 23:00:00"
- ):
- Article.objects.datetimes("pub_date", "hour").get()
- with self.assertRaisesMessage(
- pytz.NonExistentTimeError, "2016-10-16 00:00:00"
- ):
- Comment.objects.datetimes("pub_date", "day").get()
- self.assertEqual(
- Article.objects.datetimes("pub_date", "hour", is_dst=False).get().dst(),
- datetime.timedelta(0),
- )
- self.assertEqual(
- Comment.objects.datetimes("pub_date", "day", is_dst=False).get().dst(),
- datetime.timedelta(0),
- )
- self.assertEqual(
- Article.objects.datetimes("pub_date", "hour", is_dst=True).get().dst(),
- datetime.timedelta(0, 3600),
- )
- self.assertEqual(
- Comment.objects.datetimes("pub_date", "hour", is_dst=True).get().dst(),
- datetime.timedelta(0, 3600),
- )
-
def test_datetimes_returns_available_dates_for_given_scope_and_given_field(self):
pub_dates = [
datetime.datetime(2005, 7, 28, 12, 15),
diff --git a/tests/db_functions/datetime/test_extract_trunc.py b/tests/db_functions/datetime/test_extract_trunc.py
index b2327931f0..80043fe3f4 100644
--- a/tests/db_functions/datetime/test_extract_trunc.py
+++ b/tests/db_functions/datetime/test_extract_trunc.py
@@ -1,4 +1,3 @@
-import unittest
from datetime import datetime, timedelta
from datetime import timezone as datetime_timezone
@@ -7,11 +6,6 @@ try:
except ImportError:
from backports import zoneinfo
-try:
- import pytz
-except ImportError:
- pytz = None
-
from django.conf import settings
from django.db import DataError, OperationalError
from django.db.models import (
@@ -51,29 +45,14 @@ from django.db.models.functions import (
)
from django.test import (
TestCase,
- ignore_warnings,
override_settings,
skipIfDBFeature,
skipUnlessDBFeature,
)
from django.utils import timezone
-from django.utils.deprecation import RemovedInDjango50Warning
from ..models import Author, DTModel, Fan
-HAS_PYTZ = pytz is not None
-if not HAS_PYTZ:
- needs_pytz = unittest.skip("Test requires pytz")
-else:
-
- def needs_pytz(f):
- return f
-
-
-ZONE_CONSTRUCTORS = (zoneinfo.ZoneInfo,)
-if HAS_PYTZ:
- ZONE_CONSTRUCTORS += (pytz.timezone,)
-
def truncate_to(value, kind, tzinfo=None):
# Convert to target timezone before truncation
@@ -1690,10 +1669,6 @@ class DateFunctionTests(TestCase):
@override_settings(USE_TZ=True, TIME_ZONE="UTC")
class DateFunctionWithTimeZoneTests(DateFunctionTests):
- def get_timezones(self, key):
- for constructor in ZONE_CONSTRUCTORS:
- yield constructor(key)
-
def test_extract_func_with_timezone(self):
start_datetime = datetime(2015, 6, 15, 23, 30, 1, 321)
end_datetime = datetime(2015, 6, 16, 13, 11, 27, 123)
@@ -1702,62 +1677,57 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
self.create_model(start_datetime, end_datetime)
delta_tzinfo_pos = datetime_timezone(timedelta(hours=5))
delta_tzinfo_neg = datetime_timezone(timedelta(hours=-5, minutes=17))
+ melb = zoneinfo.ZoneInfo("Australia/Melbourne")
- for melb in self.get_timezones("Australia/Melbourne"):
- with self.subTest(repr(melb)):
- qs = DTModel.objects.annotate(
- day=Extract("start_datetime", "day"),
- day_melb=Extract("start_datetime", "day", tzinfo=melb),
- week=Extract("start_datetime", "week", tzinfo=melb),
- isoyear=ExtractIsoYear("start_datetime", tzinfo=melb),
- weekday=ExtractWeekDay("start_datetime"),
- weekday_melb=ExtractWeekDay("start_datetime", tzinfo=melb),
- isoweekday=ExtractIsoWeekDay("start_datetime"),
- isoweekday_melb=ExtractIsoWeekDay("start_datetime", tzinfo=melb),
- quarter=ExtractQuarter("start_datetime", tzinfo=melb),
- hour=ExtractHour("start_datetime"),
- hour_melb=ExtractHour("start_datetime", tzinfo=melb),
- hour_with_delta_pos=ExtractHour(
- "start_datetime", tzinfo=delta_tzinfo_pos
- ),
- hour_with_delta_neg=ExtractHour(
- "start_datetime", tzinfo=delta_tzinfo_neg
- ),
- minute_with_delta_neg=ExtractMinute(
- "start_datetime", tzinfo=delta_tzinfo_neg
- ),
- ).order_by("start_datetime")
+ qs = DTModel.objects.annotate(
+ day=Extract("start_datetime", "day"),
+ day_melb=Extract("start_datetime", "day", tzinfo=melb),
+ week=Extract("start_datetime", "week", tzinfo=melb),
+ isoyear=ExtractIsoYear("start_datetime", tzinfo=melb),
+ weekday=ExtractWeekDay("start_datetime"),
+ weekday_melb=ExtractWeekDay("start_datetime", tzinfo=melb),
+ isoweekday=ExtractIsoWeekDay("start_datetime"),
+ isoweekday_melb=ExtractIsoWeekDay("start_datetime", tzinfo=melb),
+ quarter=ExtractQuarter("start_datetime", tzinfo=melb),
+ hour=ExtractHour("start_datetime"),
+ hour_melb=ExtractHour("start_datetime", tzinfo=melb),
+ hour_with_delta_pos=ExtractHour("start_datetime", tzinfo=delta_tzinfo_pos),
+ hour_with_delta_neg=ExtractHour("start_datetime", tzinfo=delta_tzinfo_neg),
+ minute_with_delta_neg=ExtractMinute(
+ "start_datetime", tzinfo=delta_tzinfo_neg
+ ),
+ ).order_by("start_datetime")
- utc_model = qs.get()
- self.assertEqual(utc_model.day, 15)
- self.assertEqual(utc_model.day_melb, 16)
- self.assertEqual(utc_model.week, 25)
- self.assertEqual(utc_model.isoyear, 2015)
- self.assertEqual(utc_model.weekday, 2)
- self.assertEqual(utc_model.weekday_melb, 3)
- self.assertEqual(utc_model.isoweekday, 1)
- self.assertEqual(utc_model.isoweekday_melb, 2)
- self.assertEqual(utc_model.quarter, 2)
- self.assertEqual(utc_model.hour, 23)
- self.assertEqual(utc_model.hour_melb, 9)
- self.assertEqual(utc_model.hour_with_delta_pos, 4)
- self.assertEqual(utc_model.hour_with_delta_neg, 18)
- self.assertEqual(utc_model.minute_with_delta_neg, 47)
+ utc_model = qs.get()
+ self.assertEqual(utc_model.day, 15)
+ self.assertEqual(utc_model.day_melb, 16)
+ self.assertEqual(utc_model.week, 25)
+ self.assertEqual(utc_model.isoyear, 2015)
+ self.assertEqual(utc_model.weekday, 2)
+ self.assertEqual(utc_model.weekday_melb, 3)
+ self.assertEqual(utc_model.isoweekday, 1)
+ self.assertEqual(utc_model.isoweekday_melb, 2)
+ self.assertEqual(utc_model.quarter, 2)
+ self.assertEqual(utc_model.hour, 23)
+ self.assertEqual(utc_model.hour_melb, 9)
+ self.assertEqual(utc_model.hour_with_delta_pos, 4)
+ self.assertEqual(utc_model.hour_with_delta_neg, 18)
+ self.assertEqual(utc_model.minute_with_delta_neg, 47)
- with timezone.override(melb):
- melb_model = qs.get()
+ with timezone.override(melb):
+ melb_model = qs.get()
- self.assertEqual(melb_model.day, 16)
- self.assertEqual(melb_model.day_melb, 16)
- self.assertEqual(melb_model.week, 25)
- self.assertEqual(melb_model.isoyear, 2015)
- self.assertEqual(melb_model.weekday, 3)
- self.assertEqual(melb_model.isoweekday, 2)
- self.assertEqual(melb_model.quarter, 2)
- self.assertEqual(melb_model.weekday_melb, 3)
- self.assertEqual(melb_model.isoweekday_melb, 2)
- self.assertEqual(melb_model.hour, 9)
- self.assertEqual(melb_model.hour_melb, 9)
+ self.assertEqual(melb_model.day, 16)
+ self.assertEqual(melb_model.day_melb, 16)
+ self.assertEqual(melb_model.week, 25)
+ self.assertEqual(melb_model.isoyear, 2015)
+ self.assertEqual(melb_model.weekday, 3)
+ self.assertEqual(melb_model.isoweekday, 2)
+ self.assertEqual(melb_model.quarter, 2)
+ self.assertEqual(melb_model.weekday_melb, 3)
+ self.assertEqual(melb_model.isoweekday_melb, 2)
+ self.assertEqual(melb_model.hour, 9)
+ self.assertEqual(melb_model.hour_melb, 9)
def test_extract_func_with_timezone_minus_no_offset(self):
start_datetime = datetime(2015, 6, 15, 23, 30, 1, 321)
@@ -1765,22 +1735,22 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
start_datetime = timezone.make_aware(start_datetime)
end_datetime = timezone.make_aware(end_datetime)
self.create_model(start_datetime, end_datetime)
- for ust_nera in self.get_timezones("Asia/Ust-Nera"):
- with self.subTest(repr(ust_nera)):
- qs = DTModel.objects.annotate(
- hour=ExtractHour("start_datetime"),
- hour_tz=ExtractHour("start_datetime", tzinfo=ust_nera),
- ).order_by("start_datetime")
+ ust_nera = zoneinfo.ZoneInfo("Asia/Ust-Nera")
- utc_model = qs.get()
- self.assertEqual(utc_model.hour, 23)
- self.assertEqual(utc_model.hour_tz, 9)
+ qs = DTModel.objects.annotate(
+ hour=ExtractHour("start_datetime"),
+ hour_tz=ExtractHour("start_datetime", tzinfo=ust_nera),
+ ).order_by("start_datetime")
- with timezone.override(ust_nera):
- ust_nera_model = qs.get()
+ utc_model = qs.get()
+ self.assertEqual(utc_model.hour, 23)
+ self.assertEqual(utc_model.hour_tz, 9)
- self.assertEqual(ust_nera_model.hour, 9)
- self.assertEqual(ust_nera_model.hour_tz, 9)
+ with timezone.override(ust_nera):
+ ust_nera_model = qs.get()
+
+ self.assertEqual(ust_nera_model.hour, 9)
+ self.assertEqual(ust_nera_model.hour_tz, 9)
def test_extract_func_explicit_timezone_priority(self):
start_datetime = datetime(2015, 6, 15, 23, 30, 1, 321)
@@ -1788,35 +1758,32 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
start_datetime = timezone.make_aware(start_datetime)
end_datetime = timezone.make_aware(end_datetime)
self.create_model(start_datetime, end_datetime)
-
- for melb in self.get_timezones("Australia/Melbourne"):
- with self.subTest(repr(melb)):
- with timezone.override(melb):
- model = (
- DTModel.objects.annotate(
- day_melb=Extract("start_datetime", "day"),
- day_utc=Extract(
- "start_datetime", "day", tzinfo=datetime_timezone.utc
- ),
- )
- .order_by("start_datetime")
- .get()
- )
- self.assertEqual(model.day_melb, 16)
- self.assertEqual(model.day_utc, 15)
+ melb = zoneinfo.ZoneInfo("Australia/Melbourne")
+ with timezone.override(melb):
+ model = (
+ DTModel.objects.annotate(
+ day_melb=Extract("start_datetime", "day"),
+ day_utc=Extract(
+ "start_datetime", "day", tzinfo=datetime_timezone.utc
+ ),
+ )
+ .order_by("start_datetime")
+ .get()
+ )
+ self.assertEqual(model.day_melb, 16)
+ self.assertEqual(model.day_utc, 15)
def test_extract_invalid_field_with_timezone(self):
- for melb in self.get_timezones("Australia/Melbourne"):
- with self.subTest(repr(melb)):
- msg = "tzinfo can only be used with DateTimeField."
- with self.assertRaisesMessage(ValueError, msg):
- DTModel.objects.annotate(
- day_melb=Extract("start_date", "day", tzinfo=melb),
- ).get()
- with self.assertRaisesMessage(ValueError, msg):
- DTModel.objects.annotate(
- hour_melb=Extract("start_time", "hour", tzinfo=melb),
- ).get()
+ melb = zoneinfo.ZoneInfo("Australia/Melbourne")
+ msg = "tzinfo can only be used with DateTimeField."
+ with self.assertRaisesMessage(ValueError, msg):
+ DTModel.objects.annotate(
+ day_melb=Extract("start_date", "day", tzinfo=melb),
+ ).get()
+ with self.assertRaisesMessage(ValueError, msg):
+ DTModel.objects.annotate(
+ hour_melb=Extract("start_time", "hour", tzinfo=melb),
+ ).get()
def test_trunc_timezone_applied_before_truncation(self):
start_datetime = datetime(2016, 1, 1, 1, 30, 50, 321)
@@ -1824,74 +1791,36 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
start_datetime = timezone.make_aware(start_datetime)
end_datetime = timezone.make_aware(end_datetime)
self.create_model(start_datetime, end_datetime)
+ melb = zoneinfo.ZoneInfo("Australia/Melbourne")
+ pacific = zoneinfo.ZoneInfo("America/Los_Angeles")
- for melb, pacific in zip(
- self.get_timezones("Australia/Melbourne"),
- self.get_timezones("America/Los_Angeles"),
- ):
- with self.subTest((repr(melb), repr(pacific))):
- model = (
- DTModel.objects.annotate(
- melb_year=TruncYear("start_datetime", tzinfo=melb),
- pacific_year=TruncYear("start_datetime", tzinfo=pacific),
- melb_date=TruncDate("start_datetime", tzinfo=melb),
- pacific_date=TruncDate("start_datetime", tzinfo=pacific),
- melb_time=TruncTime("start_datetime", tzinfo=melb),
- pacific_time=TruncTime("start_datetime", tzinfo=pacific),
- )
- .order_by("start_datetime")
- .get()
- )
-
- melb_start_datetime = start_datetime.astimezone(melb)
- pacific_start_datetime = start_datetime.astimezone(pacific)
- self.assertEqual(model.start_datetime, start_datetime)
- self.assertEqual(
- model.melb_year, truncate_to(start_datetime, "year", melb)
- )
- self.assertEqual(
- model.pacific_year, truncate_to(start_datetime, "year", pacific)
- )
- self.assertEqual(model.start_datetime.year, 2016)
- self.assertEqual(model.melb_year.year, 2016)
- self.assertEqual(model.pacific_year.year, 2015)
- self.assertEqual(model.melb_date, melb_start_datetime.date())
- self.assertEqual(model.pacific_date, pacific_start_datetime.date())
- self.assertEqual(model.melb_time, melb_start_datetime.time())
- self.assertEqual(model.pacific_time, pacific_start_datetime.time())
+ model = (
+ DTModel.objects.annotate(
+ melb_year=TruncYear("start_datetime", tzinfo=melb),
+ pacific_year=TruncYear("start_datetime", tzinfo=pacific),
+ melb_date=TruncDate("start_datetime", tzinfo=melb),
+ pacific_date=TruncDate("start_datetime", tzinfo=pacific),
+ melb_time=TruncTime("start_datetime", tzinfo=melb),
+ pacific_time=TruncTime("start_datetime", tzinfo=pacific),
+ )
+ .order_by("start_datetime")
+ .get()
+ )
- @needs_pytz
- @ignore_warnings(category=RemovedInDjango50Warning)
- def test_trunc_ambiguous_and_invalid_times(self):
- sao = pytz.timezone("America/Sao_Paulo")
- start_datetime = datetime(2016, 10, 16, 13, tzinfo=datetime_timezone.utc)
- end_datetime = datetime(2016, 2, 21, 1, tzinfo=datetime_timezone.utc)
- self.create_model(start_datetime, end_datetime)
- with timezone.override(sao):
- with self.assertRaisesMessage(
- pytz.NonExistentTimeError, "2016-10-16 00:00:00"
- ):
- model = DTModel.objects.annotate(
- truncated_start=TruncDay("start_datetime")
- ).get()
- with self.assertRaisesMessage(
- pytz.AmbiguousTimeError, "2016-02-20 23:00:00"
- ):
- model = DTModel.objects.annotate(
- truncated_end=TruncHour("end_datetime")
- ).get()
- model = DTModel.objects.annotate(
- truncated_start=TruncDay("start_datetime", is_dst=False),
- truncated_end=TruncHour("end_datetime", is_dst=False),
- ).get()
- self.assertEqual(model.truncated_start.dst(), timedelta(0))
- self.assertEqual(model.truncated_end.dst(), timedelta(0))
- model = DTModel.objects.annotate(
- truncated_start=TruncDay("start_datetime", is_dst=True),
- truncated_end=TruncHour("end_datetime", is_dst=True),
- ).get()
- self.assertEqual(model.truncated_start.dst(), timedelta(0, 3600))
- self.assertEqual(model.truncated_end.dst(), timedelta(0, 3600))
+ melb_start_datetime = start_datetime.astimezone(melb)
+ pacific_start_datetime = start_datetime.astimezone(pacific)
+ self.assertEqual(model.start_datetime, start_datetime)
+ self.assertEqual(model.melb_year, truncate_to(start_datetime, "year", melb))
+ self.assertEqual(
+ model.pacific_year, truncate_to(start_datetime, "year", pacific)
+ )
+ self.assertEqual(model.start_datetime.year, 2016)
+ self.assertEqual(model.melb_year.year, 2016)
+ self.assertEqual(model.pacific_year.year, 2015)
+ self.assertEqual(model.melb_date, melb_start_datetime.date())
+ self.assertEqual(model.pacific_date, pacific_start_datetime.date())
+ self.assertEqual(model.melb_time, melb_start_datetime.time())
+ self.assertEqual(model.pacific_time, pacific_start_datetime.time())
def test_trunc_func_with_timezone(self):
"""
@@ -1904,118 +1833,109 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
end_datetime = timezone.make_aware(end_datetime)
self.create_model(start_datetime, end_datetime)
self.create_model(end_datetime, start_datetime)
+ melb = zoneinfo.ZoneInfo("Australia/Melbourne")
- for melb in self.get_timezones("Australia/Melbourne"):
- with self.subTest(repr(melb)):
-
- def test_datetime_kind(kind):
- self.assertQuerySetEqual(
- DTModel.objects.annotate(
- truncated=Trunc(
- "start_datetime",
- kind,
- output_field=DateTimeField(),
- tzinfo=melb,
- )
- ).order_by("start_datetime"),
- [
- (
- start_datetime,
- truncate_to(
- start_datetime.astimezone(melb), kind, melb
- ),
- ),
- (
- end_datetime,
- truncate_to(end_datetime.astimezone(melb), kind, melb),
- ),
- ],
- lambda m: (m.start_datetime, m.truncated),
+ def test_datetime_kind(kind):
+ self.assertQuerySetEqual(
+ DTModel.objects.annotate(
+ truncated=Trunc(
+ "start_datetime",
+ kind,
+ output_field=DateTimeField(),
+ tzinfo=melb,
)
+ ).order_by("start_datetime"),
+ [
+ (
+ start_datetime,
+ truncate_to(start_datetime.astimezone(melb), kind, melb),
+ ),
+ (
+ end_datetime,
+ truncate_to(end_datetime.astimezone(melb), kind, melb),
+ ),
+ ],
+ lambda m: (m.start_datetime, m.truncated),
+ )
- def test_datetime_to_date_kind(kind):
- self.assertQuerySetEqual(
- DTModel.objects.annotate(
- truncated=Trunc(
- "start_datetime",
- kind,
- output_field=DateField(),
- tzinfo=melb,
- ),
- ).order_by("start_datetime"),
- [
- (
- start_datetime,
- truncate_to(
- start_datetime.astimezone(melb).date(), kind
- ),
- ),
- (
- end_datetime,
- truncate_to(end_datetime.astimezone(melb).date(), kind),
- ),
- ],
- lambda m: (m.start_datetime, m.truncated),
- )
+ def test_datetime_to_date_kind(kind):
+ self.assertQuerySetEqual(
+ DTModel.objects.annotate(
+ truncated=Trunc(
+ "start_datetime",
+ kind,
+ output_field=DateField(),
+ tzinfo=melb,
+ ),
+ ).order_by("start_datetime"),
+ [
+ (
+ start_datetime,
+ truncate_to(start_datetime.astimezone(melb).date(), kind),
+ ),
+ (
+ end_datetime,
+ truncate_to(end_datetime.astimezone(melb).date(), kind),
+ ),
+ ],
+ lambda m: (m.start_datetime, m.truncated),
+ )
- def test_datetime_to_time_kind(kind):
- self.assertQuerySetEqual(
- DTModel.objects.annotate(
- truncated=Trunc(
- "start_datetime",
- kind,
- output_field=TimeField(),
- tzinfo=melb,
- )
- ).order_by("start_datetime"),
- [
- (
- start_datetime,
- truncate_to(
- start_datetime.astimezone(melb).time(), kind
- ),
- ),
- (
- end_datetime,
- truncate_to(end_datetime.astimezone(melb).time(), kind),
- ),
- ],
- lambda m: (m.start_datetime, m.truncated),
+ def test_datetime_to_time_kind(kind):
+ self.assertQuerySetEqual(
+ DTModel.objects.annotate(
+ truncated=Trunc(
+ "start_datetime",
+ kind,
+ output_field=TimeField(),
+ tzinfo=melb,
)
+ ).order_by("start_datetime"),
+ [
+ (
+ start_datetime,
+ truncate_to(start_datetime.astimezone(melb).time(), kind),
+ ),
+ (
+ end_datetime,
+ truncate_to(end_datetime.astimezone(melb).time(), kind),
+ ),
+ ],
+ lambda m: (m.start_datetime, m.truncated),
+ )
- test_datetime_to_date_kind("year")
- test_datetime_to_date_kind("quarter")
- test_datetime_to_date_kind("month")
- test_datetime_to_date_kind("week")
- test_datetime_to_date_kind("day")
- test_datetime_to_time_kind("hour")
- test_datetime_to_time_kind("minute")
- test_datetime_to_time_kind("second")
- test_datetime_kind("year")
- test_datetime_kind("quarter")
- test_datetime_kind("month")
- test_datetime_kind("week")
- test_datetime_kind("day")
- test_datetime_kind("hour")
- test_datetime_kind("minute")
- test_datetime_kind("second")
+ test_datetime_to_date_kind("year")
+ test_datetime_to_date_kind("quarter")
+ test_datetime_to_date_kind("month")
+ test_datetime_to_date_kind("week")
+ test_datetime_to_date_kind("day")
+ test_datetime_to_time_kind("hour")
+ test_datetime_to_time_kind("minute")
+ test_datetime_to_time_kind("second")
+ test_datetime_kind("year")
+ test_datetime_kind("quarter")
+ test_datetime_kind("month")
+ test_datetime_kind("week")
+ test_datetime_kind("day")
+ test_datetime_kind("hour")
+ test_datetime_kind("minute")
+ test_datetime_kind("second")
- qs = DTModel.objects.filter(
- start_datetime__date=Trunc(
- "start_datetime", "day", output_field=DateField()
- )
- )
- self.assertEqual(qs.count(), 2)
+ qs = DTModel.objects.filter(
+ start_datetime__date=Trunc(
+ "start_datetime", "day", output_field=DateField()
+ )
+ )
+ self.assertEqual(qs.count(), 2)
def test_trunc_invalid_field_with_timezone(self):
- for melb in self.get_timezones("Australia/Melbourne"):
- with self.subTest(repr(melb)):
- msg = "tzinfo can only be used with DateTimeField."
- with self.assertRaisesMessage(ValueError, msg):
- DTModel.objects.annotate(
- day_melb=Trunc("start_date", "day", tzinfo=melb),
- ).get()
- with self.assertRaisesMessage(ValueError, msg):
- DTModel.objects.annotate(
- hour_melb=Trunc("start_time", "hour", tzinfo=melb),
- ).get()
+ melb = zoneinfo.ZoneInfo("Australia/Melbourne")
+ msg = "tzinfo can only be used with DateTimeField."
+ with self.assertRaisesMessage(ValueError, msg):
+ DTModel.objects.annotate(
+ day_melb=Trunc("start_date", "day", tzinfo=melb),
+ ).get()
+ with self.assertRaisesMessage(ValueError, msg):
+ DTModel.objects.annotate(
+ hour_melb=Trunc("start_time", "hour", tzinfo=melb),
+ ).get()
diff --git a/tests/migrations/test_writer.py b/tests/migrations/test_writer.py
index 2a965ef7e0..ce1c024b13 100644
--- a/tests/migrations/test_writer.py
+++ b/tests/migrations/test_writer.py
@@ -15,11 +15,6 @@ try:
except ImportError:
from backports import zoneinfo
-try:
- import pytz
-except ImportError:
- pytz = None
-
import custom_migration_operations.more_operations
import custom_migration_operations.operations
@@ -595,16 +590,6 @@ class WriterTests(SimpleTestCase):
{"import datetime"},
),
)
- if pytz:
- self.assertSerializedResultEqual(
- pytz.timezone("Europe/Paris").localize(
- datetime.datetime(2012, 1, 1, 2, 1)
- ),
- (
- "datetime.datetime(2012, 1, 1, 1, 1, tzinfo=datetime.timezone.utc)",
- {"import datetime"},
- ),
- )
def test_serialize_fields(self):
self.assertSerializedFieldEqual(models.CharField(max_length=255))
diff --git a/tests/requirements/py3.txt b/tests/requirements/py3.txt
index f80bf8b2a7..397aaa06b1 100644
--- a/tests/requirements/py3.txt
+++ b/tests/requirements/py3.txt
@@ -12,7 +12,6 @@ Pillow >= 6.2.1; sys.platform != 'win32' or python_version < '3.12'
# pylibmc/libmemcached can't be built on Windows.
pylibmc; sys.platform != 'win32'
pymemcache >= 3.4.0
-pytz
pywatchman; sys.platform != 'win32'
PyYAML
redis >= 3.4.0
diff --git a/tests/settings_tests/tests.py b/tests/settings_tests/tests.py
index 32a9c6cc19..d6666537de 100644
--- a/tests/settings_tests/tests.py
+++ b/tests/settings_tests/tests.py
@@ -4,13 +4,7 @@ import unittest
from types import ModuleType, SimpleNamespace
from unittest import mock
-from django.conf import (
- ENVIRONMENT_VARIABLE,
- USE_DEPRECATED_PYTZ_DEPRECATED_MSG,
- LazySettings,
- Settings,
- settings,
-)
+from django.conf import ENVIRONMENT_VARIABLE, LazySettings, Settings, settings
from django.core.exceptions import ImproperlyConfigured
from django.http import HttpRequest
from django.test import (
@@ -23,7 +17,6 @@ from django.test import (
)
from django.test.utils import requires_tz_support
from django.urls import clear_script_prefix, set_script_prefix
-from django.utils.deprecation import RemovedInDjango50Warning
@modify_settings(ITEMS={"prepend": ["b"], "append": ["d"], "remove": ["a", "e"]})
@@ -348,24 +341,6 @@ class SettingsTests(SimpleTestCase):
with self.assertRaisesMessage(ValueError, "Incorrect timezone setting: test"):
settings._setup()
- def test_use_deprecated_pytz_deprecation(self):
- settings_module = ModuleType("fake_settings_module")
- settings_module.USE_DEPRECATED_PYTZ = True
- sys.modules["fake_settings_module"] = settings_module
- try:
- with self.assertRaisesMessage(
- RemovedInDjango50Warning, USE_DEPRECATED_PYTZ_DEPRECATED_MSG
- ):
- Settings("fake_settings_module")
- finally:
- del sys.modules["fake_settings_module"]
-
- holder = LazySettings()
- with self.assertRaisesMessage(
- RemovedInDjango50Warning, USE_DEPRECATED_PYTZ_DEPRECATED_MSG
- ):
- holder.configure(USE_DEPRECATED_PYTZ=True)
-
class TestComplexSettingOverride(SimpleTestCase):
def setUp(self):
diff --git a/tests/timezones/tests.py b/tests/timezones/tests.py
index f5c8d17a3c..89e87172f1 100644
--- a/tests/timezones/tests.py
+++ b/tests/timezones/tests.py
@@ -10,11 +10,6 @@ try:
except ImportError:
from backports import zoneinfo
-try:
- import pytz
-except ImportError:
- pytz = None
-
from django.contrib.auth.models import User
from django.core import serializers
from django.db import connection
@@ -31,7 +26,6 @@ from django.test import (
SimpleTestCase,
TestCase,
TransactionTestCase,
- ignore_warnings,
override_settings,
skipIfDBFeature,
skipUnlessDBFeature,
@@ -79,14 +73,6 @@ UTC = datetime.timezone.utc
EAT = timezone.get_fixed_timezone(180) # Africa/Nairobi
ICT = timezone.get_fixed_timezone(420) # Asia/Bangkok
-ZONE_CONSTRUCTORS = (zoneinfo.ZoneInfo,)
-if pytz is not None:
- ZONE_CONSTRUCTORS += (pytz.timezone,)
-
-
-def get_timezones(key):
- return [constructor(key) for constructor in ZONE_CONSTRUCTORS]
-
class UTCAliasTests(SimpleTestCase):
def test_alias_deprecation_warning(self):
@@ -413,39 +399,17 @@ class NewDatabaseTests(TestCase):
self.assertEqual(Event.objects.filter(dt__gte=dt2).count(), 1)
self.assertEqual(Event.objects.filter(dt__gt=dt2).count(), 0)
- def test_query_filter_with_pytz_timezones(self):
- for tz in get_timezones("Europe/Paris"):
- with self.subTest(repr(tz)):
- dt = datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=tz)
- Event.objects.create(dt=dt)
- next = dt + datetime.timedelta(seconds=3)
- prev = dt - datetime.timedelta(seconds=3)
- self.assertEqual(Event.objects.filter(dt__exact=dt).count(), 1)
- self.assertEqual(Event.objects.filter(dt__exact=next).count(), 0)
- self.assertEqual(Event.objects.filter(dt__in=(prev, next)).count(), 0)
- self.assertEqual(
- Event.objects.filter(dt__in=(prev, dt, next)).count(), 1
- )
- self.assertEqual(
- Event.objects.filter(dt__range=(prev, next)).count(), 1
- )
-
- @ignore_warnings(category=RemovedInDjango50Warning)
- def test_connection_timezone(self):
- tests = [
- (False, None, datetime.timezone),
- (False, "Africa/Nairobi", zoneinfo.ZoneInfo),
- ]
- if pytz is not None:
- tests += [
- (True, None, datetime.timezone),
- (True, "Africa/Nairobi", pytz.BaseTzInfo),
- ]
- for use_pytz, connection_tz, expected_type in tests:
- with self.subTest(use_pytz=use_pytz, connection_tz=connection_tz):
- with self.settings(USE_DEPRECATED_PYTZ=use_pytz):
- with override_database_connection_timezone(connection_tz):
- self.assertIsInstance(connection.timezone, expected_type)
+ def test_query_filter_with_timezones(self):
+ tz = zoneinfo.ZoneInfo("Europe/Paris")
+ dt = datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=tz)
+ Event.objects.create(dt=dt)
+ next = dt + datetime.timedelta(seconds=3)
+ prev = dt - datetime.timedelta(seconds=3)
+ self.assertEqual(Event.objects.filter(dt__exact=dt).count(), 1)
+ self.assertEqual(Event.objects.filter(dt__exact=next).count(), 0)
+ self.assertEqual(Event.objects.filter(dt__in=(prev, next)).count(), 0)
+ self.assertEqual(Event.objects.filter(dt__in=(prev, dt, next)).count(), 1)
+ self.assertEqual(Event.objects.filter(dt__range=(prev, next)).count(), 1)
def test_query_convert_timezones(self):
# Connection timezone is equal to the current timezone, datetime
@@ -1075,16 +1039,15 @@ class TemplateTests(SimpleTestCase):
)
# Use an IANA timezone as argument
- for tz in get_timezones("Europe/Paris"):
- with self.subTest(repr(tz)):
- tpl = Template("{% load tz %}{{ dt|timezone:tz }}")
- ctx = Context(
- {
- "dt": datetime.datetime(2011, 9, 1, 13, 20, 30),
- "tz": tz,
- }
- )
- self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00")
+ tz = zoneinfo.ZoneInfo("Europe/Paris")
+ tpl = Template("{% load tz %}{{ dt|timezone:tz }}")
+ ctx = Context(
+ {
+ "dt": datetime.datetime(2011, 9, 1, 13, 20, 30),
+ "tz": tz,
+ }
+ )
+ self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00")
def test_localtime_templatetag_invalid_argument(self):
with self.assertRaises(TemplateSyntaxError):
@@ -1147,15 +1110,14 @@ class TemplateTests(SimpleTestCase):
tpl = Template("{% load tz %}{% timezone tz %}{{ dt }}{% endtimezone %}")
# Use a IANA timezone as argument
- for tz in get_timezones("Europe/Paris"):
- with self.subTest(repr(tz)):
- ctx = Context(
- {
- "dt": datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT),
- "tz": tz,
- }
- )
- self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00")
+ tz = zoneinfo.ZoneInfo("Europe/Paris")
+ ctx = Context(
+ {
+ "dt": datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT),
+ "tz": tz,
+ }
+ )
+ self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00")
# Use a IANA timezone name as argument
ctx = Context(
@@ -1166,22 +1128,6 @@ class TemplateTests(SimpleTestCase):
)
self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00")
- @ignore_warnings(category=RemovedInDjango50Warning)
- def test_timezone_templatetag_invalid_argument(self):
- with self.assertRaises(TemplateSyntaxError):
- Template("{% load tz %}{% timezone %}{% endtimezone %}").render()
- with self.assertRaises(zoneinfo.ZoneInfoNotFoundError):
- Template("{% load tz %}{% timezone tz %}{% endtimezone %}").render(
- Context({"tz": "foobar"})
- )
- if pytz is not None:
- with override_settings(USE_DEPRECATED_PYTZ=True), self.assertRaises(
- pytz.UnknownTimeZoneError
- ):
- Template("{% load tz %}{% timezone tz %}{% endtimezone %}").render(
- Context({"tz": "foobar"})
- )
-
@skipIf(sys.platform == "win32", "Windows uses non-standard time zone names")
def test_get_current_timezone_templatetag(self):
"""
@@ -1205,16 +1151,12 @@ class TemplateTests(SimpleTestCase):
self.assertEqual(tpl.render(Context({"tz": ICT})), "+0700")
def test_get_current_timezone_templatetag_with_iana(self):
- """
- Test the {% get_current_timezone %} templatetag with pytz.
- """
tpl = Template(
"{% load tz %}{% get_current_timezone as time_zone %}{{ time_zone }}"
)
- for tz in get_timezones("Europe/Paris"):
- with self.subTest(repr(tz)):
- with timezone.override(tz):
- self.assertEqual(tpl.render(Context()), "Europe/Paris")
+ tz = zoneinfo.ZoneInfo("Europe/Paris")
+ with timezone.override(tz):
+ self.assertEqual(tpl.render(Context()), "Europe/Paris")
tpl = Template(
"{% load tz %}{% timezone 'Europe/Paris' %}"
@@ -1282,27 +1224,25 @@ class LegacyFormsTests(TestCase):
def test_form_with_non_existent_time(self):
form = EventForm({"dt": "2011-03-27 02:30:00"})
- for tz in get_timezones("Europe/Paris"):
- with self.subTest(repr(tz)):
- with timezone.override(tz):
- # This is a bug.
- self.assertTrue(form.is_valid())
- self.assertEqual(
- form.cleaned_data["dt"],
- datetime.datetime(2011, 3, 27, 2, 30, 0),
- )
+ tz = zoneinfo.ZoneInfo("Europe/Paris")
+ with timezone.override(tz):
+ # This is a bug.
+ self.assertTrue(form.is_valid())
+ self.assertEqual(
+ form.cleaned_data["dt"],
+ datetime.datetime(2011, 3, 27, 2, 30, 0),
+ )
def test_form_with_ambiguous_time(self):
form = EventForm({"dt": "2011-10-30 02:30:00"})
- for tz in get_timezones("Europe/Paris"):
- with self.subTest(repr(tz)):
- with timezone.override(tz):
- # This is a bug.
- self.assertTrue(form.is_valid())
- self.assertEqual(
- form.cleaned_data["dt"],
- datetime.datetime(2011, 10, 30, 2, 30, 0),
- )
+ tz = zoneinfo.ZoneInfo("Europe/Paris")
+ with timezone.override(tz):
+ # This is a bug.
+ self.assertTrue(form.is_valid())
+ self.assertEqual(
+ form.cleaned_data["dt"],
+ datetime.datetime(2011, 10, 30, 2, 30, 0),
+ )
def test_split_form(self):
form = EventSplitForm({"dt_0": "2011-09-01", "dt_1": "13:20:30"})
@@ -1338,32 +1278,30 @@ class NewFormsTests(TestCase):
)
def test_form_with_non_existent_time(self):
- for tz in get_timezones("Europe/Paris"):
- with self.subTest(repr(tz)):
- with timezone.override(tz):
- form = EventForm({"dt": "2011-03-27 02:30:00"})
- self.assertFalse(form.is_valid())
- self.assertEqual(
- form.errors["dt"],
- [
- "2011-03-27 02:30:00 couldn’t be interpreted in time zone "
- "Europe/Paris; it may be ambiguous or it may not exist."
- ],
- )
+ tz = zoneinfo.ZoneInfo("Europe/Paris")
+ with timezone.override(tz):
+ form = EventForm({"dt": "2011-03-27 02:30:00"})
+ self.assertFalse(form.is_valid())
+ self.assertEqual(
+ form.errors["dt"],
+ [
+ "2011-03-27 02:30:00 couldn’t be interpreted in time zone "
+ "Europe/Paris; it may be ambiguous or it may not exist."
+ ],
+ )
def test_form_with_ambiguous_time(self):
- for tz in get_timezones("Europe/Paris"):
- with self.subTest(repr(tz)):
- with timezone.override(tz):
- form = EventForm({"dt": "2011-10-30 02:30:00"})
- self.assertFalse(form.is_valid())
- self.assertEqual(
- form.errors["dt"],
- [
- "2011-10-30 02:30:00 couldn’t be interpreted in time zone "
- "Europe/Paris; it may be ambiguous or it may not exist."
- ],
- )
+ tz = zoneinfo.ZoneInfo("Europe/Paris")
+ with timezone.override(tz):
+ form = EventForm({"dt": "2011-10-30 02:30:00"})
+ self.assertFalse(form.is_valid())
+ self.assertEqual(
+ form.errors["dt"],
+ [
+ "2011-10-30 02:30:00 couldn’t be interpreted in time zone "
+ "Europe/Paris; it may be ambiguous or it may not exist."
+ ],
+ )
@requires_tz_support
def test_split_form(self):
diff --git a/tests/utils_tests/test_dateformat.py b/tests/utils_tests/test_dateformat.py
index 9563790b20..dce678e172 100644
--- a/tests/utils_tests/test_dateformat.py
+++ b/tests/utils_tests/test_dateformat.py
@@ -25,8 +25,7 @@ class DateFormatTests(SimpleTestCase):
self.assertEqual(datetime.fromtimestamp(int(format(dt, "U"))), dt)
def test_naive_ambiguous_datetime(self):
- # dt is ambiguous in Europe/Copenhagen. pytz raises an exception for
- # the ambiguity, which results in an empty string.
+ # dt is ambiguous in Europe/Copenhagen.
dt = datetime(2015, 10, 25, 2, 30, 0)
# Try all formatters that involve self.timezone.
diff --git a/tests/utils_tests/test_timezone.py b/tests/utils_tests/test_timezone.py
index ed8386945d..6ec8828561 100644
--- a/tests/utils_tests/test_timezone.py
+++ b/tests/utils_tests/test_timezone.py
@@ -1,18 +1,12 @@
import datetime
-import unittest
from unittest import mock
try:
- import pytz
-except ImportError:
- pytz = None
-
-try:
import zoneinfo
except ImportError:
from backports import zoneinfo
-from django.test import SimpleTestCase, ignore_warnings, override_settings
+from django.test import SimpleTestCase, override_settings
from django.utils import timezone
from django.utils.deprecation import RemovedInDjango50Warning
@@ -21,38 +15,11 @@ EAT = timezone.get_fixed_timezone(180) # Africa/Nairobi
ICT = timezone.get_fixed_timezone(420) # Asia/Bangkok
UTC = datetime.timezone.utc
-HAS_PYTZ = pytz is not None
-if not HAS_PYTZ:
- CET = None
- PARIS_IMPLS = (PARIS_ZI,)
-
- needs_pytz = unittest.skip("Test requires pytz")
-else:
- CET = pytz.timezone("Europe/Paris")
- PARIS_IMPLS = (PARIS_ZI, CET)
-
- def needs_pytz(f):
- return f
-
class TimezoneTests(SimpleTestCase):
- def setUp(self):
- # RemovedInDjango50Warning
- timezone.get_default_timezone.cache_clear()
-
- def tearDown(self):
- # RemovedInDjango50Warning
- timezone.get_default_timezone.cache_clear()
-
def test_default_timezone_is_zoneinfo(self):
self.assertIsInstance(timezone.get_default_timezone(), zoneinfo.ZoneInfo)
- @needs_pytz
- @ignore_warnings(category=RemovedInDjango50Warning)
- @override_settings(USE_DEPRECATED_PYTZ=True)
- def test_setting_allows_fallback_to_pytz(self):
- self.assertIsInstance(timezone.get_default_timezone(), pytz.BaseTzInfo)
-
def test_now(self):
with override_settings(USE_TZ=True):
self.assertTrue(timezone.is_aware(timezone.now()))
@@ -208,46 +175,15 @@ class TimezoneTests(SimpleTestCase):
def test_make_aware2(self):
CEST = datetime.timezone(datetime.timedelta(hours=2), "CEST")
- for tz in PARIS_IMPLS:
- with self.subTest(repr(tz)):
- self.assertEqual(
- timezone.make_aware(datetime.datetime(2011, 9, 1, 12, 20, 30), tz),
- datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=CEST),
- )
-
- if HAS_PYTZ:
- with self.assertRaises(ValueError):
- timezone.make_aware(
- CET.localize(datetime.datetime(2011, 9, 1, 12, 20, 30)), CET
- )
-
+ self.assertEqual(
+ timezone.make_aware(datetime.datetime(2011, 9, 1, 12, 20, 30), PARIS_ZI),
+ datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=CEST),
+ )
with self.assertRaises(ValueError):
timezone.make_aware(
datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=PARIS_ZI), PARIS_ZI
)
- @needs_pytz
- def test_make_naive_pytz(self):
- self.assertEqual(
- timezone.make_naive(
- CET.localize(datetime.datetime(2011, 9, 1, 12, 20, 30)), CET
- ),
- datetime.datetime(2011, 9, 1, 12, 20, 30),
- )
- self.assertEqual(
- timezone.make_naive(
- pytz.timezone("Asia/Bangkok").localize(
- datetime.datetime(2011, 9, 1, 17, 20, 30)
- ),
- CET,
- ),
- datetime.datetime(2011, 9, 1, 12, 20, 30),
- )
- with self.assertRaisesMessage(
- ValueError, "make_naive() cannot be applied to a naive datetime"
- ):
- timezone.make_naive(datetime.datetime(2011, 9, 1, 12, 20, 30), CET)
-
def test_make_naive_zoneinfo(self):
self.assertEqual(
timezone.make_naive(
@@ -264,21 +200,6 @@ class TimezoneTests(SimpleTestCase):
datetime.datetime(2011, 9, 1, 12, 20, 30, fold=1),
)
- @needs_pytz
- @ignore_warnings(category=RemovedInDjango50Warning)
- def test_make_aware_pytz_ambiguous(self):
- # 2:30 happens twice, once before DST ends and once after
- ambiguous = datetime.datetime(2015, 10, 25, 2, 30)
-
- with self.assertRaises(pytz.AmbiguousTimeError):
- timezone.make_aware(ambiguous, timezone=CET)
-
- std = timezone.make_aware(ambiguous, timezone=CET, is_dst=False)
- dst = timezone.make_aware(ambiguous, timezone=CET, is_dst=True)
- self.assertEqual(std - dst, datetime.timedelta(hours=1))
- self.assertEqual(std.tzinfo.utcoffset(std), datetime.timedelta(hours=1))
- self.assertEqual(dst.tzinfo.utcoffset(dst), datetime.timedelta(hours=2))
-
def test_make_aware_zoneinfo_ambiguous(self):
# 2:30 happens twice, once before DST ends and once after
ambiguous = datetime.datetime(2015, 10, 25, 2, 30)
@@ -292,21 +213,6 @@ class TimezoneTests(SimpleTestCase):
self.assertEqual(std.utcoffset(), datetime.timedelta(hours=1))
self.assertEqual(dst.utcoffset(), datetime.timedelta(hours=2))
- @needs_pytz
- @ignore_warnings(category=RemovedInDjango50Warning)
- def test_make_aware_pytz_non_existent(self):
- # 2:30 never happened due to DST
- non_existent = datetime.datetime(2015, 3, 29, 2, 30)
-
- with self.assertRaises(pytz.NonExistentTimeError):
- timezone.make_aware(non_existent, timezone=CET)
-
- std = timezone.make_aware(non_existent, timezone=CET, is_dst=False)
- dst = timezone.make_aware(non_existent, timezone=CET, is_dst=True)
- self.assertEqual(std - dst, datetime.timedelta(hours=1))
- self.assertEqual(std.tzinfo.utcoffset(std), datetime.timedelta(hours=1))
- self.assertEqual(dst.tzinfo.utcoffset(dst), datetime.timedelta(hours=2))
-
def test_make_aware_zoneinfo_non_existent(self):
# 2:30 never happened due to DST
non_existent = datetime.datetime(2015, 3, 29, 2, 30)
@@ -349,12 +255,6 @@ class TimezoneTests(SimpleTestCase):
(zoneinfo.ZoneInfo("Europe/Madrid"), "Europe/Madrid"),
(zoneinfo.ZoneInfo("Etc/GMT-10"), "+10"),
]
- if HAS_PYTZ:
- tests += [
- # pytz, named and fixed offset.
- (pytz.timezone("Europe/Madrid"), "Europe/Madrid"),
- (pytz.timezone("Etc/GMT-10"), "+10"),
- ]
for tz, expected in tests:
with self.subTest(tz=tz, expected=expected):
self.assertEqual(timezone._get_timezone_name(tz), expected)