Refs #29724 -- Added is_dst parameter to QuerySet.datetimes().
Thanks Simon Charette for the review and Mariusz Felisiak for tests.
This commit is contained in:
parent
2695ac8e04
commit
53b6a466d8
|
@ -875,7 +875,7 @@ class QuerySet:
|
||||||
'datefield', flat=True
|
'datefield', flat=True
|
||||||
).distinct().filter(plain_field__isnull=False).order_by(('-' if order == 'DESC' else '') + 'datefield')
|
).distinct().filter(plain_field__isnull=False).order_by(('-' if order == 'DESC' else '') + 'datefield')
|
||||||
|
|
||||||
def datetimes(self, field_name, kind, order='ASC', tzinfo=None):
|
def datetimes(self, field_name, kind, order='ASC', tzinfo=None, is_dst=None):
|
||||||
"""
|
"""
|
||||||
Return a list of datetime objects representing all available
|
Return a list of datetime objects representing all available
|
||||||
datetimes for the given field_name, scoped to 'kind'.
|
datetimes for the given field_name, scoped to 'kind'.
|
||||||
|
@ -890,7 +890,13 @@ class QuerySet:
|
||||||
else:
|
else:
|
||||||
tzinfo = None
|
tzinfo = None
|
||||||
return self.annotate(
|
return self.annotate(
|
||||||
datetimefield=Trunc(field_name, kind, output_field=DateTimeField(), tzinfo=tzinfo),
|
datetimefield=Trunc(
|
||||||
|
field_name,
|
||||||
|
kind,
|
||||||
|
output_field=DateTimeField(),
|
||||||
|
tzinfo=tzinfo,
|
||||||
|
is_dst=is_dst,
|
||||||
|
),
|
||||||
plain_field=F(field_name)
|
plain_field=F(field_name)
|
||||||
).values_list(
|
).values_list(
|
||||||
'datetimefield', flat=True
|
'datetimefield', flat=True
|
||||||
|
|
|
@ -759,7 +759,7 @@ Examples::
|
||||||
``datetimes()``
|
``datetimes()``
|
||||||
~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. method:: datetimes(field_name, kind, order='ASC', tzinfo=None)
|
.. method:: datetimes(field_name, kind, order='ASC', tzinfo=None, is_dst=None)
|
||||||
|
|
||||||
Returns a ``QuerySet`` that evaluates to a list of :class:`datetime.datetime`
|
Returns a ``QuerySet`` that evaluates to a list of :class:`datetime.datetime`
|
||||||
objects representing all available dates of a particular kind within the
|
objects representing all available dates of a particular kind within the
|
||||||
|
@ -781,6 +781,14 @@ object. If it's ``None``, Django uses the :ref:`current time zone
|
||||||
<default-current-time-zone>`. It has no effect when :setting:`USE_TZ` is
|
<default-current-time-zone>`. It has no effect when :setting:`USE_TZ` is
|
||||||
``False``.
|
``False``.
|
||||||
|
|
||||||
|
``is_dst`` 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.
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
|
The ``is_dst`` parameter was added.
|
||||||
|
|
||||||
.. _database-time-zone-definitions:
|
.. _database-time-zone-definitions:
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
|
@ -324,6 +324,9 @@ Models
|
||||||
:meth:`~.RelatedManager.set` methods now accept callables as values in the
|
:meth:`~.RelatedManager.set` methods now accept callables as values in the
|
||||||
``through_defaults`` argument.
|
``through_defaults`` argument.
|
||||||
|
|
||||||
|
* The new ``is_dst`` parameter of the :meth:`.QuerySet.datetimes` determines
|
||||||
|
the treatment of nonexistent and ambiguous datetimes.
|
||||||
|
|
||||||
Pagination
|
Pagination
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
import pytz
|
||||||
|
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
@ -89,6 +91,40 @@ class DateTimesTests(TestCase):
|
||||||
qs = Article.objects.datetimes('pub_date', 'second')
|
qs = Article.objects.datetimes('pub_date', 'second')
|
||||||
self.assertEqual(qs[0], now)
|
self.assertEqual(qs[0], now)
|
||||||
|
|
||||||
|
@override_settings(USE_TZ=True, TIME_ZONE='UTC')
|
||||||
|
def test_datetimes_ambiguous_and_invalid_times(self):
|
||||||
|
sao = pytz.timezone('America/Sao_Paulo')
|
||||||
|
utc = pytz.UTC
|
||||||
|
article = Article.objects.create(
|
||||||
|
title='Article 1',
|
||||||
|
pub_date=utc.localize(datetime.datetime(2016, 2, 21, 1)),
|
||||||
|
)
|
||||||
|
Comment.objects.create(
|
||||||
|
article=article,
|
||||||
|
pub_date=utc.localize(datetime.datetime(2016, 10, 16, 13)),
|
||||||
|
)
|
||||||
|
with timezone.override(sao):
|
||||||
|
with self.assertRaisesMessage(pytz.AmbiguousTimeError, '2016-02-20 23:00:00'):
|
||||||
|
Article.objects.datetimes('pub_date', 'hour').get()
|
||||||
|
with self.assertRaisesMessage(pytz.NonExistentTimeError, '2016-10-16 00:00:00'):
|
||||||
|
Comment.objects.datetimes('pub_date', 'day').get()
|
||||||
|
self.assertEqual(
|
||||||
|
Article.objects.datetimes('pub_date', 'hour', is_dst=False).get().dst(),
|
||||||
|
datetime.timedelta(0),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
Comment.objects.datetimes('pub_date', 'day', is_dst=False).get().dst(),
|
||||||
|
datetime.timedelta(0),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
Article.objects.datetimes('pub_date', 'hour', is_dst=True).get().dst(),
|
||||||
|
datetime.timedelta(0, 3600),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
Comment.objects.datetimes('pub_date', 'hour', is_dst=True).get().dst(),
|
||||||
|
datetime.timedelta(0, 3600),
|
||||||
|
)
|
||||||
|
|
||||||
def test_datetimes_returns_available_dates_for_given_scope_and_given_field(self):
|
def test_datetimes_returns_available_dates_for_given_scope_and_given_field(self):
|
||||||
pub_dates = [
|
pub_dates = [
|
||||||
datetime.datetime(2005, 7, 28, 12, 15),
|
datetime.datetime(2005, 7, 28, 12, 15),
|
||||||
|
|
Loading…
Reference in New Issue