From e6f82438d4e3750e8d299bfd79dac98eebe9f1e0 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Mon, 9 Jan 2023 09:03:38 +0100 Subject: Refs #32365 -- Removed support for pytz timezones per deprecation timeline. --- tests/admin_views/tests.py | 8 - tests/datetimes/tests.py | 49 +-- tests/db_functions/datetime/test_extract_trunc.py | 504 +++++++++------------- tests/migrations/test_writer.py | 15 - tests/requirements/py3.txt | 1 - tests/settings_tests/tests.py | 27 +- tests/timezones/tests.py | 200 +++------ tests/utils_tests/test_dateformat.py | 3 +- tests/utils_tests/test_timezone.py | 110 +---- 9 files changed, 289 insertions(+), 628 deletions(-) (limited to 'tests') 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") - - 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() - - 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) + 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) + + 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) 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") + + utc_model = qs.get() + self.assertEqual(utc_model.hour, 23) + self.assertEqual(utc_model.hour_tz, 9) - with timezone.override(ust_nera): - ust_nera_model = qs.get() + 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) + 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() - ) + 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()) - - @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) -- cgit v1.3