diff --git a/django/db/backends/base/base.py b/django/db/backends/base/base.py index 595d191dce..81f53685ca 100644 --- a/django/db/backends/base/base.py +++ b/django/db/backends/base/base.py @@ -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): """ diff --git a/django/db/backends/mysql/features.py b/django/db/backends/mysql/features.py index f8c0ddea00..81494efa63 100644 --- a/django/db/backends/mysql/features.py +++ b/django/db/backends/mysql/features.py @@ -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") diff --git a/django/db/backends/oracle/features.py b/django/db/backends/oracle/features.py index 956f7a051b..f09dee8425 100644 --- a/django/db/backends/oracle/features.py +++ b/django/db/backends/oracle/features.py @@ -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 diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index ff78adaffa..66ad27842b 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -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 diff --git a/django/db/backends/sqlite3/features.py b/django/db/backends/sqlite3/features.py index bee26f2da4..43bec87c99 100644 --- a/django/db/backends/sqlite3/features.py +++ b/django/db/backends/sqlite3/features.py @@ -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 diff --git a/django/db/backends/sqlite3/operations.py b/django/db/backends/sqlite3/operations.py index 7f6de932ad..47a26b5165 100644 --- a/django/db/backends/sqlite3/operations.py +++ b/django/db/backends/sqlite3/operations.py @@ -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] diff --git a/django/db/models/functions/datetime.py b/django/db/models/functions/datetime.py index 9cbdcb0231..6294bc3b94 100644 --- a/django/db/models/functions/datetime.py +++ b/django/db/models/functions/datetime.py @@ -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) diff --git a/django/templatetags/tz.py b/django/templatetags/tz.py index 68e40ebb19..4088ba7eb8 100644 --- a/django/templatetags/tz.py +++ b/django/templatetags/tz.py @@ -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:: diff --git a/django/utils/timezone.py b/django/utils/timezone.py index e2c6967607..82c14f4f96 100644 --- a/django/utils/timezone.py +++ b/django/utils/timezone.py @@ -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 "" - - 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() + return pytz.timezone(settings.TIME_ZONE) # 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 diff --git a/docs/internals/contributing/writing-code/coding-style.txt b/docs/internals/contributing/writing-code/coding-style.txt index 137ab3e67f..39f49f10e6 100644 --- a/docs/internals/contributing/writing-code/coding-style.txt +++ b/docs/internals/contributing/writing-code/coding-style.txt @@ -117,9 +117,9 @@ Imports # try/except try: - import pytz + import yaml except ImportError: - pytz = None + yaml = None CONSTANT = 'foo' diff --git a/docs/internals/contributing/writing-code/unit-tests.txt b/docs/internals/contributing/writing-code/unit-tests.txt index d5fdb72561..bb77eced7d 100644 --- a/docs/internals/contributing/writing-code/unit-tests.txt +++ b/docs/internals/contributing/writing-code/unit-tests.txt @@ -232,7 +232,7 @@ dependencies: * numpy_ * Pillow_ * PyYAML_ -* pytz_ +* pytz_ (required) * setuptools_ * memcached_, plus a :ref:`supported Python binding ` * mock_ (for Python 2) diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index f1c3864377..aa25cd2c0e 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -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 diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 223362c0f6..cf4da849c3 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -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 - `, 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 `, 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 +`. 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. diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index 45ffd80fc4..320f5db795 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -966,7 +966,7 @@ appropriate entities. Sets the :ref:`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 `. - 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 `. -.. _pytz: http://pytz.sourceforge.net/ - ``django.utils.translation`` ============================ diff --git a/docs/releases/1.11.txt b/docs/releases/1.11.txt index 653de07caf..27f7481fa6 100644 --- a/docs/releases/1.11.txt +++ b/docs/releases/1.11.txt @@ -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 +`_:: + + 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 +`__ +if there's a use case where you find you can't adapt your code to set a +``TIME_ZONE``. + Miscellaneous ------------- diff --git a/docs/spelling_wordlist b/docs/spelling_wordlist index 8218fe0219..13553bc5f1 100644 --- a/docs/spelling_wordlist +++ b/docs/spelling_wordlist @@ -622,7 +622,6 @@ Pygments pysqlite pythonic Pythonista -pytz qs Québec queryset diff --git a/docs/topics/i18n/timezones.txt b/docs/topics/i18n/timezones.txt index a5848bf33e..ecabefb421 100644 --- a/docs/topics/i18n/timezones.txt +++ b/docs/topics/i18n/timezones.txt @@ -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 ` 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 ` 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 ` 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 ` 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 ` 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=) - 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?** diff --git a/setup.py b/setup.py index 2d7080a131..88e3f72468 100644 --- a/setup.py +++ b/setup.py @@ -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"], diff --git a/tests/admin_widgets/tests.py b/tests/admin_widgets/tests.py index 057771bfa4..443e6c6e18 100644 --- a/tests/admin_widgets/tests.py +++ b/tests/admin_widgets/tests.py @@ -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): diff --git a/tests/cache/tests.py b/tests/cache/tests.py index 122017b54e..832727d53c 100644 --- a/tests/cache/tests.py +++ b/tests/cache/tests.py @@ -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), diff --git a/tests/datetimes/tests.py b/tests/datetimes/tests.py index efef456c43..273333e61c 100644 --- a/tests/datetimes/tests.py +++ b/tests/datetimes/tests.py @@ -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)) diff --git a/tests/db_functions/test_datetime.py b/tests/db_functions/test_datetime.py index 1b44c9a658..f9a9a54c1c 100644 --- a/tests/db_functions/test_datetime.py +++ b/tests/db_functions/test_datetime.py @@ -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): diff --git a/tests/file_storage/tests.py b/tests/file_storage/tests.py index acb663ef7a..5492f105f7 100644 --- a/tests/file_storage/tests.py +++ b/tests/file_storage/tests.py @@ -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) diff --git a/tests/humanize_tests/tests.py b/tests/humanize_tests/tests.py index 22ae426954..a3cc512239 100644 --- a/tests/humanize_tests/tests.py +++ b/tests/humanize_tests/tests.py @@ -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 diff --git a/tests/requirements/base.txt b/tests/requirements/base.txt index 374b836fad..f9cb570c62 100644 --- a/tests/requirements/base.txt +++ b/tests/requirements/base.txt @@ -8,7 +8,6 @@ Pillow PyYAML # pylibmc/libmemcached can't be built on Windows. pylibmc; sys.platform != 'win32' -pytz > dev selenium sqlparse tblib diff --git a/tests/schema/tests.py b/tests/schema/tests.py index 9aeada3e67..567a1f86da 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -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 diff --git a/tests/syndication_tests/tests.py b/tests/syndication_tests/tests.py index 6f4655838a..60285efea7 100644 --- a/tests/syndication_tests/tests.py +++ b/tests/syndication_tests/tests.py @@ -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() diff --git a/tests/timezones/tests.py b/tests/timezones/tests.py index e72fff069b..d95877d227 100644 --- a/tests/timezones/tests.py +++ b/tests/timezones/tests.py @@ -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'}) diff --git a/tests/utils_tests/test_dateformat.py b/tests/utils_tests/test_dateformat.py index bda69fb4cd..bd99b31a42 100644 --- a/tests/utils_tests/test_dateformat.py +++ b/tests/utils_tests/test_dateformat.py @@ -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): diff --git a/tests/utils_tests/test_timezone.py b/tests/utils_tests/test_timezone.py index 1be1d63b2c..2fe5445c48 100644 --- a/tests/utils_tests/test_timezone.py +++ b/tests/utils_tests/test_timezone.py @@ -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))