summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorPaul Ganssle <paul@ganssle.io>2021-01-19 11:16:01 +0100
committerCarlton Gibson <carlton.gibson@noumenal.es>2021-01-19 12:00:40 +0100
commita5d70cca12aaa1067871eb4e67ab2ed088d1edd6 (patch)
tree9cee8f3ffda53629160df0fab580d8e03ae13ce7 /django
parentde4e854f079dd3a638b9919ad73e5835d5e90d3f (diff)
[3.2.x] Refs #32365 -- Allowed use of non-pytz timezone implementations.
Backport of 10d126198434810529e0220b0c6896ed64ca0e88 from master
Diffstat (limited to 'django')
-rw-r--r--django/forms/utils.py5
-rw-r--r--django/utils/dateformat.py40
-rw-r--r--django/utils/timezone.py26
3 files changed, 43 insertions, 28 deletions
diff --git a/django/forms/utils.py b/django/forms/utils.py
index fbe79f1142..50412f414b 100644
--- a/django/forms/utils.py
+++ b/django/forms/utils.py
@@ -161,6 +161,11 @@ 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)
+ ):
+ raise ValueError('Ambiguous or non-existent time.')
return timezone.make_aware(value, current_timezone)
except Exception as exc:
raise ValidationError(
diff --git a/django/utils/dateformat.py b/django/utils/dateformat.py
index 9bd05a437b..38e89c47bb 100644
--- a/django/utils/dateformat.py
+++ b/django/utils/dateformat.py
@@ -20,7 +20,8 @@ from django.utils.dates import (
)
from django.utils.regex_helper import _lazy_re_compile
from django.utils.timezone import (
- get_default_timezone, is_aware, is_naive, make_aware,
+ _datetime_ambiguous_or_imaginary, get_default_timezone, is_aware, is_naive,
+ make_aware,
)
from django.utils.translation import gettext as _
@@ -160,15 +161,9 @@ class TimeFormat(Formatter):
if not self.timezone:
return ""
- name = None
- try:
+ if not _datetime_ambiguous_or_imaginary(self.data, self.timezone):
name = self.timezone.tzname(self.data)
- except Exception:
- # pytz raises AmbiguousTimeError during the autumn DST change.
- # This happens mainly when __init__ receives a naive datetime
- # and sets self.timezone = get_default_timezone().
- pass
- if name is None:
+ else:
name = self.format('O')
return str(name)
@@ -184,16 +179,13 @@ class TimeFormat(Formatter):
If timezone information is not available, return an empty string.
"""
- if not self.timezone:
+ if (
+ not self.timezone or
+ _datetime_ambiguous_or_imaginary(self.data, self.timezone)
+ ):
return ""
- try:
- offset = self.timezone.utcoffset(self.data)
- except Exception:
- # pytz raises AmbiguousTimeError during the autumn DST change.
- # This happens mainly when __init__ receives a naive datetime
- # and sets self.timezone = get_default_timezone().
- return ""
+ offset = self.timezone.utcoffset(self.data)
# `offset` is a datetime.timedelta. For negative values (to the west of
# UTC) only days can be negative (days=-1) and seconds are always
@@ -232,16 +224,12 @@ class DateFormat(TimeFormat):
def I(self): # NOQA: E743, E741
"'1' if Daylight Savings Time, '0' otherwise."
- try:
- if self.timezone and self.timezone.dst(self.data):
- return '1'
- else:
- return '0'
- except Exception:
- # pytz raises AmbiguousTimeError during the autumn DST change.
- # This happens mainly when __init__ receives a naive datetime
- # and sets self.timezone = get_default_timezone().
+ if (
+ not self.timezone or
+ _datetime_ambiguous_or_imaginary(self.data, self.timezone)
+ ):
return ''
+ return '1' if self.timezone.dst(self.data) else '0'
def j(self):
"Day of the month without leading zeros; i.e. '1' to '31'"
diff --git a/django/utils/timezone.py b/django/utils/timezone.py
index a87ec5fc33..cf22ec34d0 100644
--- a/django/utils/timezone.py
+++ b/django/utils/timezone.py
@@ -24,6 +24,11 @@ __all__ = [
# UTC time zone as a tzinfo instance.
utc = pytz.utc
+_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 = _PYTZ_BASE_CLASSES + (type(pytz.UTC),)
+
def get_fixed_timezone(offset):
"""Return a tzinfo instance with a fixed offset from UTC."""
@@ -68,7 +73,7 @@ def get_current_timezone_name():
def _get_timezone_name(timezone):
"""Return the name of ``timezone``."""
- return timezone.tzname(None)
+ return str(timezone)
# Timezone selection functions.
@@ -229,7 +234,7 @@ def make_aware(value, timezone=None, is_dst=None):
"""Make a naive datetime.datetime in a given time zone aware."""
if timezone is None:
timezone = get_current_timezone()
- if hasattr(timezone, 'localize'):
+ if _is_pytz_zone(timezone):
# This method is available for pytz time zones.
return timezone.localize(value, is_dst=is_dst)
else:
@@ -249,3 +254,20 @@ def make_naive(value, timezone=None):
if is_naive(value):
raise ValueError("make_naive() cannot be applied to a naive datetime")
return value.astimezone(timezone).replace(tzinfo=None)
+
+
+def _is_pytz_zone(tz):
+ """Checks if a zone is a pytz zone."""
+ return isinstance(tz, _PYTZ_BASE_CLASSES)
+
+
+def _datetime_ambiguous_or_imaginary(dt, tz):
+ if _is_pytz_zone(tz):
+ 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)