summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVytis Banaitis <vytis.banaitis@gmail.com>2017-02-17 20:34:22 +0200
committerTim Graham <timograham@gmail.com>2017-02-21 12:06:16 -0500
commit4045fd56cb0f83d0f78de9aca073c7104e4cf8fd (patch)
treeb2e72f157a082b91a728dc48297f93d00cb2e2e3
parent5a6f70b4281817656db2f36c5919036d38fcce7f (diff)
Fixed #27856 -- Improved accuracy of date subtraction on PostgreSQL.
Accuracy was incorrect when dates differ by a month or more.
-rw-r--r--django/db/backends/postgresql/operations.py2
-rw-r--r--docs/releases/1.10.6.txt3
-rw-r--r--tests/expressions/tests.py22
3 files changed, 23 insertions, 4 deletions
diff --git a/django/db/backends/postgresql/operations.py b/django/db/backends/postgresql/operations.py
index 8891612074..463fdc3eda 100644
--- a/django/db/backends/postgresql/operations.py
+++ b/django/db/backends/postgresql/operations.py
@@ -253,7 +253,7 @@ class DatabaseOperations(BaseDatabaseOperations):
if internal_type == 'DateField':
lhs_sql, lhs_params = lhs
rhs_sql, rhs_params = rhs
- return "age(%s, %s)" % (lhs_sql, rhs_sql), lhs_params + rhs_params
+ return "(interval '1 day' * (%s - %s))" % (lhs_sql, rhs_sql), lhs_params + rhs_params
return super().subtract_temporals(internal_type, lhs, rhs)
def fulltext_search_sql(self, field_name):
diff --git a/docs/releases/1.10.6.txt b/docs/releases/1.10.6.txt
index b549ae1b9a..41a308bba8 100644
--- a/docs/releases/1.10.6.txt
+++ b/docs/releases/1.10.6.txt
@@ -17,3 +17,6 @@ Bugfixes
* Fixed a crash on Oracle and PostgreSQL when subtracting ``DurationField``
or ``IntegerField`` from ``DateField`` (:ticket:`27828`).
+
+* Fixed query expression date subtraction accuracy on PostgreSQL for
+ differences large an a month (:ticket:`27856`).
diff --git a/tests/expressions/tests.py b/tests/expressions/tests.py
index 5b754c88f1..fc05b0a280 100644
--- a/tests/expressions/tests.py
+++ b/tests/expressions/tests.py
@@ -952,6 +952,7 @@ class FTimeDeltaTests(TestCase):
delta2 = datetime.timedelta(seconds=44)
delta3 = datetime.timedelta(hours=21, minutes=8)
delta4 = datetime.timedelta(days=10)
+ delta5 = datetime.timedelta(days=90)
# Test data is set so that deltas and delays will be
# strictly increasing.
@@ -1015,6 +1016,18 @@ class FTimeDeltaTests(TestCase):
cls.deltas.append(delta4)
cls.delays.append(e4.start - datetime.datetime.combine(e4.assigned, midnight))
cls.days_long.append(e4.completed - e4.assigned)
+
+ # e5: started a month after assignment, very long duration
+ delay = datetime.timedelta(30)
+ end = stime + delay + delta5
+ e5 = Experiment.objects.create(
+ name='e5', assigned=sday, start=stime + delay, end=end,
+ completed=end.date(), estimated_time=delta5,
+ )
+ cls.deltas.append(delta5)
+ cls.delays.append(e5.start - datetime.datetime.combine(e5.assigned, midnight))
+ cls.days_long.append(e5.completed - e5.assigned)
+
cls.expnames = [e.name for e in Experiment.objects.all()]
def test_multiple_query_compilation(self):
@@ -1138,7 +1151,10 @@ class FTimeDeltaTests(TestCase):
)
at_least_5_days = {e.name for e in queryset.filter(completion_duration__gte=datetime.timedelta(days=5))}
- self.assertEqual(at_least_5_days, {'e3', 'e4'})
+ self.assertEqual(at_least_5_days, {'e3', 'e4', 'e5'})
+
+ at_least_120_days = {e.name for e in queryset.filter(completion_duration__gte=datetime.timedelta(days=120))}
+ self.assertEqual(at_least_120_days, {'e5'})
less_than_5_days = {e.name for e in queryset.filter(completion_duration__lt=datetime.timedelta(days=5))}
expected = {'e0', 'e2'}
@@ -1182,13 +1198,13 @@ class FTimeDeltaTests(TestCase):
over_estimate = Experiment.objects.exclude(name='e1').filter(
completed__gt=self.stime + F('estimated_time'),
).order_by('name')
- self.assertQuerysetEqual(over_estimate, ['e3', 'e4'], lambda e: e.name)
+ self.assertQuerysetEqual(over_estimate, ['e3', 'e4', 'e5'], lambda e: e.name)
def test_date_minus_duration(self):
more_than_4_days = Experiment.objects.filter(
assigned__lt=F('completed') - Value(datetime.timedelta(days=4), output_field=models.DurationField())
)
- self.assertQuerysetEqual(more_than_4_days, ['e3', 'e4'], lambda e: e.name)
+ self.assertQuerysetEqual(more_than_4_days, ['e3', 'e4', 'e5'], lambda e: e.name)
def test_negative_timedelta_update(self):
# subtract 30 seconds, 30 minutes, 2 hours and 2 days