Fixed #27327 -- Simplified time zone handling by requiring pytz.

This commit is contained in:
Tim Graham 2016-10-07 21:06:49 -04:00
parent d84ffcc22b
commit 414ad25b09
30 changed files with 109 additions and 426 deletions

View File

@ -4,6 +4,8 @@ import warnings
from collections import deque
from contextlib import contextmanager
import pytz
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.db import DEFAULT_DB_ALIAS
@ -16,11 +18,6 @@ from django.utils import timezone
from django.utils.functional import cached_property
from django.utils.six.moves import _thread as thread
try:
import pytz
except ImportError:
pytz = None
NO_DB_ALIAS = '__no_db__'
@ -128,7 +125,6 @@ class BaseDatabaseWrapper(object):
elif self.settings_dict['TIME_ZONE'] is None:
return timezone.utc
else:
# Only this branch requires pytz.
return pytz.timezone(self.settings_dict['TIME_ZONE'])
@cached_property
@ -207,10 +203,6 @@ class BaseDatabaseWrapper(object):
raise ImproperlyConfigured(
"Connection '%s' cannot set TIME_ZONE because its engine "
"handles time zones conversions natively." % self.alias)
elif pytz is None:
raise ImproperlyConfigured(
"Connection '%s' cannot set TIME_ZONE because pytz isn't "
"installed." % self.alias)
def ensure_connection(self):
"""

View File

@ -3,11 +3,6 @@ from django.utils.functional import cached_property
from .base import Database
try:
import pytz
except ImportError:
pytz = None
class DatabaseFeatures(BaseDatabaseFeatures):
empty_fetchmany_value = ()
@ -56,14 +51,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
@cached_property
def has_zoneinfo_database(self):
# MySQL accepts full time zones names (eg. Africa/Nairobi) but rejects
# abbreviations (eg. EAT). When pytz isn't installed and the current
# time zone is LocalTimezone (the only sensible value in this
# context), the current time zone name will be an abbreviation. As a
# consequence, MySQL cannot perform time zone conversions reliably.
if pytz is None:
return False
# Test if the time zone definitions are installed.
with self.connection.cursor() as cursor:
cursor.execute("SELECT 1 FROM mysql.time_zone LIMIT 1")

View File

@ -1,11 +1,6 @@
from django.db.backends.base.features import BaseDatabaseFeatures
from django.db.utils import InterfaceError
try:
import pytz
except ImportError:
pytz = None
class DatabaseFeatures(BaseDatabaseFeatures):
empty_fetchmany_value = ()
@ -19,7 +14,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
supports_subqueries_in_group_by = False
supports_transactions = True
supports_timezones = False
has_zoneinfo_database = pytz is not None
supports_bitwise_or = False
has_native_duration_field = True
can_defer_constraint_checks = True

View File

@ -11,6 +11,8 @@ import decimal
import re
import warnings
import pytz
from django.conf import settings
from django.db import utils
from django.db.backends import utils as backend_utils
@ -23,11 +25,6 @@ from django.utils.deprecation import RemovedInDjango20Warning
from django.utils.encoding import force_text
from django.utils.safestring import SafeBytes
try:
import pytz
except ImportError:
pytz = None
try:
try:
from pysqlite2 import dbapi2 as Database

View File

@ -7,11 +7,6 @@ from django.utils.functional import cached_property
from .base import Database
try:
import pytz
except ImportError:
pytz = None
class DatabaseFeatures(BaseDatabaseFeatures):
# SQLite cannot handle us only partially reading from a cursor's result set
@ -78,7 +73,3 @@ class DatabaseFeatures(BaseDatabaseFeatures):
has_support = False
cursor.execute('DROP TABLE STDDEV_TEST')
return has_support
@cached_property
def has_zoneinfo_database(self):
return pytz is not None

View File

@ -4,7 +4,7 @@ import datetime
import uuid
from django.conf import settings
from django.core.exceptions import FieldError, ImproperlyConfigured
from django.core.exceptions import FieldError
from django.db import utils
from django.db.backends import utils as backend_utils
from django.db.backends.base.operations import BaseDatabaseOperations
@ -13,11 +13,6 @@ from django.utils import six, timezone
from django.utils.dateparse import parse_date, parse_datetime, parse_time
from django.utils.duration import duration_string
try:
import pytz
except ImportError:
pytz = None
class DatabaseOperations(BaseDatabaseOperations):
def bulk_batch_size(self, fields, objs):
@ -77,27 +72,19 @@ class DatabaseOperations(BaseDatabaseOperations):
# cause a collision with a field name).
return "django_time_trunc('%s', %s)" % (lookup_type.lower(), field_name)
def _require_pytz(self):
if settings.USE_TZ and pytz is None:
raise ImproperlyConfigured("This query requires pytz, but it isn't installed.")
def datetime_cast_date_sql(self, field_name, tzname):
self._require_pytz()
return "django_datetime_cast_date(%s, %%s)" % field_name, [tzname]
def datetime_cast_time_sql(self, field_name, tzname):
self._require_pytz()
return "django_datetime_cast_time(%s, %%s)" % field_name, [tzname]
def datetime_extract_sql(self, lookup_type, field_name, tzname):
# Same comment as in date_extract_sql.
self._require_pytz()
return "django_datetime_extract('%s', %s, %%s)" % (
lookup_type.lower(), field_name), [tzname]
def datetime_trunc_sql(self, lookup_type, field_name, tzname):
# Same comment as in date_trunc_sql.
self._require_pytz()
return "django_datetime_trunc('%s', %s, %%s)" % (
lookup_type.lower(), field_name), [tzname]

View File

@ -191,7 +191,7 @@ class TruncBase(TimezoneMixin, Transform):
if value is None:
raise ValueError(
"Database returned an invalid datetime value. "
"Are time zone definitions for your database and pytz installed?"
"Are time zone definitions for your database installed?"
)
value = value.replace(tzinfo=None)
value = timezone.make_aware(value, self.tzinfo)

View File

@ -1,14 +1,10 @@
from datetime import datetime, tzinfo
import pytz
from django.template import Library, Node, TemplateSyntaxError
from django.utils import six, timezone
try:
import pytz
except ImportError:
pytz = None
register = Library()
@ -44,7 +40,6 @@ def do_timezone(value, arg):
Converts a datetime to local time in a given time zone.
The argument must be an instance of a tzinfo subclass or a time zone name.
If it is a time zone name, pytz is required.
Naive datetimes are assumed to be in local time in the default time zone.
"""
@ -64,7 +59,7 @@ def do_timezone(value, arg):
# Obtain a tzinfo instance
if isinstance(arg, tzinfo):
tz = arg
elif isinstance(arg, six.string_types) and pytz is not None:
elif isinstance(arg, six.string_types):
try:
tz = pytz.timezone(arg)
except pytz.UnknownTimeZoneError:
@ -156,8 +151,8 @@ def timezone_tag(parser, token):
Enables a given time zone just for this block.
The ``timezone`` argument must be an instance of a ``tzinfo`` subclass, a
time zone name, or ``None``. If is it a time zone name, pytz is required.
If it is ``None``, the default time zone is used within the block.
time zone name, or ``None``. If it is ``None``, the default time zone is
used within the block.
Sample usage::

View File

@ -1,24 +1,16 @@
"""
Timezone-related classes and functions.
This module uses pytz when it's available and fallbacks when it isn't.
"""
import sys
import time as _time
from datetime import datetime, timedelta, tzinfo
from threading import local
import pytz
from django.conf import settings
from django.utils import lru_cache, six
from django.utils.decorators import ContextDecorator
try:
import pytz
except ImportError:
pytz = None
__all__ = [
'utc', 'get_fixed_timezone',
'get_default_timezone', 'get_default_timezone_name',
@ -34,26 +26,6 @@ __all__ = [
ZERO = timedelta(0)
class UTC(tzinfo):
"""
UTC implementation taken from Python's docs.
Used only when pytz isn't available.
"""
def __repr__(self):
return "<UTC>"
def utcoffset(self, dt):
return ZERO
def tzname(self, dt):
return "UTC"
def dst(self, dt):
return ZERO
class FixedOffset(tzinfo):
"""
Fixed offset in minutes east from UTC. Taken from Python's docs.
@ -78,79 +50,7 @@ class FixedOffset(tzinfo):
def dst(self, dt):
return ZERO
class ReferenceLocalTimezone(tzinfo):
"""
Local time. Taken from Python's docs.
Used only when pytz isn't available, and most likely inaccurate. If you're
having trouble with this class, don't waste your time, just install pytz.
Kept as close as possible to the reference version. __init__ was added to
delay the computation of STDOFFSET, DSTOFFSET and DSTDIFF which is
performed at import time in the example.
Subclasses contain further improvements.
"""
def __init__(self):
self.STDOFFSET = timedelta(seconds=-_time.timezone)
if _time.daylight:
self.DSTOFFSET = timedelta(seconds=-_time.altzone)
else:
self.DSTOFFSET = self.STDOFFSET
self.DSTDIFF = self.DSTOFFSET - self.STDOFFSET
tzinfo.__init__(self)
def utcoffset(self, dt):
if self._isdst(dt):
return self.DSTOFFSET
else:
return self.STDOFFSET
def dst(self, dt):
if self._isdst(dt):
return self.DSTDIFF
else:
return ZERO
def tzname(self, dt):
return _time.tzname[self._isdst(dt)]
def _isdst(self, dt):
tt = (dt.year, dt.month, dt.day,
dt.hour, dt.minute, dt.second,
dt.weekday(), 0, 0)
stamp = _time.mktime(tt)
tt = _time.localtime(stamp)
return tt.tm_isdst > 0
class LocalTimezone(ReferenceLocalTimezone):
"""
Slightly improved local time implementation focusing on correctness.
It still crashes on dates before 1970 or after 2038, but at least the
error message is helpful.
"""
def tzname(self, dt):
is_dst = False if dt is None else self._isdst(dt)
return _time.tzname[is_dst]
def _isdst(self, dt):
try:
return super(LocalTimezone, self)._isdst(dt)
except (OverflowError, ValueError) as exc:
exc_type = type(exc)
exc_value = exc_type(
"Unsupported value: %r. You should install pytz." % dt)
exc_value.__cause__ = exc
if not hasattr(exc, '__traceback__'):
exc.__traceback__ = sys.exc_info()[2]
six.reraise(exc_type, exc_value, sys.exc_info()[2])
utc = pytz.utc if pytz else UTC()
utc = pytz.utc
"""UTC time zone as a tzinfo instance."""
@ -175,11 +75,7 @@ def get_default_timezone():
This is the time zone defined by settings.TIME_ZONE.
"""
if isinstance(settings.TIME_ZONE, six.string_types) and pytz is not None:
return pytz.timezone(settings.TIME_ZONE)
else:
# This relies on os.environ['TZ'] being set to settings.TIME_ZONE.
return LocalTimezone()
# This function exists for consistency with get_current_timezone_name
@ -228,11 +124,11 @@ def activate(timezone):
Sets the time zone for the current thread.
The ``timezone`` argument must be an instance of a tzinfo subclass or a
time zone name. If it is a time zone name, pytz is required.
time zone name.
"""
if isinstance(timezone, tzinfo):
_active.value = timezone
elif isinstance(timezone, six.string_types) and pytz is not None:
elif isinstance(timezone, six.string_types):
_active.value = pytz.timezone(timezone)
else:
raise ValueError("Invalid timezone: %r" % timezone)
@ -257,8 +153,8 @@ class override(ContextDecorator):
on exit.
The ``timezone`` argument must be an instance of a ``tzinfo`` subclass, a
time zone name, or ``None``. If is it a time zone name, pytz is required.
If it is ``None``, Django enables the default time zone.
time zone name, or ``None``. If it is ``None``, Django enables the default
time zone.
"""
def __init__(self, timezone):
self.timezone = timezone

View File

@ -117,9 +117,9 @@ Imports
# try/except
try:
import pytz
import yaml
except ImportError:
pytz = None
yaml = None
CONSTANT = 'foo'

View File

@ -232,7 +232,7 @@ dependencies:
* numpy_
* Pillow_
* PyYAML_
* pytz_
* pytz_ (required)
* setuptools_
* memcached_, plus a :ref:`supported Python binding <memcached>`
* mock_ (for Python 2)

View File

@ -758,11 +758,11 @@ object. If it's ``None``, Django uses the :ref:`current time zone
As a consequence, your database must be able to interpret the value of
``tzinfo.tzname(None)``. This translates into the following requirements:
- SQLite: install pytz_ — conversions are actually performed in Python.
- SQLite: no requirements. Conversions are performed in Python with pytz_
(installed when you install Django).
- PostgreSQL: no requirements (see `Time Zones`_).
- Oracle: no requirements (see `Choosing a Time Zone File`_).
- MySQL: install pytz_ and load the time zone tables with
`mysql_tzinfo_to_sql`_.
- MySQL: load the time zone tables with `mysql_tzinfo_to_sql`_.
.. _pytz: http://pytz.sourceforge.net/
.. _Time Zones: https://www.postgresql.org/docs/current/static/datatype-datetime.html#DATATYPE-TIMEZONES

View File

@ -607,8 +607,6 @@ This allows interacting with third-party databases that store datetimes in
local time rather than UTC. To avoid issues around DST changes, you shouldn't
set this option for databases managed by Django.
Setting this option requires installing pytz_.
When :setting:`USE_TZ` is ``True`` and the database doesn't support time zones
(e.g. SQLite, MySQL, Oracle), Django reads and writes datetimes in local time
according to this option if it is set and in UTC if it isn't.
@ -618,8 +616,6 @@ PostgreSQL), it is an error to set this option.
When :setting:`USE_TZ` is ``False``, it is an error to set this option.
.. _pytz: http://pytz.sourceforge.net/
.. setting:: USER
``USER``
@ -2478,8 +2474,8 @@ See also :setting:`DATE_INPUT_FORMATS` and :setting:`DATETIME_INPUT_FORMATS`.
Default: ``'America/Chicago'``
A string representing the time zone for this installation, or ``None``. See
the `list of time zones`_.
A string representing the time zone for this installation. See the `list of
time zones`_.
.. note::
Since Django was first released with the :setting:`TIME_ZONE` set to
@ -2496,22 +2492,15 @@ will store all datetimes. When :setting:`USE_TZ` is ``True``, this is the
default time zone that Django will use to display datetimes in templates and
to interpret datetimes entered in forms.
Django sets the ``os.environ['TZ']`` variable to the time zone you specify in
the :setting:`TIME_ZONE` setting. Thus, all your views and models will
On Unix environments (where :func:`time.tzset` is implemented), Django sets the
``os.environ['TZ']`` variable to the time zone you specify in the
:setting:`TIME_ZONE` setting. Thus, all your views and models will
automatically operate in this time zone. However, Django won't set the ``TZ``
environment variable under the following conditions:
* If you're using the manual configuration option as described in
:ref:`manually configuring settings
<settings-without-django-settings-module>`, or
* If you specify ``TIME_ZONE = None``. This will cause Django to fall back to
using the system timezone. However, this is discouraged when :setting:`USE_TZ
= True <USE_TZ>`, because it makes conversions between local time and UTC
less reliable.
If Django doesn't set the ``TZ`` environment variable, it's up to you
to ensure your processes are running in the correct environment.
environment variable if you're using the manual configuration option as
described in :ref:`manually configuring settings
<settings-without-django-settings-module>`. If Django doesn't set the ``TZ``
environment variable, it's up to you to ensure your processes are running in
the correct environment.
.. note::
Django cannot reliably use alternate time zones in a Windows environment.

View File

@ -966,7 +966,7 @@ appropriate entities.
Sets the :ref:`current time zone <default-current-time-zone>`. The
``timezone`` argument must be an instance of a :class:`~datetime.tzinfo`
subclass or, if pytz_ is available, a time zone name.
subclass or a time zone name.
.. function:: deactivate()
@ -1043,21 +1043,18 @@ appropriate entities.
:class:`~datetime.datetime`. If ``timezone`` is set to ``None``, it
defaults to the :ref:`current time zone <default-current-time-zone>`.
When pytz_ is installed, the exception ``pytz.AmbiguousTimeError``
will be raised if you try to make ``value`` aware during a DST transition
where the same time occurs twice (when reverting from DST). Setting
``is_dst`` to ``True`` or ``False`` will avoid the exception by choosing if
the time is pre-transition or post-transition respectively.
The ``pytz.AmbiguousTimeError`` exception is raised if you try to make
``value`` aware during a DST transition where the same time occurs twice
(when reverting from DST). Setting ``is_dst`` to ``True`` or ``False`` will
avoid the exception by choosing if the time is pre-transition or
post-transition respectively.
When pytz_ is installed, the exception ``pytz.NonExistentTimeError``
will be raised if you try to make ``value`` aware during a DST transition
such that the time never occurred (when entering into DST). Setting
``is_dst`` to ``True`` or ``False`` will avoid the exception by moving the
hour backwards or forwards by 1 respectively. For example, ``is_dst=True``
would change a non-existent time of 2:30 to 1:30 and ``is_dst=False``
would change the time to 3:30.
``is_dst`` has no effect when ``pytz`` is not installed.
The ``pytz.NonExistentTimeError`` exception is raised if you try to make
``value`` aware during a DST transition such that the time never occurred
(when entering into DST). Setting ``is_dst`` to ``True`` or ``False`` will
avoid the exception by moving the hour backwards or forwards by 1
respectively. For example, ``is_dst=True`` would change a non-existent
time of 2:30 to 1:30 and ``is_dst=False`` would change the time to 3:30.
.. function:: make_naive(value, timezone=None)
@ -1066,8 +1063,6 @@ appropriate entities.
aware :class:`~datetime.datetime`. If ``timezone`` is set to ``None``, it
defaults to the :ref:`current time zone <default-current-time-zone>`.
.. _pytz: http://pytz.sourceforge.net/
``django.utils.translation``
============================

View File

@ -468,6 +468,27 @@ To prevent typos from passing silently,
arguments are model fields. This should be backwards-incompatible only in the
fact that it might expose a bug in your project.
``pytz`` is a required dependency and support for ``settings.TIME_ZONE = None`` is removed
------------------------------------------------------------------------------------------
To simplify Django's timezone handling, ``pytz`` is now a required dependency.
It's automatically installed along with Django.
Support for ``settings.TIME_ZONE = None`` is removed as the behavior isn't
commonly used and is questionably useful. If you want to automatically detect
the timezone based on the system timezone, you can use `tzlocal
<https://pypi.python.org/pypi/tzlocal>`_::
from tzlocal import get_localzone
TIME_ZONE = get_localzone().zone
This works similar to ``settings.TIME_ZONE = None`` except that it also sets
``os.environ['TZ']``. `Let us know
<https://groups.google.com/d/topic/django-developers/OAV3FChfuPM/discussion>`__
if there's a use case where you find you can't adapt your code to set a
``TIME_ZONE``.
Miscellaneous
-------------

View File

@ -622,7 +622,6 @@ Pygments
pysqlite
pythonic
Pythonista
pytz
qs
Québec
queryset

View File

@ -26,14 +26,12 @@ to this problem is to use UTC in the code and use local time only when
interacting with end users.
Time zone support is disabled by default. To enable it, set :setting:`USE_TZ =
True <USE_TZ>` in your settings file. Installing pytz_ is highly recommended,
but may not be mandatory depending on your particular database backend,
operating system and time zone. If you encounter an exception querying dates
or times, please try installing it before filing a bug. It's as simple as:
True <USE_TZ>` in your settings file. Time zone support uses pytz_, which is
installed when you install Django.
.. code-block:: console
.. versionchanged:: 1.11
$ pip install pytz
Older versions don't require ``pytz`` or install it automatically.
.. note::
@ -113,11 +111,8 @@ receives one, it attempts to make it aware by interpreting it in the
:ref:`default time zone <default-current-time-zone>` and raises a warning.
Unfortunately, during DST transitions, some datetimes don't exist or are
ambiguous. In such situations, pytz_ raises an exception. Other
:class:`~datetime.tzinfo` implementations, such as the local time zone used as
a fallback when pytz_ isn't installed, may raise an exception or return
inaccurate results. That's why you should always create aware datetime objects
when time zone support is enabled.
ambiguous. In such situations, pytz_ raises an exception. That's why you should
always create aware datetime objects when time zone support is enabled.
In practice, this is rarely an issue. Django gives you aware datetime objects
in the models and forms, and most often, new datetime objects are created from
@ -360,7 +355,7 @@ For example::
Forces conversion of a single value to an arbitrary timezone.
The argument must be an instance of a :class:`~datetime.tzinfo` subclass or a
time zone name. If it is a time zone name, pytz_ is required.
time zone name.
For example::
@ -405,9 +400,8 @@ Code
----
The first step is to add :setting:`USE_TZ = True <USE_TZ>` to your settings
file and install pytz_ (if possible). At this point, things should mostly
work. If you create naive datetime objects in your code, Django makes them
aware when necessary.
file. At this point, things should mostly work. If you create naive datetime
objects in your code, Django makes them aware when necessary.
However, these conversions may fail around DST transitions, which means you
aren't getting the full benefits of time zone support yet. Also, you're likely
@ -523,22 +517,7 @@ Setup
one year is 2011-02-28 or 2011-03-01, which depends on your business
requirements.)
3. **Should I install pytz?**
Yes. Django has a policy of not requiring external dependencies, and for
this reason pytz_ is optional. However, it's much safer to install it.
As soon as you activate time zone support, Django needs a definition of the
default time zone. When pytz is available, Django loads this definition
from the `tz database`_. This is the most accurate solution. Otherwise, it
relies on the difference between local time and UTC, as reported by the
operating system, to compute conversions. This is less reliable, especially
around DST transitions.
Furthermore, if you want to support users in more than one time zone, pytz
is the reference for time zone definitions.
4. **How do I interact with a database that stores datetimes in local time?**
3. **How do I interact with a database that stores datetimes in local time?**
Set the :setting:`TIME_ZONE <DATABASE-TIME_ZONE>` option to the appropriate
time zone for this database in the :setting:`DATABASES` setting.
@ -653,8 +632,8 @@ Troubleshooting
>>> local.date()
datetime.date(2012, 3, 3)
4. **I get an error** "``Are time zone definitions for your database and pytz
installed?``" **pytz is installed, so I guess the problem is my database?**
4. **I get an error** "``Are time zone definitions for your database
installed?``"
If you are using MySQL, see the :ref:`mysql-time-zone-definitions` section
of the MySQL notes for instructions on loading time zone definitions.
@ -700,8 +679,7 @@ Usage
>>> timezone.localtime(timezone.now())
datetime.datetime(2012, 3, 3, 20, 10, 53, 873365, tzinfo=<DstTzInfo 'Europe/Paris' CET+1:00:00 STD>)
In this example, pytz_ is installed and the current time zone is
``"Europe/Paris"``.
In this example, the current time zone is ``"Europe/Paris"``.
3. **How can I see all available time zones?**

View File

@ -47,6 +47,7 @@ setup(
entry_points={'console_scripts': [
'django-admin = django.core.management:execute_from_command_line',
]},
install_requires=['pytz'],
extras_require={
"bcrypt": ["bcrypt"],
"argon2": ["argon2-cffi >= 16.1.0"],

View File

@ -5,7 +5,8 @@ import gettext
import os
from datetime import datetime, timedelta
from importlib import import_module
from unittest import skipIf
import pytz
from django import forms
from django.conf import settings
@ -23,11 +24,6 @@ from django.utils import six, translation
from . import models
from .widgetadmin import site as widget_admin_site
try:
import pytz
except ImportError:
pytz = None
class TestDataMixin(object):
@ -794,7 +790,6 @@ class DateTimePickerSeleniumTests(AdminWidgetSeleniumTestCase):
self.wait_for_text('#calendarin0 caption', expected_caption)
@skipIf(pytz is None, "this test requires pytz")
@override_settings(TIME_ZONE='Asia/Singapore')
class DateTimePickerShortcutsSeleniumTests(AdminWidgetSeleniumTestCase):

15
tests/cache/tests.py vendored
View File

@ -1832,17 +1832,16 @@ class CacheI18nTest(TestCase):
@override_settings(USE_I18N=False, USE_L10N=False, USE_TZ=True)
def test_cache_key_with_non_ascii_tzname(self):
# Regression test for #17476
class CustomTzName(timezone.UTC):
name = ''
def tzname(self, dt):
return self.name
# Timezone-dependent cache keys should use ASCII characters only
# (#17476). The implementation here is a bit odd (timezone.utc is an
# instance, not a class), but it simulates the correct conditions.
class CustomTzName(timezone.utc):
pass
request = self.factory.get(self.path)
response = HttpResponse()
with timezone.override(CustomTzName()):
CustomTzName.name = 'Hora estándar de Argentina'.encode('UTF-8') # UTF-8 string
with timezone.override(CustomTzName):
CustomTzName.zone = 'Hora estándar de Argentina'.encode('UTF-8') # UTF-8 string
sanitized_name = 'Hora_estndar_de_Argentina'
self.assertIn(
sanitized_name, learn_cache_key(request, response),

View File

@ -1,18 +1,12 @@
from __future__ import unicode_literals
import datetime
from unittest import skipIf
from django.test import TestCase, override_settings
from django.utils import timezone
from .models import Article, Category, Comment
try:
import pytz
except ImportError:
pytz = None
class DateTimesTests(TestCase):
def test_related_model_traverse(self):
@ -84,7 +78,6 @@ class DateTimesTests(TestCase):
],
)
@skipIf(pytz is None, "this test requires pytz")
@override_settings(USE_TZ=True)
def test_21432(self):
now = timezone.localtime(timezone.now().replace(microsecond=0))

View File

@ -1,7 +1,8 @@
from __future__ import unicode_literals
from datetime import datetime
from unittest import skipIf
import pytz
from django.conf import settings
from django.db import connection
@ -16,11 +17,6 @@ from django.utils import timezone
from .models import DTModel
try:
import pytz
except ImportError:
pytz = None
def microsecond_support(value):
return value if connection.features.supports_microsecond_precision else value.replace(microsecond=0)
@ -659,7 +655,6 @@ class DateFunctionTests(TestCase):
list(DTModel.objects.annotate(truncated=TruncSecond('start_date', output_field=DateField())))
@skipIf(pytz is None, "this test requires pytz")
@override_settings(USE_TZ=True, TIME_ZONE='UTC')
class DateFunctionWithTimeZoneTests(DateFunctionTests):

View File

@ -32,12 +32,6 @@ from django.utils.six.moves.urllib.request import urlopen
from .models import Storage, temp_storage, temp_storage_location
try:
import pytz
except ImportError:
pytz = None
FILE_SUFFIX_REGEX = '[A-Za-z0-9]{7}'
@ -99,10 +93,6 @@ class FileSystemStorageTests(unittest.TestCase):
storage.url(storage.base_url)
# Tests for TZ-aware time methods need pytz.
requires_pytz = unittest.skipIf(pytz is None, "this test requires pytz")
class FileStorageTests(SimpleTestCase):
storage_class = FileSystemStorage
@ -157,7 +147,6 @@ class FileStorageTests(SimpleTestCase):
# different.
now_in_algiers = timezone.make_aware(datetime.now())
# Use a fixed offset timezone so we don't need pytz.
with timezone.override(timezone.get_fixed_timezone(-300)):
# At this point the system TZ is +1 and the Django TZ
# is -5. The following will be aware in UTC.
@ -191,7 +180,6 @@ class FileStorageTests(SimpleTestCase):
# different.
now_in_algiers = timezone.make_aware(datetime.now())
# Use a fixed offset timezone so we don't need pytz.
with timezone.override(timezone.get_fixed_timezone(-300)):
# At this point the system TZ is +1 and the Django TZ
# is -5.
@ -220,7 +208,6 @@ class FileStorageTests(SimpleTestCase):
_dt = timezone.make_aware(dt, now_in_algiers.tzinfo)
self.assertLess(abs(_dt - now_in_algiers), timedelta(seconds=2))
@requires_pytz
def test_file_get_accessed_time(self):
"""
File storage returns a Datetime object for the last accessed time of
@ -236,7 +223,6 @@ class FileStorageTests(SimpleTestCase):
self.assertEqual(atime, datetime.fromtimestamp(os.path.getatime(self.storage.path(f_name))))
self.assertLess(timezone.now() - self.storage.get_accessed_time(f_name), timedelta(seconds=2))
@requires_pytz
@requires_tz_support
def test_file_get_accessed_time_timezone(self):
self._test_file_time_getter(self.storage.get_accessed_time)
@ -256,7 +242,6 @@ class FileStorageTests(SimpleTestCase):
self.assertEqual(atime, datetime.fromtimestamp(os.path.getatime(self.storage.path(f_name))))
self.assertLess(datetime.now() - self.storage.accessed_time(f_name), timedelta(seconds=2))
@requires_pytz
def test_file_get_created_time(self):
"""
File storage returns a datetime for the creation time of a file.
@ -271,7 +256,6 @@ class FileStorageTests(SimpleTestCase):
self.assertEqual(ctime, datetime.fromtimestamp(os.path.getctime(self.storage.path(f_name))))
self.assertLess(timezone.now() - self.storage.get_created_time(f_name), timedelta(seconds=2))
@requires_pytz
@requires_tz_support
def test_file_get_created_time_timezone(self):
self._test_file_time_getter(self.storage.get_created_time)
@ -291,7 +275,6 @@ class FileStorageTests(SimpleTestCase):
self.assertEqual(ctime, datetime.fromtimestamp(os.path.getctime(self.storage.path(f_name))))
self.assertLess(datetime.now() - self.storage.created_time(f_name), timedelta(seconds=2))
@requires_pytz
def test_file_get_modified_time(self):
"""
File storage returns a datetime for the last modified time of a file.
@ -306,7 +289,6 @@ class FileStorageTests(SimpleTestCase):
self.assertEqual(mtime, datetime.fromtimestamp(os.path.getmtime(self.storage.path(f_name))))
self.assertLess(timezone.now() - self.storage.get_modified_time(f_name), timedelta(seconds=2))
@requires_pytz
@requires_tz_support
def test_file_get_modified_time_timezone(self):
self._test_file_time_getter(self.storage.get_modified_time)

View File

@ -2,7 +2,6 @@ from __future__ import unicode_literals
import datetime
from decimal import Decimal
from unittest import skipIf
from django.contrib.humanize.templatetags import humanize
from django.template import Context, Template, defaultfilters
@ -12,12 +11,6 @@ from django.utils.html import escape
from django.utils.timezone import get_fixed_timezone, utc
from django.utils.translation import ugettext as _
try:
import pytz
except ImportError:
pytz = None
# Mock out datetime in some tests so they don't fail occasionally when they
# run too slow. Use a fixed datetime for datetime.now(). DST change in
# America/Chicago (the default time zone) happened on March 11th in 2012.
@ -174,7 +167,6 @@ class HumanizeTests(SimpleTestCase):
# As 24h of difference they will never be the same
self.assertNotEqual(naturalday_one, naturalday_two)
@skipIf(pytz is None, "this test requires pytz")
def test_naturalday_uses_localtime(self):
# Regression for #18504
# This is 2012-03-08HT19:30:00-06:00 in America/Chicago

View File

@ -8,7 +8,6 @@ Pillow
PyYAML
# pylibmc/libmemcached can't be built on Windows.
pylibmc; sys.platform != 'win32'
pytz > dev
selenium
sqlparse
tblib

View File

@ -22,7 +22,7 @@ from django.test import (
TransactionTestCase, mock, skipIfDBFeature, skipUnlessDBFeature,
)
from django.test.utils import CaptureQueriesContext
from django.utils.timezone import UTC
from django.utils import timezone
from .fields import (
CustomManyToManyField, InheritedManyToManyField, MediumBlobField,
@ -2187,7 +2187,7 @@ class SchemaTests(TransactionTestCase):
TimeField if auto_now or auto_add_now is set (#25005).
"""
now = datetime.datetime(month=1, day=1, year=2000, hour=1, minute=1)
now_tz = datetime.datetime(month=1, day=1, year=2000, hour=1, minute=1, tzinfo=UTC())
now_tz = datetime.datetime(month=1, day=1, year=2000, hour=1, minute=1, tzinfo=timezone.utc)
mocked_datetime.now = mock.MagicMock(return_value=now)
mocked_tz.now = mock.MagicMock(return_value=now_tz)
# Create the table

View File

@ -16,11 +16,6 @@ from django.utils.feedgenerator import (
from .models import Article, Entry
try:
import pytz
except ImportError:
pytz = None
TZ = timezone.get_default_timezone()

View File

@ -8,6 +8,8 @@ from contextlib import contextmanager
from unittest import SkipTest, skipIf
from xml.dom.minidom import parseString
import pytz
from django.contrib.auth.models import User
from django.core import serializers
from django.core.exceptions import ImproperlyConfigured
@ -33,13 +35,6 @@ from .models import (
AllDayEvent, Event, MaybeEvent, Session, SessionEvent, Timestamp,
)
try:
import pytz
except ImportError:
pytz = None
requires_pytz = skipIf(pytz is None, "this test requires pytz")
# These tests use the EAT (Eastern Africa Time) and ICT (Indochina Time)
# who don't have Daylight Saving Time, so we can represent them easily
# with FixedOffset, and use them directly as tzinfo in the constructors.
@ -363,7 +358,6 @@ class NewDatabaseTests(TestCase):
self.assertEqual(Event.objects.filter(dt__gte=dt2).count(), 1)
self.assertEqual(Event.objects.filter(dt__gt=dt2).count(), 0)
@requires_pytz
def test_query_filter_with_pytz_timezones(self):
tz = pytz.timezone('Europe/Paris')
dt = datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=tz)
@ -908,7 +902,6 @@ class TemplateTests(SimpleTestCase):
expected = results[k1][k2]
self.assertEqual(actual, expected, '%s / %s: %r != %r' % (k1, k2, actual, expected))
@requires_pytz
def test_localtime_filters_with_pytz(self):
"""
Test the |localtime, |utc, and |timezone filters with pytz.
@ -976,7 +969,6 @@ class TemplateTests(SimpleTestCase):
"2011-09-01T13:20:30+03:00|2011-09-01T17:20:30+07:00|2011-09-01T13:20:30+03:00"
)
@requires_pytz
def test_timezone_templatetag_with_pytz(self):
"""
Test the {% timezone %} templatetag with pytz.
@ -996,7 +988,7 @@ class TemplateTests(SimpleTestCase):
def test_timezone_templatetag_invalid_argument(self):
with self.assertRaises(TemplateSyntaxError):
Template("{% load tz %}{% timezone %}{% endtimezone %}").render()
with self.assertRaises(ValueError if pytz is None else pytz.UnknownTimeZoneError):
with self.assertRaises(pytz.UnknownTimeZoneError):
Template("{% load tz %}{% timezone tz %}{% endtimezone %}").render(Context({'tz': 'foobar'}))
@skipIf(sys.platform.startswith('win'), "Windows uses non-standard time zone names")
@ -1006,7 +998,7 @@ class TemplateTests(SimpleTestCase):
"""
tpl = Template("{% load tz %}{% get_current_timezone as time_zone %}{{ time_zone }}")
self.assertEqual(tpl.render(Context()), "Africa/Nairobi" if pytz else "EAT")
self.assertEqual(tpl.render(Context()), "Africa/Nairobi")
with timezone.override(UTC):
self.assertEqual(tpl.render(Context()), "UTC")
@ -1019,7 +1011,6 @@ class TemplateTests(SimpleTestCase):
with timezone.override(UTC):
self.assertEqual(tpl.render(Context({'tz': ICT})), "+0700")
@requires_pytz
def test_get_current_timezone_templatetag_with_pytz(self):
"""
Test the {% get_current_timezone %} templatetag with pytz.
@ -1048,7 +1039,7 @@ class TemplateTests(SimpleTestCase):
context = Context()
self.assertEqual(tpl.render(context), "")
request_context = RequestContext(HttpRequest(), processors=[context_processors.tz])
self.assertEqual(tpl.render(request_context), "Africa/Nairobi" if pytz else "EAT")
self.assertEqual(tpl.render(request_context), "Africa/Nairobi")
@requires_tz_support
def test_date_and_time_template_filters(self):
@ -1068,15 +1059,6 @@ class TemplateTests(SimpleTestCase):
with timezone.override(ICT):
self.assertEqual(tpl.render(ctx), "2011-09-01 at 20:20:20")
def test_localtime_with_time_zone_setting_set_to_none(self):
# Regression for #17274
tpl = Template("{% load tz %}{{ dt }}")
ctx = Context({'dt': datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=EAT)})
with self.settings(TIME_ZONE=None):
# the actual value depends on the system time zone of the host
self.assertTrue(tpl.render(ctx).startswith("2011"))
@requires_tz_support
def test_now_template_tag_uses_current_time_zone(self):
# Regression for #17343
@ -1094,7 +1076,6 @@ class LegacyFormsTests(TestCase):
self.assertTrue(form.is_valid())
self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 9, 1, 13, 20, 30))
@requires_pytz
def test_form_with_non_existent_time(self):
form = EventForm({'dt': '2011-03-27 02:30:00'})
with timezone.override(pytz.timezone('Europe/Paris')):
@ -1102,7 +1083,6 @@ class LegacyFormsTests(TestCase):
self.assertTrue(form.is_valid())
self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 3, 27, 2, 30, 0))
@requires_pytz
def test_form_with_ambiguous_time(self):
form = EventForm({'dt': '2011-10-30 02:30:00'})
with timezone.override(pytz.timezone('Europe/Paris')):
@ -1141,7 +1121,6 @@ class NewFormsTests(TestCase):
# Datetime inputs formats don't allow providing a time zone.
self.assertFalse(form.is_valid())
@requires_pytz
def test_form_with_non_existent_time(self):
with timezone.override(pytz.timezone('Europe/Paris')):
form = EventForm({'dt': '2011-03-27 02:30:00'})
@ -1153,7 +1132,6 @@ class NewFormsTests(TestCase):
]
)
@requires_pytz
def test_form_with_ambiguous_time(self):
with timezone.override(pytz.timezone('Europe/Paris')):
form = EventForm({'dt': '2011-10-30 02:30:00'})

View File

@ -1,8 +1,6 @@
from __future__ import unicode_literals
import sys
from datetime import date, datetime
from unittest import skipIf
from django.test import SimpleTestCase, override_settings
from django.test.utils import TZ_SUPPORT, requires_tz_support
@ -12,11 +10,6 @@ from django.utils.timezone import (
get_default_timezone, get_fixed_timezone, make_aware, utc,
)
try:
import pytz
except ImportError:
pytz = None
@override_settings(TIME_ZONE='Europe/Copenhagen')
class DateFormatTests(SimpleTestCase):
@ -36,18 +29,16 @@ class DateFormatTests(SimpleTestCase):
dt = datetime(2009, 5, 16, 5, 30, 30)
self.assertEqual(datetime.fromtimestamp(int(format(dt, 'U'))), dt)
@skipIf(sys.platform.startswith('win') and not pytz, "Test requires pytz on Windows")
def test_naive_ambiguous_datetime(self):
# dt is ambiguous in Europe/Copenhagen. LocalTimezone guesses the
# offset (and gets it wrong 50% of the time) while pytz refuses the
# temptation to guess. In any case, this shouldn't crash.
# dt is ambiguous in Europe/Copenhagen. pytz raises an exception for
# the ambiguity, which results in an empty string.
dt = datetime(2015, 10, 25, 2, 30, 0)
# Try all formatters that involve self.timezone.
self.assertEqual(format(dt, 'I'), '0' if pytz is None else '')
self.assertEqual(format(dt, 'O'), '+0100' if pytz is None else '')
self.assertEqual(format(dt, 'T'), 'CET' if pytz is None else '')
self.assertEqual(format(dt, 'Z'), '3600' if pytz is None else '')
self.assertEqual(format(dt, 'I'), '')
self.assertEqual(format(dt, 'O'), '')
self.assertEqual(format(dt, 'T'), '')
self.assertEqual(format(dt, 'Z'), '')
@requires_tz_support
def test_datetime_with_local_tzinfo(self):

View File

@ -1,21 +1,12 @@
import copy
import datetime
import pickle
import sys
import unittest
import pytz
from django.test import SimpleTestCase, mock, override_settings
from django.utils import timezone
try:
import pytz
except ImportError:
pytz = None
requires_pytz = unittest.skipIf(pytz is None, "this test requires pytz")
if pytz is not None:
CET = pytz.timezone("Europe/Paris")
CET = pytz.timezone("Europe/Paris")
EAT = timezone.get_fixed_timezone(180) # Africa/Nairobi
ICT = timezone.get_fixed_timezone(420) # Asia/Bangkok
@ -24,33 +15,6 @@ PY36 = sys.version_info >= (3, 6)
class TimezoneTests(SimpleTestCase):
def test_localtime(self):
now = datetime.datetime.utcnow().replace(tzinfo=timezone.utc)
local_tz = timezone.LocalTimezone()
with timezone.override(local_tz):
local_now = timezone.localtime(now)
self.assertEqual(local_now.tzinfo, local_tz)
local_now = timezone.localtime(now, timezone=local_tz)
self.assertEqual(local_now.tzinfo, local_tz)
def test_localtime_naive(self):
now = datetime.datetime.now()
if PY36:
self.assertEqual(timezone.localtime(now), now.replace(tzinfo=timezone.LocalTimezone()))
else:
with self.assertRaisesMessage(ValueError, 'astimezone() cannot be applied to a naive datetime'):
timezone.localtime(now)
def test_localtime_out_of_range(self):
local_tz = timezone.LocalTimezone()
long_ago = datetime.datetime(1900, 1, 1, tzinfo=timezone.utc)
try:
timezone.localtime(long_ago, local_tz)
except (OverflowError, ValueError) as exc:
self.assertIn("install pytz", exc.args[0])
else:
self.skipTest("Failed to trigger an OverflowError or ValueError")
def test_now(self):
with override_settings(USE_TZ=True):
self.assertTrue(timezone.is_aware(timezone.now()))
@ -133,18 +97,6 @@ class TimezoneTests(SimpleTestCase):
finally:
timezone.deactivate()
def test_copy(self):
self.assertIsInstance(copy.copy(timezone.UTC()), timezone.UTC)
self.assertIsInstance(copy.copy(timezone.LocalTimezone()), timezone.LocalTimezone)
def test_deepcopy(self):
self.assertIsInstance(copy.deepcopy(timezone.UTC()), timezone.UTC)
self.assertIsInstance(copy.deepcopy(timezone.LocalTimezone()), timezone.LocalTimezone)
def test_pickling_unpickling(self):
self.assertIsInstance(pickle.loads(pickle.dumps(timezone.UTC())), timezone.UTC)
self.assertIsInstance(pickle.loads(pickle.dumps(timezone.LocalTimezone())), timezone.LocalTimezone)
def test_is_aware(self):
self.assertTrue(timezone.is_aware(datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)))
self.assertFalse(timezone.is_aware(datetime.datetime(2011, 9, 1, 13, 20, 30)))
@ -175,7 +127,6 @@ class TimezoneTests(SimpleTestCase):
with self.assertRaisesMessage(ValueError, 'astimezone() cannot be applied to a naive datetime'):
timezone.make_naive(*args)
@requires_pytz
def test_make_aware2(self):
self.assertEqual(
timezone.make_aware(datetime.datetime(2011, 9, 1, 12, 20, 30), CET),
@ -183,7 +134,6 @@ class TimezoneTests(SimpleTestCase):
with self.assertRaises(ValueError):
timezone.make_aware(CET.localize(datetime.datetime(2011, 9, 1, 12, 20, 30)), CET)
@requires_pytz
def test_make_aware_pytz(self):
self.assertEqual(
timezone.make_naive(CET.localize(datetime.datetime(2011, 9, 1, 12, 20, 30)), CET),
@ -202,7 +152,6 @@ class TimezoneTests(SimpleTestCase):
with self.assertRaises(ValueError):
timezone.make_naive(datetime.datetime(2011, 9, 1, 12, 20, 30), CET)
@requires_pytz
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)
@ -216,7 +165,6 @@ class TimezoneTests(SimpleTestCase):
self.assertEqual(std.tzinfo.utcoffset(std), datetime.timedelta(hours=1))
self.assertEqual(dst.tzinfo.utcoffset(dst), datetime.timedelta(hours=2))
@requires_pytz
def test_make_aware_pytz_non_existent(self):
# 2:30 never happened due to DST
non_existent = datetime.datetime(2015, 3, 29, 2, 30)
@ -229,9 +177,3 @@ class TimezoneTests(SimpleTestCase):
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))
# round trip to UTC then back to CET
std = timezone.localtime(timezone.localtime(std, timezone.UTC()), CET)
dst = timezone.localtime(timezone.localtime(dst, timezone.UTC()), CET)
self.assertEqual((std.hour, std.minute), (3, 30))
self.assertEqual((dst.hour, dst.minute), (1, 30))