summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHasan Ramezani <hasan.r67@gmail.com>2020-02-18 18:12:51 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2020-03-20 12:55:33 +0100
commit53b6a466d89f972aa435fe7b5135e9b501ce68ee (patch)
tree79764700fb91a071c231e7351f4919303b927998
parent2695ac8e0441b4d7e5460eac3bb7ea315164a6bf (diff)
Refs #29724 -- Added is_dst parameter to QuerySet.datetimes().
Thanks Simon Charette for the review and Mariusz Felisiak for tests.
-rw-r--r--django/db/models/query.py10
-rw-r--r--docs/ref/models/querysets.txt10
-rw-r--r--docs/releases/3.1.txt3
-rw-r--r--tests/datetimes/tests.py36
4 files changed, 56 insertions, 3 deletions
diff --git a/django/db/models/query.py b/django/db/models/query.py
index a7c16c4bd8..5fdee3ff37 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -875,7 +875,7 @@ class QuerySet:
'datefield', flat=True
).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
datetimes for the given field_name, scoped to 'kind'.
@@ -890,7 +890,13 @@ class QuerySet:
else:
tzinfo = None
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)
).values_list(
'datetimefield', flat=True
diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt
index 6b042d305e..9647b1a8ff 100644
--- a/docs/ref/models/querysets.txt
+++ b/docs/ref/models/querysets.txt
@@ -759,7 +759,7 @@ Examples::
``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`
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
``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:
.. note::
diff --git a/docs/releases/3.1.txt b/docs/releases/3.1.txt
index 77a71bbc43..44ba489a44 100644
--- a/docs/releases/3.1.txt
+++ b/docs/releases/3.1.txt
@@ -324,6 +324,9 @@ Models
:meth:`~.RelatedManager.set` methods now accept callables as values in the
``through_defaults`` argument.
+* The new ``is_dst`` parameter of the :meth:`.QuerySet.datetimes` determines
+ the treatment of nonexistent and ambiguous datetimes.
+
Pagination
~~~~~~~~~~
diff --git a/tests/datetimes/tests.py b/tests/datetimes/tests.py
index a9f1199f06..857293acb6 100644
--- a/tests/datetimes/tests.py
+++ b/tests/datetimes/tests.py
@@ -1,5 +1,7 @@
import datetime
+import pytz
+
from django.test import TestCase, override_settings
from django.utils import timezone
@@ -89,6 +91,40 @@ class DateTimesTests(TestCase):
qs = Article.objects.datetimes('pub_date', 'second')
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):
pub_dates = [
datetime.datetime(2005, 7, 28, 12, 15),