mirror of
https://github.com/django/django.git
synced 2025-08-03 10:34:04 +00:00
Fixed #32365 -- Made zoneinfo the default timezone implementation.
Thanks to Adam Johnson, Aymeric Augustin, David Smith, Mariusz Felisiak, Nick Pope, and Paul Ganssle for reviews.
This commit is contained in:
parent
7132d17de1
commit
306607d5b9
27 changed files with 635 additions and 280 deletions
|
@ -36,6 +36,24 @@ details on these changes.
|
|||
|
||||
* The ``USE_L10N`` setting will be removed.
|
||||
|
||||
* The ``USE_DEPRECATED_PYTZ`` transitional setting will be removed.
|
||||
|
||||
* Support for ``pytz`` timezones will be removed.
|
||||
|
||||
* The ``is_dst`` argument will be removed from:
|
||||
|
||||
* ``QuerySet.datetimes()``
|
||||
* ``django.utils.timezone.make_aware()``
|
||||
* ``django.db.models.functions.Trunc()``
|
||||
* ``django.db.models.functions.TruncSecond()``
|
||||
* ``django.db.models.functions.TruncMinute()``
|
||||
* ``django.db.models.functions.TruncHour()``
|
||||
* ``django.db.models.functions.TruncDay()``
|
||||
* ``django.db.models.functions.TruncWeek()``
|
||||
* ``django.db.models.functions.TruncMonth()``
|
||||
* ``django.db.models.functions.TruncQuarter()``
|
||||
* ``django.db.models.functions.TruncYear()``
|
||||
|
||||
.. _deprecation-removed-in-4.1:
|
||||
|
||||
4.1
|
||||
|
|
|
@ -242,7 +242,8 @@ Takes an ``expression`` representing a ``DateField``, ``DateTimeField``,
|
|||
of the date referenced by ``lookup_name`` as an ``IntegerField``.
|
||||
Django usually uses the databases' extract function, so you may use any
|
||||
``lookup_name`` that your database supports. A ``tzinfo`` subclass, usually
|
||||
provided by ``pytz``, can be passed to extract a value in a specific timezone.
|
||||
provided by :mod:`zoneinfo`, can be passed to extract a value in a specific
|
||||
timezone.
|
||||
|
||||
Given the datetime ``2015-06-15 23:30:01.000321+00:00``, the built-in
|
||||
``lookup_name``\s return:
|
||||
|
@ -450,8 +451,8 @@ to that timezone before the value is extracted. The example below converts to
|
|||
the Melbourne timezone (UTC +10:00), which changes the day, weekday, and hour
|
||||
values that are returned::
|
||||
|
||||
>>> import pytz
|
||||
>>> melb = pytz.timezone('Australia/Melbourne') # UTC+10:00
|
||||
>>> import zoneinfo
|
||||
>>> melb = zoneinfo.ZoneInfo('Australia/Melbourne') # UTC+10:00
|
||||
>>> with timezone.override(melb):
|
||||
... Experiment.objects.annotate(
|
||||
... day=ExtractDay('start_datetime'),
|
||||
|
@ -466,8 +467,8 @@ values that are returned::
|
|||
Explicitly passing the timezone to the ``Extract`` function behaves in the same
|
||||
way, and takes priority over an active timezone::
|
||||
|
||||
>>> import pytz
|
||||
>>> melb = pytz.timezone('Australia/Melbourne')
|
||||
>>> import zoneinfo
|
||||
>>> melb = zoneinfo.ZoneInfo('Australia/Melbourne')
|
||||
>>> Experiment.objects.annotate(
|
||||
... day=ExtractDay('start_datetime', tzinfo=melb),
|
||||
... weekday=ExtractWeekDay('start_datetime', tzinfo=melb),
|
||||
|
@ -517,12 +518,16 @@ part, and an ``output_field`` that's either ``DateTimeField()``,
|
|||
``TimeField()``, or ``DateField()``. It returns a datetime, date, or time
|
||||
depending on ``output_field``, with fields up to ``kind`` set to their minimum
|
||||
value. If ``output_field`` is omitted, it will default to the ``output_field``
|
||||
of ``expression``. A ``tzinfo`` subclass, usually provided by ``pytz``, can be
|
||||
passed to truncate a value in a specific timezone.
|
||||
of ``expression``. A ``tzinfo`` subclass, usually provided by :mod:`zoneinfo`,
|
||||
can be passed to truncate a value in a specific timezone.
|
||||
|
||||
The ``is_dst`` parameter indicates whether or not ``pytz`` should interpret
|
||||
nonexistent and ambiguous datetimes in daylight saving time. By default (when
|
||||
``is_dst=None``), ``pytz`` raises an exception for such datetimes.
|
||||
.. deprecated:: 4.0
|
||||
|
||||
The ``is_dst`` parameter indicates whether or not ``pytz`` should interpret
|
||||
nonexistent and ambiguous datetimes in daylight saving time. By default
|
||||
(when ``is_dst=None``), ``pytz`` raises an exception for such datetimes.
|
||||
|
||||
The ``is_dst`` parameter is deprecated and will be removed in Django 5.0.
|
||||
|
||||
Given the datetime ``2015-06-15 14:30:50.000321+00:00``, the built-in ``kind``\s
|
||||
return:
|
||||
|
@ -607,6 +612,10 @@ Usage example::
|
|||
|
||||
.. attribute:: kind = 'quarter'
|
||||
|
||||
.. deprecated:: 4.0
|
||||
|
||||
The ``is_dst`` parameter is deprecated and will be removed in Django 5.0.
|
||||
|
||||
These are logically equivalent to ``Trunc('date_field', kind)``. They truncate
|
||||
all parts of the date up to ``kind`` which allows grouping or filtering dates
|
||||
with less precision. ``expression`` can have an ``output_field`` of either
|
||||
|
@ -634,8 +643,8 @@ that deal with date-parts can be used with ``DateField``::
|
|||
2014-01-01 1
|
||||
2015-01-01 2
|
||||
|
||||
>>> import pytz
|
||||
>>> melb = pytz.timezone('Australia/Melbourne')
|
||||
>>> import zoneinfo
|
||||
>>> melb = zoneinfo.ZoneInfo('Australia/Melbourne')
|
||||
>>> experiments_per_month = Experiment.objects.annotate(
|
||||
... month=TruncMonth('start_datetime', tzinfo=melb)).values('month').annotate(
|
||||
... experiments=Count('id'))
|
||||
|
@ -691,6 +700,10 @@ truncate function. It's also registered as a transform on ``DateTimeField`` as
|
|||
|
||||
.. attribute:: kind = 'second'
|
||||
|
||||
.. deprecated:: 4.0
|
||||
|
||||
The ``is_dst`` parameter is deprecated and will be removed in Django 5.0.
|
||||
|
||||
These are logically equivalent to ``Trunc('datetime_field', kind)``. They
|
||||
truncate all parts of the date up to ``kind`` and allow grouping or filtering
|
||||
datetimes with less precision. ``expression`` must have an ``output_field`` of
|
||||
|
@ -704,10 +717,10 @@ Usage example::
|
|||
... TruncDate, TruncDay, TruncHour, TruncMinute, TruncSecond,
|
||||
... )
|
||||
>>> from django.utils import timezone
|
||||
>>> import pytz
|
||||
>>> import zoneinfo
|
||||
>>> start1 = datetime(2014, 6, 15, 14, 30, 50, 321, tzinfo=timezone.utc)
|
||||
>>> Experiment.objects.create(start_datetime=start1, start_date=start1.date())
|
||||
>>> melb = pytz.timezone('Australia/Melbourne')
|
||||
>>> melb = zoneinfo.ZoneInfo('Australia/Melbourne')
|
||||
>>> Experiment.objects.annotate(
|
||||
... date=TruncDate('start_datetime'),
|
||||
... day=TruncDay('start_datetime', tzinfo=melb),
|
||||
|
@ -716,10 +729,10 @@ Usage example::
|
|||
... second=TruncSecond('start_datetime'),
|
||||
... ).values('date', 'day', 'hour', 'minute', 'second').get()
|
||||
{'date': datetime.date(2014, 6, 15),
|
||||
'day': datetime.datetime(2014, 6, 16, 0, 0, tzinfo=<DstTzInfo 'Australia/Melbourne' AEST+10:00:00 STD>),
|
||||
'hour': datetime.datetime(2014, 6, 16, 0, 0, tzinfo=<DstTzInfo 'Australia/Melbourne' AEST+10:00:00 STD>),
|
||||
'minute': 'minute': datetime.datetime(2014, 6, 15, 14, 30, tzinfo=<UTC>),
|
||||
'second': datetime.datetime(2014, 6, 15, 14, 30, 50, tzinfo=<UTC>)
|
||||
'day': datetime.datetime(2014, 6, 16, 0, 0, tzinfo=zoneinfo.ZoneInfo('Australia/Melbourne')),
|
||||
'hour': datetime.datetime(2014, 6, 16, 0, 0, tzinfo=zoneinfo.ZoneInfo('Australia/Melbourne')),
|
||||
'minute': 'minute': datetime.datetime(2014, 6, 15, 14, 30, tzinfo=zoneinfo.ZoneInfo('UTC')),
|
||||
'second': datetime.datetime(2014, 6, 15, 14, 30, 50, tzinfo=zoneinfo.ZoneInfo('UTC'))
|
||||
}
|
||||
|
||||
``TimeField`` truncation
|
||||
|
@ -740,6 +753,10 @@ Usage example::
|
|||
|
||||
.. attribute:: kind = 'second'
|
||||
|
||||
.. deprecated:: 4.0
|
||||
|
||||
The ``is_dst`` parameter is deprecated and will be removed in Django 5.0.
|
||||
|
||||
These are logically equivalent to ``Trunc('time_field', kind)``. They truncate
|
||||
all parts of the time up to ``kind`` which allows grouping or filtering times
|
||||
with less precision. ``expression`` can have an ``output_field`` of either
|
||||
|
@ -767,8 +784,8 @@ that deal with time-parts can be used with ``TimeField``::
|
|||
14:00:00 2
|
||||
17:00:00 1
|
||||
|
||||
>>> import pytz
|
||||
>>> melb = pytz.timezone('Australia/Melbourne')
|
||||
>>> import zoneinfo
|
||||
>>> melb = zoneinfo.ZoneInfo('Australia/Melbourne')
|
||||
>>> experiments_per_hour = Experiment.objects.annotate(
|
||||
... hour=TruncHour('start_datetime', tzinfo=melb),
|
||||
... ).values('hour').annotate(experiments=Count('id'))
|
||||
|
|
|
@ -834,6 +834,10 @@ object. If it's ``None``, Django uses the :ref:`current time zone
|
|||
ambiguous datetimes in daylight saving time. By default (when ``is_dst=None``),
|
||||
``pytz`` raises an exception for such datetimes.
|
||||
|
||||
.. deprecated:: 4.0
|
||||
|
||||
The ``is_dst`` parameter is deprecated and will be removed in Django 5.0.
|
||||
|
||||
.. _database-time-zone-definitions:
|
||||
|
||||
.. note::
|
||||
|
@ -842,13 +846,11 @@ ambiguous datetimes in daylight saving time. By default (when ``is_dst=None``),
|
|||
As a consequence, your database must be able to interpret the value of
|
||||
``tzinfo.tzname(None)``. This translates into the following requirements:
|
||||
|
||||
- SQLite: no requirements. Conversions are performed in Python with pytz_
|
||||
(installed when you install Django).
|
||||
- SQLite: no requirements. Conversions are performed in Python.
|
||||
- PostgreSQL: no requirements (see `Time Zones`_).
|
||||
- Oracle: no requirements (see `Choosing a Time Zone File`_).
|
||||
- MySQL: load the time zone tables with `mysql_tzinfo_to_sql`_.
|
||||
|
||||
.. _pytz: http://pytz.sourceforge.net/
|
||||
.. _Time Zones: https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-TIMEZONES
|
||||
.. _Choosing a Time Zone File: https://docs.oracle.com/en/database/oracle/
|
||||
oracle-database/18/nlspg/datetime-data-types-and-time-zone-support.html
|
||||
|
|
|
@ -677,8 +677,8 @@ When :setting:`USE_TZ` is ``False``, it is an error to set this option.
|
|||
otherwise, you should leave this option unset. It's best to store datetimes
|
||||
in UTC because it avoids ambiguous or nonexistent datetimes during daylight
|
||||
saving time changes. Also, receiving datetimes in UTC keeps datetime
|
||||
arithmetic simple — there's no need for the ``normalize()`` method provided
|
||||
by pytz.
|
||||
arithmetic simple — there's no need to consider potential offset changes
|
||||
over a DST transition.
|
||||
|
||||
* If you're connecting to a third-party database that stores datetimes in a
|
||||
local time rather than UTC, then you must set this option to the
|
||||
|
@ -695,8 +695,8 @@ When :setting:`USE_TZ` is ``False``, it is an error to set this option.
|
|||
as ``date_trunc``, because their results depend on the time zone.
|
||||
|
||||
However, this has a downside: receiving all datetimes in local time makes
|
||||
datetime arithmetic more tricky — you must call the ``normalize()`` method
|
||||
provided by pytz after each operation.
|
||||
datetime arithmetic more tricky — you must account for possible offset
|
||||
changes over DST transitions.
|
||||
|
||||
Consider converting to local time explicitly with ``AT TIME ZONE`` in raw SQL
|
||||
queries instead of setting the ``TIME_ZONE`` option.
|
||||
|
@ -2758,6 +2758,23 @@ the correct environment.
|
|||
|
||||
.. _list of time zones: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
||||
|
||||
.. setting:: USE_DEPRECATED_PYTZ
|
||||
|
||||
``USE_DEPRECATED_PYTZ``
|
||||
-----------------------
|
||||
|
||||
.. versionadded:: 4.0
|
||||
|
||||
Default: ``False``
|
||||
|
||||
A boolean that specifies whether to use ``pytz``, rather than :mod:`zoneinfo`,
|
||||
as the default time zone implementation.
|
||||
|
||||
.. deprecated:: 4.0
|
||||
|
||||
This transitional setting is deprecated. Support for using ``pytz`` will be
|
||||
removed in Django 5.0.
|
||||
|
||||
.. setting:: USE_I18N
|
||||
|
||||
``USE_I18N``
|
||||
|
|
|
@ -941,24 +941,30 @@ appropriate entities.
|
|||
:class:`~datetime.datetime`. If ``timezone`` is set to ``None``, it
|
||||
defaults to the :ref:`current time zone <default-current-time-zone>`.
|
||||
|
||||
When using ``pytz``, 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.
|
||||
.. deprecated:: 4.0
|
||||
|
||||
When using ``pytz``, the ``pytz.NonExistentTimeError`` exception is raised
|
||||
if you try to make ``value`` aware during a DST transition such that the
|
||||
time never occurred. For example, if the 2:00 hour is skipped during a DST
|
||||
transition, trying to make 2:30 aware in that time zone will raise an
|
||||
exception. To avoid that you can use ``is_dst`` to specify how
|
||||
``make_aware()`` should interpret such a nonexistent time. If
|
||||
``is_dst=True`` then the above time would be interpreted as 2:30 DST time
|
||||
(equivalent to 1:30 local time). Conversely, if ``is_dst=False`` the time
|
||||
would be interpreted as 2:30 standard time (equivalent to 3:30 local time).
|
||||
When using ``pytz``, 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.
|
||||
|
||||
The ``is_dst`` parameter has no effect when using non-``pytz`` timezone
|
||||
implementations.
|
||||
When using ``pytz``, the ``pytz.NonExistentTimeError`` exception is
|
||||
raised if you try to make ``value`` aware during a DST transition such
|
||||
that the time never occurred. For example, if the 2:00 hour is skipped
|
||||
during a DST transition, trying to make 2:30 aware in that time zone
|
||||
will raise an exception. To avoid that you can use ``is_dst`` to
|
||||
specify how ``make_aware()`` should interpret such a nonexistent time.
|
||||
If ``is_dst=True`` then the above time would be interpreted as 2:30 DST
|
||||
time (equivalent to 1:30 local time). Conversely, if ``is_dst=False``
|
||||
the time would be interpreted as 2:30 standard time (equivalent to 3:30
|
||||
local time).
|
||||
|
||||
The ``is_dst`` parameter has no effect when using non-``pytz`` timezone
|
||||
implementations.
|
||||
|
||||
The ``is_dst`` parameter is deprecated and will be removed in Django
|
||||
5.0.
|
||||
|
||||
.. function:: make_naive(value, timezone=None)
|
||||
|
||||
|
|
|
@ -28,6 +28,46 @@ The Django 3.2.x series is the last to support Python 3.6 and 3.7.
|
|||
What's new in Django 4.0
|
||||
========================
|
||||
|
||||
``zoneinfo`` default timezone implementation
|
||||
--------------------------------------------
|
||||
|
||||
The Python standard library's :mod:`zoneinfo` is now the default timezone
|
||||
implementation in Django.
|
||||
|
||||
This is the next step in the migration from using ``pytz`` to using
|
||||
:mod:`zoneinfo`. Django 3.2 allowed the use of non-``pytz`` time zones. Django
|
||||
4.0 makes ``zoneinfo`` the default implementation. Support for ``pytz`` is now
|
||||
deprecated and will be removed in Django 5.0.
|
||||
|
||||
:mod:`zoneinfo` is part of the Python standard library from Python 3.9. The
|
||||
``backports.zoneinfo`` package is automatically installed alongside Django if
|
||||
you are using Python 3.8.
|
||||
|
||||
The move to ``zoneinfo`` should be largely transparent. Selection of the
|
||||
current timezone, conversion of datetime instances to the current timezone in
|
||||
forms and templates, as well as operations on aware datetimes in UTC are
|
||||
unaffected.
|
||||
|
||||
However, if you are you are working with non-UTC time zones, and using the
|
||||
``pytz`` ``normalize()`` and ``localize()`` APIs, possibly with the
|
||||
:setting:`DATABASE-TIME_ZONE` setting, you will need to audit your code, since
|
||||
``pytz`` and ``zoneinfo`` are not entirely equivalent.
|
||||
|
||||
To give time for such an audit, the transitional :setting:`USE_DEPRECATED_PYTZ`
|
||||
setting allows continued use of ``pytz`` during the 4.x release cycle. This
|
||||
setting will be removed in Django 5.0.
|
||||
|
||||
In addition, a `pytz_deprecation_shim`_ package, created by the ``zoneinfo``
|
||||
author, can be used to assist with the migration from ``pytz``. This package
|
||||
provides shims to help you safely remove ``pytz``, and has a detailed
|
||||
`migration guide`_ showing how to move to the new ``zoneinfo`` APIs.
|
||||
|
||||
Using `pytz_deprecation_shim`_ and the :setting:`USE_DEPRECATED_PYTZ`
|
||||
transitional setting is recommended if you need a gradual update path.
|
||||
|
||||
.. _pytz_deprecation_shim: https://pytz-deprecation-shim.readthedocs.io/en/latest/index.html
|
||||
.. _migration guide: https://pytz-deprecation-shim.readthedocs.io/en/latest/migration.html
|
||||
|
||||
Functional unique constraints
|
||||
-----------------------------
|
||||
|
||||
|
@ -595,11 +635,37 @@ Miscellaneous
|
|||
* The default value of the ``USE_L10N`` setting is changed to ``True``. See the
|
||||
:ref:`Localization section <use_l10n_deprecation>` above for more details.
|
||||
|
||||
* As part of the :ref:`move to zoneinfo <whats-new-4.0>`,
|
||||
:attr:`django.utils.timezone.utc` is changed to alias
|
||||
:attr:`datetime.timezone.utc`.
|
||||
|
||||
.. _deprecated-features-4.0:
|
||||
|
||||
Features deprecated in 4.0
|
||||
==========================
|
||||
|
||||
Use of ``pytz`` time zones
|
||||
--------------------------
|
||||
|
||||
As part of the :ref:`move to zoneinfo <whats-new-4.0>`, use of ``pytz`` time
|
||||
zones is deprecated.
|
||||
|
||||
Accordingly, the ``is_dst`` arguments to the following are also deprecated:
|
||||
|
||||
* :meth:`django.db.models.query.QuerySet.datetimes()`
|
||||
* :func:`django.db.models.functions.Trunc()`
|
||||
* :func:`django.db.models.functions.TruncSecond()`
|
||||
* :func:`django.db.models.functions.TruncMinute()`
|
||||
* :func:`django.db.models.functions.TruncHour()`
|
||||
* :func:`django.db.models.functions.TruncDay()`
|
||||
* :func:`django.db.models.functions.TruncWeek()`
|
||||
* :func:`django.db.models.functions.TruncMonth()`
|
||||
* :func:`django.db.models.functions.TruncQuarter()`
|
||||
* :func:`django.db.models.functions.TruncYear()`
|
||||
* :func:`django.utils.timezone.make_aware()`
|
||||
|
||||
Support for use of ``pytz`` will be removed in Django 5.0.
|
||||
|
||||
Time zone support
|
||||
-----------------
|
||||
|
||||
|
|
|
@ -19,10 +19,9 @@ practice to store data in UTC in your database. The main reason is daylight
|
|||
saving time (DST). Many countries have a system of DST, where clocks are moved
|
||||
forward in spring and backward in autumn. If you're working in local time,
|
||||
you're likely to encounter errors twice a year, when the transitions happen.
|
||||
(The pytz_ documentation discusses `these issues`_ in greater detail.) This
|
||||
probably doesn't matter for your blog, but it's a problem if you over-bill or
|
||||
under-bill your customers by one hour, twice a year, every year. The solution
|
||||
to this problem is to use UTC in the code and use local time only when
|
||||
This probably doesn't matter for your blog, but it's a problem if you over bill
|
||||
or under bill your customers by one hour, twice a year, every year. The
|
||||
solution 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 =
|
||||
|
@ -32,15 +31,20 @@ True <USE_TZ>` in your settings file.
|
|||
|
||||
In Django 5.0, time zone support will be enabled by default.
|
||||
|
||||
By default, time zone support uses pytz_, which is installed when you install
|
||||
Django; Django also supports the use of other time zone implementations like
|
||||
:mod:`zoneinfo` by passing :class:`~datetime.tzinfo` objects directly to
|
||||
functions in :mod:`django.utils.timezone`.
|
||||
Time zone support uses :mod:`zoneinfo`, which is part of the Python standard
|
||||
library from Python 3.9. The ``backports.zoneinfo`` package is automatically
|
||||
installed alongside Django if you are using Python 3.8.
|
||||
|
||||
.. versionchanged:: 3.2
|
||||
|
||||
Support for non-``pytz`` timezone implementations was added.
|
||||
|
||||
.. versionchanged:: 4.0
|
||||
|
||||
:mod:`zoneinfo` was made the default timezone implementation. You may
|
||||
continue to use `pytz`_ during the 4.x release cycle via the
|
||||
:setting:`USE_DEPRECATED_PYTZ` setting.
|
||||
|
||||
.. note::
|
||||
|
||||
The default :file:`settings.py` file created by :djadmin:`django-admin
|
||||
|
@ -88,8 +92,8 @@ should be aware too. In this mode, the example above becomes::
|
|||
Dealing with aware datetime objects isn't always intuitive. For instance,
|
||||
the ``tzinfo`` argument of the standard datetime constructor doesn't work
|
||||
reliably for time zones with DST. Using UTC is generally safe; if you're
|
||||
using other time zones, you should review the `pytz`_ documentation
|
||||
carefully.
|
||||
using other time zones, you should review the :mod:`zoneinfo`
|
||||
documentation carefully.
|
||||
|
||||
.. note::
|
||||
|
||||
|
@ -113,8 +117,10 @@ 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. That's why you should
|
||||
always create aware datetime objects when time zone support is enabled.
|
||||
ambiguous. That's why you should always create aware datetime objects when time
|
||||
zone support is enabled. (See the :mod:`Using ZoneInfo section of the zoneinfo
|
||||
docs <zoneinfo>` for examples using the ``fold`` attribute to specify the
|
||||
offset that should apply to a datetime during a DST transition.)
|
||||
|
||||
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
|
||||
|
@ -163,16 +169,16 @@ selection logic that makes sense for you.
|
|||
|
||||
Most websites that care about time zones ask users in which time zone they live
|
||||
and store this information in the user's profile. For anonymous users, they use
|
||||
the time zone of their primary audience or UTC. pytz_ provides helpers_, like a
|
||||
list of time zones per country, that you can use to pre-select the most likely
|
||||
choices.
|
||||
the time zone of their primary audience or UTC.
|
||||
:func:`zoneinfo.available_timezones` provides a set of available timezones that
|
||||
you can use to build a map from likely locations to time zones.
|
||||
|
||||
Here's an example that stores the current timezone in the session. (It skips
|
||||
error handling entirely for the sake of simplicity.)
|
||||
|
||||
Add the following middleware to :setting:`MIDDLEWARE`::
|
||||
|
||||
import pytz
|
||||
import zoneinfo
|
||||
|
||||
from django.utils import timezone
|
||||
|
||||
|
@ -183,7 +189,7 @@ Add the following middleware to :setting:`MIDDLEWARE`::
|
|||
def __call__(self, request):
|
||||
tzname = request.session.get('django_timezone')
|
||||
if tzname:
|
||||
timezone.activate(pytz.timezone(tzname))
|
||||
timezone.activate(zoneinfo.ZoneInfo(tzname))
|
||||
else:
|
||||
timezone.deactivate()
|
||||
return self.get_response(request)
|
||||
|
@ -192,12 +198,19 @@ Create a view that can set the current timezone::
|
|||
|
||||
from django.shortcuts import redirect, render
|
||||
|
||||
# Prepare a map of common locations to timezone choices you wish to offer.
|
||||
common_timezones = {
|
||||
'London': 'Europe/London',
|
||||
'Paris': 'Europe/Paris',
|
||||
'New York': 'America/New_York',
|
||||
}
|
||||
|
||||
def set_timezone(request):
|
||||
if request.method == 'POST':
|
||||
request.session['django_timezone'] = request.POST['timezone']
|
||||
return redirect('/')
|
||||
else:
|
||||
return render(request, 'template.html', {'timezones': pytz.common_timezones})
|
||||
return render(request, 'template.html', {'timezones': common_timezones})
|
||||
|
||||
Include a form in ``template.html`` that will ``POST`` to this view:
|
||||
|
||||
|
@ -209,8 +222,8 @@ Include a form in ``template.html`` that will ``POST`` to this view:
|
|||
{% csrf_token %}
|
||||
<label for="timezone">Time zone:</label>
|
||||
<select name="timezone">
|
||||
{% for tz in timezones %}
|
||||
<option value="{{ tz }}"{% if tz == TIME_ZONE %} selected{% endif %}>{{ tz }}</option>
|
||||
{% for city, tz in timezones %}
|
||||
<option value="{{ tz }}"{% if tz == TIME_ZONE %} selected{% endif %}>{{ city }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="submit" value="Set">
|
||||
|
@ -225,9 +238,8 @@ When you enable time zone support, Django interprets datetimes entered in
|
|||
forms in the :ref:`current time zone <default-current-time-zone>` and returns
|
||||
aware datetime objects in ``cleaned_data``.
|
||||
|
||||
If the current time zone raises an exception for datetimes that don't exist or
|
||||
are ambiguous because they fall in a DST transition (the timezones provided by
|
||||
pytz_ do this), such datetimes will be reported as invalid values.
|
||||
Converted datetimes that don't exist or are ambiguous because they fall in a
|
||||
DST transition will be reported as invalid values.
|
||||
|
||||
.. _time-zones-in-templates:
|
||||
|
||||
|
@ -583,20 +595,20 @@ Troubleshooting
|
|||
None of this is true in a time zone aware environment::
|
||||
|
||||
>>> import datetime
|
||||
>>> import pytz
|
||||
>>> paris_tz = pytz.timezone("Europe/Paris")
|
||||
>>> new_york_tz = pytz.timezone("America/New_York")
|
||||
>>> paris = paris_tz.localize(datetime.datetime(2012, 3, 3, 1, 30))
|
||||
# This is the correct way to convert between time zones with pytz.
|
||||
>>> new_york = new_york_tz.normalize(paris.astimezone(new_york_tz))
|
||||
>>> import zoneinfo
|
||||
>>> paris_tz = zoneinfo.ZoneInfo("Europe/Paris")
|
||||
>>> new_york_tz = zoneinfo.ZoneInfo("America/New_York")
|
||||
>>> paris = datetime.datetime(2012, 3, 3, 1, 30, tzinfo=paris_tz)
|
||||
# This is the correct way to convert between time zones.
|
||||
>>> new_york = paris.astimezone(new_york_tz)
|
||||
>>> paris == new_york, paris.date() == new_york.date()
|
||||
(True, False)
|
||||
>>> paris - new_york, paris.date() - new_york.date()
|
||||
(datetime.timedelta(0), datetime.timedelta(1))
|
||||
>>> paris
|
||||
datetime.datetime(2012, 3, 3, 1, 30, tzinfo=<DstTzInfo 'Europe/Paris' CET+1:00:00 STD>)
|
||||
datetime.datetime(2012, 3, 3, 1, 30, tzinfo=zoneinfo.ZoneInfo(key='Europe/Paris'))
|
||||
>>> new_york
|
||||
datetime.datetime(2012, 3, 2, 19, 30, tzinfo=<DstTzInfo 'America/New_York' EST-1 day, 19:00:00 STD>)
|
||||
datetime.datetime(2012, 3, 2, 19, 30, tzinfo=zoneinfo.ZoneInfo(key='America/New_York'))
|
||||
|
||||
As this example shows, the same datetime has a different date, depending on
|
||||
the time zone in which it is represented. But the real problem is more
|
||||
|
@ -621,14 +633,13 @@ Troubleshooting
|
|||
will be the current timezone::
|
||||
|
||||
>>> from django.utils import timezone
|
||||
>>> timezone.activate(pytz.timezone("Asia/Singapore"))
|
||||
>>> timezone.activate(zoneinfo.ZoneInfo("Asia/Singapore"))
|
||||
# For this example, we set the time zone to Singapore, but here's how
|
||||
# you would obtain the current time zone in the general case.
|
||||
>>> current_tz = timezone.get_current_timezone()
|
||||
# Again, this is the correct way to convert between time zones with pytz.
|
||||
>>> local = current_tz.normalize(paris.astimezone(current_tz))
|
||||
>>> local = paris.astimezone(current_tz)
|
||||
>>> local
|
||||
datetime.datetime(2012, 3, 3, 8, 30, tzinfo=<DstTzInfo 'Asia/Singapore' SGT+8:00:00 STD>)
|
||||
datetime.datetime(2012, 3, 3, 8, 30, tzinfo=zoneinfo.ZoneInfo(key='Asia/Singapore'))
|
||||
>>> local.date()
|
||||
datetime.date(2012, 3, 3)
|
||||
|
||||
|
@ -645,18 +656,14 @@ Usage
|
|||
``"Europe/Helsinki"`` **time zone. How do I turn that into an aware
|
||||
datetime?**
|
||||
|
||||
This is exactly what pytz_ is for.
|
||||
Here you need to create the required ``ZoneInfo`` instance and attach it to
|
||||
the naïve datetime::
|
||||
|
||||
>>> import zoneinfo
|
||||
>>> from django.utils.dateparse import parse_datetime
|
||||
>>> naive = parse_datetime("2012-02-21 10:28:45")
|
||||
>>> import pytz
|
||||
>>> pytz.timezone("Europe/Helsinki").localize(naive, is_dst=None)
|
||||
datetime.datetime(2012, 2, 21, 10, 28, 45, tzinfo=<DstTzInfo 'Europe/Helsinki' EET+2:00:00 STD>)
|
||||
|
||||
Note that ``localize`` is a pytz extension to the :class:`~datetime.tzinfo`
|
||||
API. Also, you may want to catch ``pytz.InvalidTimeError``. The
|
||||
documentation of pytz contains `more examples`_. You should review it
|
||||
before attempting to manipulate aware datetimes.
|
||||
>>> naive.replace(tzinfo=zoneinfo.ZoneInfo("Europe/Helsinki"))
|
||||
datetime.datetime(2012, 2, 21, 10, 28, 45, tzinfo=zoneinfo.ZoneInfo(key='Europe/Helsinki'))
|
||||
|
||||
#. **How can I obtain the local time in the current time zone?**
|
||||
|
||||
|
@ -677,19 +684,14 @@ Usage
|
|||
|
||||
>>> from django.utils import timezone
|
||||
>>> timezone.localtime(timezone.now())
|
||||
datetime.datetime(2012, 3, 3, 20, 10, 53, 873365, tzinfo=<DstTzInfo 'Europe/Paris' CET+1:00:00 STD>)
|
||||
datetime.datetime(2012, 3, 3, 20, 10, 53, 873365, tzinfo=zoneinfo.ZoneInfo(key='Europe/Paris'))
|
||||
|
||||
In this example, the current time zone is ``"Europe/Paris"``.
|
||||
|
||||
#. **How can I see all available time zones?**
|
||||
|
||||
pytz_ provides helpers_, including a list of current time zones and a list
|
||||
of all available time zones -- some of which are only of historical
|
||||
interest. :mod:`zoneinfo` also provides similar functionality via
|
||||
:func:`zoneinfo.available_timezones`.
|
||||
:func:`zoneinfo.available_timezones` provides the set of all valid keys for
|
||||
IANA time zones available to your system. See the docs for usage
|
||||
considerations.
|
||||
|
||||
.. _pytz: http://pytz.sourceforge.net/
|
||||
.. _more examples: http://pytz.sourceforge.net/#example-usage
|
||||
.. _these issues: http://pytz.sourceforge.net/#problems-with-localtime
|
||||
.. _helpers: http://pytz.sourceforge.net/#helpers
|
||||
.. _tz database: https://en.wikipedia.org/wiki/Tz_database
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue