mirror of
https://github.com/django/django.git
synced 2024-11-21 10:59:04 +01:00
Refs #32365 -- Removed support for pytz timezones per deprecation timeline.
This commit is contained in:
parent
8d98f99a4a
commit
e6f82438d4
@ -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(
|
||||
|
@ -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"
|
||||
|
@ -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,
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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 ""
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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``
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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/
|
||||
|
@ -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:
|
||||
"""
|
||||
|
@ -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),
|
||||
|
@ -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()
|
||||
)
|
||||
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()
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user