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:
Carlton Gibson 2021-09-09 15:15:44 +02:00
parent 7132d17de1
commit 306607d5b9
27 changed files with 635 additions and 280 deletions

View file

@ -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'))

View file

@ -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

View file

@ -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``

View file

@ -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)