mirror of https://github.com/django/django.git
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"
|
STATICFILES_STORAGE_ALIAS = "staticfiles"
|
||||||
|
|
||||||
# RemovedInDjango50Warning
|
# 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 = (
|
CSRF_COOKIE_MASKED_DEPRECATED_MSG = (
|
||||||
"The CSRF_COOKIE_MASKED transitional setting is deprecated. Support for "
|
"The CSRF_COOKIE_MASKED transitional setting is deprecated. Support for "
|
||||||
"it will be removed in Django 5.0."
|
"it will be removed in Django 5.0."
|
||||||
|
@ -217,9 +211,6 @@ class Settings:
|
||||||
setattr(self, setting, setting_value)
|
setattr(self, setting, setting_value)
|
||||||
self._explicit_settings.add(setting)
|
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"):
|
if self.is_overridden("CSRF_COOKIE_MASKED"):
|
||||||
warnings.warn(CSRF_COOKIE_MASKED_DEPRECATED_MSG, RemovedInDjango50Warning)
|
warnings.warn(CSRF_COOKIE_MASKED_DEPRECATED_MSG, RemovedInDjango50Warning)
|
||||||
|
|
||||||
|
@ -294,8 +285,6 @@ class UserSettingsHolder:
|
||||||
}
|
}
|
||||||
warnings.warn(STATICFILES_STORAGE_DEPRECATED_MSG, RemovedInDjango51Warning)
|
warnings.warn(STATICFILES_STORAGE_DEPRECATED_MSG, RemovedInDjango51Warning)
|
||||||
super().__setattr__(name, value)
|
super().__setattr__(name, value)
|
||||||
if name == "USE_DEPRECATED_PYTZ":
|
|
||||||
warnings.warn(USE_DEPRECATED_PYTZ_DEPRECATED_MSG, RemovedInDjango50Warning)
|
|
||||||
# RemovedInDjango51Warning.
|
# RemovedInDjango51Warning.
|
||||||
if name == "STORAGES":
|
if name == "STORAGES":
|
||||||
self.STORAGES.setdefault(
|
self.STORAGES.setdefault(
|
||||||
|
|
|
@ -43,11 +43,6 @@ TIME_ZONE = "America/Chicago"
|
||||||
# If you set this to True, Django will use timezone-aware datetimes.
|
# If you set this to True, Django will use timezone-aware datetimes.
|
||||||
USE_TZ = True
|
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:
|
# Language code for this installation. All choices can be found here:
|
||||||
# http://www.i18nguy.com/unicode/language-identifiers.html
|
# http://www.i18nguy.com/unicode/language-identifiers.html
|
||||||
LANGUAGE_CODE = "en-us"
|
LANGUAGE_CODE = "en-us"
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
|
from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
|
||||||
from django.contrib.admin.utils import (
|
from django.contrib.admin.utils import (
|
||||||
display_for_field,
|
display_for_field,
|
||||||
|
@ -357,10 +356,8 @@ def date_hierarchy(cl):
|
||||||
field = get_fields_from_path(cl.model, field_name)[-1]
|
field = get_fields_from_path(cl.model, field_name)[-1]
|
||||||
if isinstance(field, models.DateTimeField):
|
if isinstance(field, models.DateTimeField):
|
||||||
dates_or_datetimes = "datetimes"
|
dates_or_datetimes = "datetimes"
|
||||||
qs_kwargs = {"is_dst": True} if settings.USE_DEPRECATED_PYTZ else {}
|
|
||||||
else:
|
else:
|
||||||
dates_or_datetimes = "dates"
|
dates_or_datetimes = "dates"
|
||||||
qs_kwargs = {}
|
|
||||||
year_field = "%s__year" % field_name
|
year_field = "%s__year" % field_name
|
||||||
month_field = "%s__month" % field_name
|
month_field = "%s__month" % field_name
|
||||||
day_field = "%s__day" % field_name
|
day_field = "%s__day" % field_name
|
||||||
|
@ -401,9 +398,7 @@ def date_hierarchy(cl):
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
elif year_lookup and month_lookup:
|
elif year_lookup and month_lookup:
|
||||||
days = getattr(cl.queryset, dates_or_datetimes)(
|
days = getattr(cl.queryset, dates_or_datetimes)(field_name, "day")
|
||||||
field_name, "day", **qs_kwargs
|
|
||||||
)
|
|
||||||
return {
|
return {
|
||||||
"show": True,
|
"show": True,
|
||||||
"back": {
|
"back": {
|
||||||
|
@ -425,9 +420,7 @@ def date_hierarchy(cl):
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
elif year_lookup:
|
elif year_lookup:
|
||||||
months = getattr(cl.queryset, dates_or_datetimes)(
|
months = getattr(cl.queryset, dates_or_datetimes)(field_name, "month")
|
||||||
field_name, "month", **qs_kwargs
|
|
||||||
)
|
|
||||||
return {
|
return {
|
||||||
"show": True,
|
"show": True,
|
||||||
"back": {"link": link({}), "title": _("All dates")},
|
"back": {"link": link({}), "title": _("All dates")},
|
||||||
|
@ -444,9 +437,7 @@ def date_hierarchy(cl):
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
years = getattr(cl.queryset, dates_or_datetimes)(
|
years = getattr(cl.queryset, dates_or_datetimes)(field_name, "year")
|
||||||
field_name, "year", **qs_kwargs
|
|
||||||
)
|
|
||||||
return {
|
return {
|
||||||
"show": True,
|
"show": True,
|
||||||
"back": None,
|
"back": None,
|
||||||
|
|
|
@ -32,15 +32,6 @@ RAN_DB_VERSION_CHECK = set()
|
||||||
logger = logging.getLogger("django.db.backends.base")
|
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:
|
class BaseDatabaseWrapper:
|
||||||
"""Represent a database connection."""
|
"""Represent a database connection."""
|
||||||
|
|
||||||
|
@ -166,7 +157,7 @@ class BaseDatabaseWrapper:
|
||||||
elif self.settings_dict["TIME_ZONE"] is None:
|
elif self.settings_dict["TIME_ZONE"] is None:
|
||||||
return datetime.timezone.utc
|
return datetime.timezone.utc
|
||||||
else:
|
else:
|
||||||
return timezone_constructor(self.settings_dict["TIME_ZONE"])
|
return zoneinfo.ZoneInfo(self.settings_dict["TIME_ZONE"])
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def timezone_name(self):
|
def timezone_name(self):
|
||||||
|
|
|
@ -26,7 +26,6 @@ from math import (
|
||||||
)
|
)
|
||||||
from re import search as re_search
|
from re import search as re_search
|
||||||
|
|
||||||
from django.db.backends.base.base import timezone_constructor
|
|
||||||
from django.db.backends.utils import (
|
from django.db.backends.utils import (
|
||||||
split_tzname_delta,
|
split_tzname_delta,
|
||||||
typecast_time,
|
typecast_time,
|
||||||
|
@ -36,6 +35,11 @@ from django.utils import timezone
|
||||||
from django.utils.crypto import md5
|
from django.utils.crypto import md5
|
||||||
from django.utils.duration import duration_microseconds
|
from django.utils.duration import duration_microseconds
|
||||||
|
|
||||||
|
try:
|
||||||
|
import zoneinfo
|
||||||
|
except ImportError:
|
||||||
|
from backports import zoneinfo
|
||||||
|
|
||||||
|
|
||||||
def register(connection):
|
def register(connection):
|
||||||
create_deterministic_function = functools.partial(
|
create_deterministic_function = functools.partial(
|
||||||
|
@ -111,14 +115,14 @@ def _sqlite_datetime_parse(dt, tzname=None, conn_tzname=None):
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
return None
|
return None
|
||||||
if conn_tzname:
|
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:
|
if tzname is not None and tzname != conn_tzname:
|
||||||
tzname, sign, offset = split_tzname_delta(tzname)
|
tzname, sign, offset = split_tzname_delta(tzname)
|
||||||
if offset:
|
if offset:
|
||||||
hours, minutes = offset.split(":")
|
hours, minutes = offset.split(":")
|
||||||
offset_delta = timedelta(hours=int(hours), minutes=int(minutes))
|
offset_delta = timedelta(hours=int(hours), minutes=int(minutes))
|
||||||
dt += offset_delta if sign == "+" else -offset_delta
|
dt += offset_delta if sign == "+" else -offset_delta
|
||||||
dt = timezone.localtime(dt, timezone_constructor(tzname))
|
dt = timezone.localtime(dt, zoneinfo.ZoneInfo(tzname))
|
||||||
return dt
|
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):
|
if settings.USE_TZ and value is not None and timezone.is_naive(value):
|
||||||
current_timezone = timezone.get_current_timezone()
|
current_timezone = timezone.get_current_timezone()
|
||||||
try:
|
try:
|
||||||
if not timezone._is_pytz_zone(
|
if timezone._datetime_ambiguous_or_imaginary(value, current_timezone):
|
||||||
current_timezone
|
|
||||||
) and timezone._datetime_ambiguous_or_imaginary(value, current_timezone):
|
|
||||||
raise ValueError("Ambiguous or non-existent time.")
|
raise ValueError("Ambiguous or non-existent time.")
|
||||||
return timezone.make_aware(value, current_timezone)
|
return timezone.make_aware(value, current_timezone)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
|
|
@ -7,34 +7,12 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from backports import zoneinfo
|
from backports import zoneinfo
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.template import Library, Node, TemplateSyntaxError
|
from django.template import Library, Node, TemplateSyntaxError
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
register = Library()
|
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
|
# HACK: datetime instances cannot be assigned new attributes. Define a subclass
|
||||||
# in order to define new attributes in do_timezone().
|
# in order to define new attributes in do_timezone().
|
||||||
class datetimeobject(datetime):
|
class datetimeobject(datetime):
|
||||||
|
@ -79,8 +57,7 @@ def do_timezone(value, arg):
|
||||||
if timezone.is_naive(value):
|
if timezone.is_naive(value):
|
||||||
default_timezone = timezone.get_default_timezone()
|
default_timezone = timezone.get_default_timezone()
|
||||||
value = timezone.make_aware(value, default_timezone)
|
value = timezone.make_aware(value, default_timezone)
|
||||||
# Filters must never raise exceptions, and pytz' exceptions inherit
|
# Filters must never raise exceptionsm, so catch everything.
|
||||||
# Exception directly, not a specific subclass. So catch everything.
|
|
||||||
except Exception:
|
except Exception:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
@ -89,8 +66,8 @@ def do_timezone(value, arg):
|
||||||
tz = arg
|
tz = arg
|
||||||
elif isinstance(arg, str):
|
elif isinstance(arg, str):
|
||||||
try:
|
try:
|
||||||
tz = timezone_constructor(arg)
|
tz = zoneinfo.ZoneInfo(arg)
|
||||||
except UnknownTimezoneException:
|
except zoneinfo.ZoneInfoNotFoundError:
|
||||||
return ""
|
return ""
|
||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
|
|
|
@ -3,7 +3,6 @@ Timezone-related classes and functions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import functools
|
import functools
|
||||||
import sys
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -75,10 +74,6 @@ def get_default_timezone():
|
||||||
|
|
||||||
This is the time zone defined by settings.TIME_ZONE.
|
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)
|
return zoneinfo.ZoneInfo(settings.TIME_ZONE)
|
||||||
|
|
||||||
|
|
||||||
|
@ -125,11 +120,6 @@ def activate(timezone):
|
||||||
if isinstance(timezone, tzinfo):
|
if isinstance(timezone, tzinfo):
|
||||||
_active.value = timezone
|
_active.value = timezone
|
||||||
elif isinstance(timezone, str):
|
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:
|
else:
|
||||||
raise ValueError("Invalid timezone: %r" % timezone)
|
raise ValueError("Invalid timezone: %r" % timezone)
|
||||||
|
@ -282,10 +272,6 @@ def make_aware(value, timezone=None, is_dst=NOT_PASSED):
|
||||||
)
|
)
|
||||||
if timezone is None:
|
if timezone is None:
|
||||||
timezone = get_current_timezone()
|
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.
|
# Check that we won't overwrite the timezone of an aware datetime.
|
||||||
if is_aware(value):
|
if is_aware(value):
|
||||||
raise ValueError("make_aware expects a naive datetime, got %s" % value)
|
raise ValueError("make_aware expects a naive datetime, got %s" % value)
|
||||||
|
@ -303,53 +289,7 @@ def make_naive(value, timezone=None):
|
||||||
return value.astimezone(timezone).replace(tzinfo=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):
|
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)
|
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
|
.. _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
|
.. setting:: USE_I18N
|
||||||
|
|
||||||
``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``
|
<DATABASE-TIME_ZONE>` setting, you will need to audit your code, since ``pytz``
|
||||||
and ``zoneinfo`` are not entirely equivalent.
|
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 allows continued use of ``pytz`` during the 4.x release cycle. This
|
||||||
setting will be removed in Django 5.0.
|
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
|
provides shims to help you safely remove ``pytz``, and has a detailed
|
||||||
`migration guide`_ showing how to move to the new ``zoneinfo`` APIs.
|
`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.
|
transitional setting is recommended if you need a gradual update path.
|
||||||
|
|
||||||
.. _pytz_deprecation_shim: https://pytz-deprecation-shim.readthedocs.io/en/latest/index.html
|
.. _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_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
|
See :ref:`deprecated-features-4.1` for details on these changes, including how
|
||||||
to remove usage of these features.
|
to remove usage of these features.
|
||||||
|
|
||||||
|
|
|
@ -677,5 +677,3 @@ Usage
|
||||||
:func:`zoneinfo.available_timezones` provides the set of all valid keys for
|
:func:`zoneinfo.available_timezones` provides the set of all valid keys for
|
||||||
IANA time zones available to your system. See the docs for usage
|
IANA time zones available to your system. See the docs for usage
|
||||||
considerations.
|
considerations.
|
||||||
|
|
||||||
.. _pytz: http://pytz.sourceforge.net/
|
|
||||||
|
|
|
@ -10,11 +10,6 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from backports import zoneinfo
|
from backports import zoneinfo
|
||||||
|
|
||||||
try:
|
|
||||||
import pytz
|
|
||||||
except ImportError:
|
|
||||||
pytz = None
|
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.contrib.admin import AdminSite, ModelAdmin
|
from django.contrib.admin import AdminSite, ModelAdmin
|
||||||
from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME
|
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."""
|
"""Makes one aware datetime for each supported time zone provider."""
|
||||||
yield dt.replace(tzinfo=zoneinfo.ZoneInfo(iana_key))
|
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:
|
class AdminFieldExtractionMixin:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,14 +1,7 @@
|
||||||
import datetime
|
import datetime
|
||||||
import unittest
|
|
||||||
|
|
||||||
try:
|
from django.test import TestCase, override_settings
|
||||||
import pytz
|
|
||||||
except ImportError:
|
|
||||||
pytz = None
|
|
||||||
|
|
||||||
from django.test import TestCase, ignore_warnings, override_settings
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.deprecation import RemovedInDjango50Warning
|
|
||||||
|
|
||||||
from .models import Article, Category, Comment
|
from .models import Article, Category, Comment
|
||||||
|
|
||||||
|
@ -102,46 +95,6 @@ class DateTimesTests(TestCase):
|
||||||
qs = Article.objects.datetimes("pub_date", "second")
|
qs = Article.objects.datetimes("pub_date", "second")
|
||||||
self.assertEqual(qs[0], now)
|
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):
|
def test_datetimes_returns_available_dates_for_given_scope_and_given_field(self):
|
||||||
pub_dates = [
|
pub_dates = [
|
||||||
datetime.datetime(2005, 7, 28, 12, 15),
|
datetime.datetime(2005, 7, 28, 12, 15),
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import unittest
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from datetime import timezone as datetime_timezone
|
from datetime import timezone as datetime_timezone
|
||||||
|
|
||||||
|
@ -7,11 +6,6 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from backports import zoneinfo
|
from backports import zoneinfo
|
||||||
|
|
||||||
try:
|
|
||||||
import pytz
|
|
||||||
except ImportError:
|
|
||||||
pytz = None
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import DataError, OperationalError
|
from django.db import DataError, OperationalError
|
||||||
from django.db.models import (
|
from django.db.models import (
|
||||||
|
@ -51,29 +45,14 @@ from django.db.models.functions import (
|
||||||
)
|
)
|
||||||
from django.test import (
|
from django.test import (
|
||||||
TestCase,
|
TestCase,
|
||||||
ignore_warnings,
|
|
||||||
override_settings,
|
override_settings,
|
||||||
skipIfDBFeature,
|
skipIfDBFeature,
|
||||||
skipUnlessDBFeature,
|
skipUnlessDBFeature,
|
||||||
)
|
)
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.deprecation import RemovedInDjango50Warning
|
|
||||||
|
|
||||||
from ..models import Author, DTModel, Fan
|
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):
|
def truncate_to(value, kind, tzinfo=None):
|
||||||
# Convert to target timezone before truncation
|
# Convert to target timezone before truncation
|
||||||
|
@ -1690,10 +1669,6 @@ class DateFunctionTests(TestCase):
|
||||||
|
|
||||||
@override_settings(USE_TZ=True, TIME_ZONE="UTC")
|
@override_settings(USE_TZ=True, TIME_ZONE="UTC")
|
||||||
class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
||||||
def get_timezones(self, key):
|
|
||||||
for constructor in ZONE_CONSTRUCTORS:
|
|
||||||
yield constructor(key)
|
|
||||||
|
|
||||||
def test_extract_func_with_timezone(self):
|
def test_extract_func_with_timezone(self):
|
||||||
start_datetime = datetime(2015, 6, 15, 23, 30, 1, 321)
|
start_datetime = datetime(2015, 6, 15, 23, 30, 1, 321)
|
||||||
end_datetime = datetime(2015, 6, 16, 13, 11, 27, 123)
|
end_datetime = datetime(2015, 6, 16, 13, 11, 27, 123)
|
||||||
|
@ -1702,9 +1677,8 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
||||||
self.create_model(start_datetime, end_datetime)
|
self.create_model(start_datetime, end_datetime)
|
||||||
delta_tzinfo_pos = datetime_timezone(timedelta(hours=5))
|
delta_tzinfo_pos = datetime_timezone(timedelta(hours=5))
|
||||||
delta_tzinfo_neg = datetime_timezone(timedelta(hours=-5, minutes=17))
|
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(
|
qs = DTModel.objects.annotate(
|
||||||
day=Extract("start_datetime", "day"),
|
day=Extract("start_datetime", "day"),
|
||||||
day_melb=Extract("start_datetime", "day", tzinfo=melb),
|
day_melb=Extract("start_datetime", "day", tzinfo=melb),
|
||||||
|
@ -1717,12 +1691,8 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
||||||
quarter=ExtractQuarter("start_datetime", tzinfo=melb),
|
quarter=ExtractQuarter("start_datetime", tzinfo=melb),
|
||||||
hour=ExtractHour("start_datetime"),
|
hour=ExtractHour("start_datetime"),
|
||||||
hour_melb=ExtractHour("start_datetime", tzinfo=melb),
|
hour_melb=ExtractHour("start_datetime", tzinfo=melb),
|
||||||
hour_with_delta_pos=ExtractHour(
|
hour_with_delta_pos=ExtractHour("start_datetime", tzinfo=delta_tzinfo_pos),
|
||||||
"start_datetime", tzinfo=delta_tzinfo_pos
|
hour_with_delta_neg=ExtractHour("start_datetime", tzinfo=delta_tzinfo_neg),
|
||||||
),
|
|
||||||
hour_with_delta_neg=ExtractHour(
|
|
||||||
"start_datetime", tzinfo=delta_tzinfo_neg
|
|
||||||
),
|
|
||||||
minute_with_delta_neg=ExtractMinute(
|
minute_with_delta_neg=ExtractMinute(
|
||||||
"start_datetime", tzinfo=delta_tzinfo_neg
|
"start_datetime", tzinfo=delta_tzinfo_neg
|
||||||
),
|
),
|
||||||
|
@ -1765,8 +1735,8 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
||||||
start_datetime = timezone.make_aware(start_datetime)
|
start_datetime = timezone.make_aware(start_datetime)
|
||||||
end_datetime = timezone.make_aware(end_datetime)
|
end_datetime = timezone.make_aware(end_datetime)
|
||||||
self.create_model(start_datetime, end_datetime)
|
self.create_model(start_datetime, end_datetime)
|
||||||
for ust_nera in self.get_timezones("Asia/Ust-Nera"):
|
ust_nera = zoneinfo.ZoneInfo("Asia/Ust-Nera")
|
||||||
with self.subTest(repr(ust_nera)):
|
|
||||||
qs = DTModel.objects.annotate(
|
qs = DTModel.objects.annotate(
|
||||||
hour=ExtractHour("start_datetime"),
|
hour=ExtractHour("start_datetime"),
|
||||||
hour_tz=ExtractHour("start_datetime", tzinfo=ust_nera),
|
hour_tz=ExtractHour("start_datetime", tzinfo=ust_nera),
|
||||||
|
@ -1788,9 +1758,7 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
||||||
start_datetime = timezone.make_aware(start_datetime)
|
start_datetime = timezone.make_aware(start_datetime)
|
||||||
end_datetime = timezone.make_aware(end_datetime)
|
end_datetime = timezone.make_aware(end_datetime)
|
||||||
self.create_model(start_datetime, end_datetime)
|
self.create_model(start_datetime, end_datetime)
|
||||||
|
melb = zoneinfo.ZoneInfo("Australia/Melbourne")
|
||||||
for melb in self.get_timezones("Australia/Melbourne"):
|
|
||||||
with self.subTest(repr(melb)):
|
|
||||||
with timezone.override(melb):
|
with timezone.override(melb):
|
||||||
model = (
|
model = (
|
||||||
DTModel.objects.annotate(
|
DTModel.objects.annotate(
|
||||||
|
@ -1806,8 +1774,7 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
||||||
self.assertEqual(model.day_utc, 15)
|
self.assertEqual(model.day_utc, 15)
|
||||||
|
|
||||||
def test_extract_invalid_field_with_timezone(self):
|
def test_extract_invalid_field_with_timezone(self):
|
||||||
for melb in self.get_timezones("Australia/Melbourne"):
|
melb = zoneinfo.ZoneInfo("Australia/Melbourne")
|
||||||
with self.subTest(repr(melb)):
|
|
||||||
msg = "tzinfo can only be used with DateTimeField."
|
msg = "tzinfo can only be used with DateTimeField."
|
||||||
with self.assertRaisesMessage(ValueError, msg):
|
with self.assertRaisesMessage(ValueError, msg):
|
||||||
DTModel.objects.annotate(
|
DTModel.objects.annotate(
|
||||||
|
@ -1824,12 +1791,9 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
||||||
start_datetime = timezone.make_aware(start_datetime)
|
start_datetime = timezone.make_aware(start_datetime)
|
||||||
end_datetime = timezone.make_aware(end_datetime)
|
end_datetime = timezone.make_aware(end_datetime)
|
||||||
self.create_model(start_datetime, 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 = (
|
model = (
|
||||||
DTModel.objects.annotate(
|
DTModel.objects.annotate(
|
||||||
melb_year=TruncYear("start_datetime", tzinfo=melb),
|
melb_year=TruncYear("start_datetime", tzinfo=melb),
|
||||||
|
@ -1846,9 +1810,7 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
||||||
melb_start_datetime = start_datetime.astimezone(melb)
|
melb_start_datetime = start_datetime.astimezone(melb)
|
||||||
pacific_start_datetime = start_datetime.astimezone(pacific)
|
pacific_start_datetime = start_datetime.astimezone(pacific)
|
||||||
self.assertEqual(model.start_datetime, start_datetime)
|
self.assertEqual(model.start_datetime, start_datetime)
|
||||||
self.assertEqual(
|
self.assertEqual(model.melb_year, truncate_to(start_datetime, "year", melb))
|
||||||
model.melb_year, truncate_to(start_datetime, "year", melb)
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
model.pacific_year, truncate_to(start_datetime, "year", pacific)
|
model.pacific_year, truncate_to(start_datetime, "year", pacific)
|
||||||
)
|
)
|
||||||
|
@ -1860,39 +1822,6 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
||||||
self.assertEqual(model.melb_time, melb_start_datetime.time())
|
self.assertEqual(model.melb_time, melb_start_datetime.time())
|
||||||
self.assertEqual(model.pacific_time, pacific_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))
|
|
||||||
|
|
||||||
def test_trunc_func_with_timezone(self):
|
def test_trunc_func_with_timezone(self):
|
||||||
"""
|
"""
|
||||||
If the truncated datetime transitions to a different offset (daylight
|
If the truncated datetime transitions to a different offset (daylight
|
||||||
|
@ -1904,9 +1833,7 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
||||||
end_datetime = timezone.make_aware(end_datetime)
|
end_datetime = timezone.make_aware(end_datetime)
|
||||||
self.create_model(start_datetime, end_datetime)
|
self.create_model(start_datetime, end_datetime)
|
||||||
self.create_model(end_datetime, start_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):
|
def test_datetime_kind(kind):
|
||||||
self.assertQuerySetEqual(
|
self.assertQuerySetEqual(
|
||||||
|
@ -1921,9 +1848,7 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
start_datetime,
|
start_datetime,
|
||||||
truncate_to(
|
truncate_to(start_datetime.astimezone(melb), kind, melb),
|
||||||
start_datetime.astimezone(melb), kind, melb
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
end_datetime,
|
end_datetime,
|
||||||
|
@ -1946,9 +1871,7 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
start_datetime,
|
start_datetime,
|
||||||
truncate_to(
|
truncate_to(start_datetime.astimezone(melb).date(), kind),
|
||||||
start_datetime.astimezone(melb).date(), kind
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
end_datetime,
|
end_datetime,
|
||||||
|
@ -1971,9 +1894,7 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
start_datetime,
|
start_datetime,
|
||||||
truncate_to(
|
truncate_to(start_datetime.astimezone(melb).time(), kind),
|
||||||
start_datetime.astimezone(melb).time(), kind
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
end_datetime,
|
end_datetime,
|
||||||
|
@ -2008,8 +1929,7 @@ class DateFunctionWithTimeZoneTests(DateFunctionTests):
|
||||||
self.assertEqual(qs.count(), 2)
|
self.assertEqual(qs.count(), 2)
|
||||||
|
|
||||||
def test_trunc_invalid_field_with_timezone(self):
|
def test_trunc_invalid_field_with_timezone(self):
|
||||||
for melb in self.get_timezones("Australia/Melbourne"):
|
melb = zoneinfo.ZoneInfo("Australia/Melbourne")
|
||||||
with self.subTest(repr(melb)):
|
|
||||||
msg = "tzinfo can only be used with DateTimeField."
|
msg = "tzinfo can only be used with DateTimeField."
|
||||||
with self.assertRaisesMessage(ValueError, msg):
|
with self.assertRaisesMessage(ValueError, msg):
|
||||||
DTModel.objects.annotate(
|
DTModel.objects.annotate(
|
||||||
|
|
|
@ -15,11 +15,6 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from backports import zoneinfo
|
from backports import zoneinfo
|
||||||
|
|
||||||
try:
|
|
||||||
import pytz
|
|
||||||
except ImportError:
|
|
||||||
pytz = None
|
|
||||||
|
|
||||||
import custom_migration_operations.more_operations
|
import custom_migration_operations.more_operations
|
||||||
import custom_migration_operations.operations
|
import custom_migration_operations.operations
|
||||||
|
|
||||||
|
@ -595,16 +590,6 @@ class WriterTests(SimpleTestCase):
|
||||||
{"import datetime"},
|
{"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):
|
def test_serialize_fields(self):
|
||||||
self.assertSerializedFieldEqual(models.CharField(max_length=255))
|
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/libmemcached can't be built on Windows.
|
||||||
pylibmc; sys.platform != 'win32'
|
pylibmc; sys.platform != 'win32'
|
||||||
pymemcache >= 3.4.0
|
pymemcache >= 3.4.0
|
||||||
pytz
|
|
||||||
pywatchman; sys.platform != 'win32'
|
pywatchman; sys.platform != 'win32'
|
||||||
PyYAML
|
PyYAML
|
||||||
redis >= 3.4.0
|
redis >= 3.4.0
|
||||||
|
|
|
@ -4,13 +4,7 @@ import unittest
|
||||||
from types import ModuleType, SimpleNamespace
|
from types import ModuleType, SimpleNamespace
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from django.conf import (
|
from django.conf import ENVIRONMENT_VARIABLE, LazySettings, Settings, settings
|
||||||
ENVIRONMENT_VARIABLE,
|
|
||||||
USE_DEPRECATED_PYTZ_DEPRECATED_MSG,
|
|
||||||
LazySettings,
|
|
||||||
Settings,
|
|
||||||
settings,
|
|
||||||
)
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
from django.test import (
|
from django.test import (
|
||||||
|
@ -23,7 +17,6 @@ from django.test import (
|
||||||
)
|
)
|
||||||
from django.test.utils import requires_tz_support
|
from django.test.utils import requires_tz_support
|
||||||
from django.urls import clear_script_prefix, set_script_prefix
|
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"]})
|
@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"):
|
with self.assertRaisesMessage(ValueError, "Incorrect timezone setting: test"):
|
||||||
settings._setup()
|
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):
|
class TestComplexSettingOverride(SimpleTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
|
@ -10,11 +10,6 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from backports import zoneinfo
|
from backports import zoneinfo
|
||||||
|
|
||||||
try:
|
|
||||||
import pytz
|
|
||||||
except ImportError:
|
|
||||||
pytz = None
|
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core import serializers
|
from django.core import serializers
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
|
@ -31,7 +26,6 @@ from django.test import (
|
||||||
SimpleTestCase,
|
SimpleTestCase,
|
||||||
TestCase,
|
TestCase,
|
||||||
TransactionTestCase,
|
TransactionTestCase,
|
||||||
ignore_warnings,
|
|
||||||
override_settings,
|
override_settings,
|
||||||
skipIfDBFeature,
|
skipIfDBFeature,
|
||||||
skipUnlessDBFeature,
|
skipUnlessDBFeature,
|
||||||
|
@ -79,14 +73,6 @@ UTC = datetime.timezone.utc
|
||||||
EAT = timezone.get_fixed_timezone(180) # Africa/Nairobi
|
EAT = timezone.get_fixed_timezone(180) # Africa/Nairobi
|
||||||
ICT = timezone.get_fixed_timezone(420) # Asia/Bangkok
|
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):
|
class UTCAliasTests(SimpleTestCase):
|
||||||
def test_alias_deprecation_warning(self):
|
def test_alias_deprecation_warning(self):
|
||||||
|
@ -413,9 +399,8 @@ class NewDatabaseTests(TestCase):
|
||||||
self.assertEqual(Event.objects.filter(dt__gte=dt2).count(), 1)
|
self.assertEqual(Event.objects.filter(dt__gte=dt2).count(), 1)
|
||||||
self.assertEqual(Event.objects.filter(dt__gt=dt2).count(), 0)
|
self.assertEqual(Event.objects.filter(dt__gt=dt2).count(), 0)
|
||||||
|
|
||||||
def test_query_filter_with_pytz_timezones(self):
|
def test_query_filter_with_timezones(self):
|
||||||
for tz in get_timezones("Europe/Paris"):
|
tz = zoneinfo.ZoneInfo("Europe/Paris")
|
||||||
with self.subTest(repr(tz)):
|
|
||||||
dt = datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=tz)
|
dt = datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=tz)
|
||||||
Event.objects.create(dt=dt)
|
Event.objects.create(dt=dt)
|
||||||
next = dt + datetime.timedelta(seconds=3)
|
next = dt + datetime.timedelta(seconds=3)
|
||||||
|
@ -423,29 +408,8 @@ class NewDatabaseTests(TestCase):
|
||||||
self.assertEqual(Event.objects.filter(dt__exact=dt).count(), 1)
|
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__exact=next).count(), 0)
|
||||||
self.assertEqual(Event.objects.filter(dt__in=(prev, next)).count(), 0)
|
self.assertEqual(Event.objects.filter(dt__in=(prev, next)).count(), 0)
|
||||||
self.assertEqual(
|
self.assertEqual(Event.objects.filter(dt__in=(prev, dt, next)).count(), 1)
|
||||||
Event.objects.filter(dt__in=(prev, dt, next)).count(), 1
|
self.assertEqual(Event.objects.filter(dt__range=(prev, 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_convert_timezones(self):
|
def test_query_convert_timezones(self):
|
||||||
# Connection timezone is equal to the current timezone, datetime
|
# Connection timezone is equal to the current timezone, datetime
|
||||||
|
@ -1075,8 +1039,7 @@ class TemplateTests(SimpleTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Use an IANA timezone as argument
|
# Use an IANA timezone as argument
|
||||||
for tz in get_timezones("Europe/Paris"):
|
tz = zoneinfo.ZoneInfo("Europe/Paris")
|
||||||
with self.subTest(repr(tz)):
|
|
||||||
tpl = Template("{% load tz %}{{ dt|timezone:tz }}")
|
tpl = Template("{% load tz %}{{ dt|timezone:tz }}")
|
||||||
ctx = Context(
|
ctx = Context(
|
||||||
{
|
{
|
||||||
|
@ -1147,8 +1110,7 @@ class TemplateTests(SimpleTestCase):
|
||||||
tpl = Template("{% load tz %}{% timezone tz %}{{ dt }}{% endtimezone %}")
|
tpl = Template("{% load tz %}{% timezone tz %}{{ dt }}{% endtimezone %}")
|
||||||
|
|
||||||
# Use a IANA timezone as argument
|
# Use a IANA timezone as argument
|
||||||
for tz in get_timezones("Europe/Paris"):
|
tz = zoneinfo.ZoneInfo("Europe/Paris")
|
||||||
with self.subTest(repr(tz)):
|
|
||||||
ctx = Context(
|
ctx = Context(
|
||||||
{
|
{
|
||||||
"dt": datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT),
|
"dt": datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT),
|
||||||
|
@ -1166,22 +1128,6 @@ class TemplateTests(SimpleTestCase):
|
||||||
)
|
)
|
||||||
self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00")
|
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")
|
@skipIf(sys.platform == "win32", "Windows uses non-standard time zone names")
|
||||||
def test_get_current_timezone_templatetag(self):
|
def test_get_current_timezone_templatetag(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1205,14 +1151,10 @@ class TemplateTests(SimpleTestCase):
|
||||||
self.assertEqual(tpl.render(Context({"tz": ICT})), "+0700")
|
self.assertEqual(tpl.render(Context({"tz": ICT})), "+0700")
|
||||||
|
|
||||||
def test_get_current_timezone_templatetag_with_iana(self):
|
def test_get_current_timezone_templatetag_with_iana(self):
|
||||||
"""
|
|
||||||
Test the {% get_current_timezone %} templatetag with pytz.
|
|
||||||
"""
|
|
||||||
tpl = Template(
|
tpl = Template(
|
||||||
"{% load tz %}{% get_current_timezone as time_zone %}{{ time_zone }}"
|
"{% load tz %}{% get_current_timezone as time_zone %}{{ time_zone }}"
|
||||||
)
|
)
|
||||||
for tz in get_timezones("Europe/Paris"):
|
tz = zoneinfo.ZoneInfo("Europe/Paris")
|
||||||
with self.subTest(repr(tz)):
|
|
||||||
with timezone.override(tz):
|
with timezone.override(tz):
|
||||||
self.assertEqual(tpl.render(Context()), "Europe/Paris")
|
self.assertEqual(tpl.render(Context()), "Europe/Paris")
|
||||||
|
|
||||||
|
@ -1282,8 +1224,7 @@ class LegacyFormsTests(TestCase):
|
||||||
|
|
||||||
def test_form_with_non_existent_time(self):
|
def test_form_with_non_existent_time(self):
|
||||||
form = EventForm({"dt": "2011-03-27 02:30:00"})
|
form = EventForm({"dt": "2011-03-27 02:30:00"})
|
||||||
for tz in get_timezones("Europe/Paris"):
|
tz = zoneinfo.ZoneInfo("Europe/Paris")
|
||||||
with self.subTest(repr(tz)):
|
|
||||||
with timezone.override(tz):
|
with timezone.override(tz):
|
||||||
# This is a bug.
|
# This is a bug.
|
||||||
self.assertTrue(form.is_valid())
|
self.assertTrue(form.is_valid())
|
||||||
|
@ -1294,8 +1235,7 @@ class LegacyFormsTests(TestCase):
|
||||||
|
|
||||||
def test_form_with_ambiguous_time(self):
|
def test_form_with_ambiguous_time(self):
|
||||||
form = EventForm({"dt": "2011-10-30 02:30:00"})
|
form = EventForm({"dt": "2011-10-30 02:30:00"})
|
||||||
for tz in get_timezones("Europe/Paris"):
|
tz = zoneinfo.ZoneInfo("Europe/Paris")
|
||||||
with self.subTest(repr(tz)):
|
|
||||||
with timezone.override(tz):
|
with timezone.override(tz):
|
||||||
# This is a bug.
|
# This is a bug.
|
||||||
self.assertTrue(form.is_valid())
|
self.assertTrue(form.is_valid())
|
||||||
|
@ -1338,8 +1278,7 @@ class NewFormsTests(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_form_with_non_existent_time(self):
|
def test_form_with_non_existent_time(self):
|
||||||
for tz in get_timezones("Europe/Paris"):
|
tz = zoneinfo.ZoneInfo("Europe/Paris")
|
||||||
with self.subTest(repr(tz)):
|
|
||||||
with timezone.override(tz):
|
with timezone.override(tz):
|
||||||
form = EventForm({"dt": "2011-03-27 02:30:00"})
|
form = EventForm({"dt": "2011-03-27 02:30:00"})
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
|
@ -1352,8 +1291,7 @@ class NewFormsTests(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_form_with_ambiguous_time(self):
|
def test_form_with_ambiguous_time(self):
|
||||||
for tz in get_timezones("Europe/Paris"):
|
tz = zoneinfo.ZoneInfo("Europe/Paris")
|
||||||
with self.subTest(repr(tz)):
|
|
||||||
with timezone.override(tz):
|
with timezone.override(tz):
|
||||||
form = EventForm({"dt": "2011-10-30 02:30:00"})
|
form = EventForm({"dt": "2011-10-30 02:30:00"})
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
|
|
|
@ -25,8 +25,7 @@ class DateFormatTests(SimpleTestCase):
|
||||||
self.assertEqual(datetime.fromtimestamp(int(format(dt, "U"))), dt)
|
self.assertEqual(datetime.fromtimestamp(int(format(dt, "U"))), dt)
|
||||||
|
|
||||||
def test_naive_ambiguous_datetime(self):
|
def test_naive_ambiguous_datetime(self):
|
||||||
# dt is ambiguous in Europe/Copenhagen. pytz raises an exception for
|
# dt is ambiguous in Europe/Copenhagen.
|
||||||
# the ambiguity, which results in an empty string.
|
|
||||||
dt = datetime(2015, 10, 25, 2, 30, 0)
|
dt = datetime(2015, 10, 25, 2, 30, 0)
|
||||||
|
|
||||||
# Try all formatters that involve self.timezone.
|
# Try all formatters that involve self.timezone.
|
||||||
|
|
|
@ -1,18 +1,12 @@
|
||||||
import datetime
|
import datetime
|
||||||
import unittest
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
try:
|
|
||||||
import pytz
|
|
||||||
except ImportError:
|
|
||||||
pytz = None
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import zoneinfo
|
import zoneinfo
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from backports import zoneinfo
|
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 import timezone
|
||||||
from django.utils.deprecation import RemovedInDjango50Warning
|
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
|
ICT = timezone.get_fixed_timezone(420) # Asia/Bangkok
|
||||||
UTC = datetime.timezone.utc
|
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):
|
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):
|
def test_default_timezone_is_zoneinfo(self):
|
||||||
self.assertIsInstance(timezone.get_default_timezone(), zoneinfo.ZoneInfo)
|
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):
|
def test_now(self):
|
||||||
with override_settings(USE_TZ=True):
|
with override_settings(USE_TZ=True):
|
||||||
self.assertTrue(timezone.is_aware(timezone.now()))
|
self.assertTrue(timezone.is_aware(timezone.now()))
|
||||||
|
@ -208,46 +175,15 @@ class TimezoneTests(SimpleTestCase):
|
||||||
|
|
||||||
def test_make_aware2(self):
|
def test_make_aware2(self):
|
||||||
CEST = datetime.timezone(datetime.timedelta(hours=2), "CEST")
|
CEST = datetime.timezone(datetime.timedelta(hours=2), "CEST")
|
||||||
for tz in PARIS_IMPLS:
|
|
||||||
with self.subTest(repr(tz)):
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
timezone.make_aware(datetime.datetime(2011, 9, 1, 12, 20, 30), tz),
|
timezone.make_aware(datetime.datetime(2011, 9, 1, 12, 20, 30), PARIS_ZI),
|
||||||
datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=CEST),
|
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
|
|
||||||
)
|
|
||||||
|
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
timezone.make_aware(
|
timezone.make_aware(
|
||||||
datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=PARIS_ZI), PARIS_ZI
|
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):
|
def test_make_naive_zoneinfo(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
timezone.make_naive(
|
timezone.make_naive(
|
||||||
|
@ -264,21 +200,6 @@ class TimezoneTests(SimpleTestCase):
|
||||||
datetime.datetime(2011, 9, 1, 12, 20, 30, fold=1),
|
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):
|
def test_make_aware_zoneinfo_ambiguous(self):
|
||||||
# 2:30 happens twice, once before DST ends and once after
|
# 2:30 happens twice, once before DST ends and once after
|
||||||
ambiguous = datetime.datetime(2015, 10, 25, 2, 30)
|
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(std.utcoffset(), datetime.timedelta(hours=1))
|
||||||
self.assertEqual(dst.utcoffset(), datetime.timedelta(hours=2))
|
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):
|
def test_make_aware_zoneinfo_non_existent(self):
|
||||||
# 2:30 never happened due to DST
|
# 2:30 never happened due to DST
|
||||||
non_existent = datetime.datetime(2015, 3, 29, 2, 30)
|
non_existent = datetime.datetime(2015, 3, 29, 2, 30)
|
||||||
|
@ -349,12 +255,6 @@ class TimezoneTests(SimpleTestCase):
|
||||||
(zoneinfo.ZoneInfo("Europe/Madrid"), "Europe/Madrid"),
|
(zoneinfo.ZoneInfo("Europe/Madrid"), "Europe/Madrid"),
|
||||||
(zoneinfo.ZoneInfo("Etc/GMT-10"), "+10"),
|
(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:
|
for tz, expected in tests:
|
||||||
with self.subTest(tz=tz, expected=expected):
|
with self.subTest(tz=tz, expected=expected):
|
||||||
self.assertEqual(timezone._get_timezone_name(tz), expected)
|
self.assertEqual(timezone._get_timezone_name(tz), expected)
|
||||||
|
|
Loading…
Reference in New Issue