mirror of
https://github.com/django/django.git
synced 2025-10-24 06:06:09 +00:00
Fixed #2443 -- Added DurationField.
A field for storing periods of time - modeled in Python by timedelta. It is stored in the native interval data type on PostgreSQL and as a bigint of microseconds on other backends. Also includes significant changes to the internals of time related maths in expressions, including the removal of DateModifierNode. Thanks to Tim and Josh in particular for reviews.
This commit is contained in:
@@ -4,7 +4,7 @@ from copy import deepcopy
|
||||
import datetime
|
||||
|
||||
from django.core.exceptions import FieldError
|
||||
from django.db import connection, transaction
|
||||
from django.db import connection, transaction, DatabaseError
|
||||
from django.db.models import F
|
||||
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
|
||||
from django.test.utils import Approximate
|
||||
@@ -602,7 +602,7 @@ class FTimeDeltaTests(TestCase):
|
||||
# e0: started same day as assigned, zero duration
|
||||
end = stime + delta0
|
||||
e0 = Experiment.objects.create(name='e0', assigned=sday, start=stime,
|
||||
end=end, completed=end.date())
|
||||
end=end, completed=end.date(), estimated_time=delta0)
|
||||
self.deltas.append(delta0)
|
||||
self.delays.append(e0.start -
|
||||
datetime.datetime.combine(e0.assigned, midnight))
|
||||
@@ -617,7 +617,7 @@ class FTimeDeltaTests(TestCase):
|
||||
delay = datetime.timedelta(1)
|
||||
end = stime + delay + delta1
|
||||
e1 = Experiment.objects.create(name='e1', assigned=sday,
|
||||
start=stime + delay, end=end, completed=end.date())
|
||||
start=stime + delay, end=end, completed=end.date(), estimated_time=delta1)
|
||||
self.deltas.append(delta1)
|
||||
self.delays.append(e1.start -
|
||||
datetime.datetime.combine(e1.assigned, midnight))
|
||||
@@ -627,7 +627,7 @@ class FTimeDeltaTests(TestCase):
|
||||
end = stime + delta2
|
||||
e2 = Experiment.objects.create(name='e2',
|
||||
assigned=sday - datetime.timedelta(3), start=stime, end=end,
|
||||
completed=end.date())
|
||||
completed=end.date(), estimated_time=datetime.timedelta(hours=1))
|
||||
self.deltas.append(delta2)
|
||||
self.delays.append(e2.start -
|
||||
datetime.datetime.combine(e2.assigned, midnight))
|
||||
@@ -637,7 +637,7 @@ class FTimeDeltaTests(TestCase):
|
||||
delay = datetime.timedelta(4)
|
||||
end = stime + delay + delta3
|
||||
e3 = Experiment.objects.create(name='e3',
|
||||
assigned=sday, start=stime + delay, end=end, completed=end.date())
|
||||
assigned=sday, start=stime + delay, end=end, completed=end.date(), estimated_time=delta3)
|
||||
self.deltas.append(delta3)
|
||||
self.delays.append(e3.start -
|
||||
datetime.datetime.combine(e3.assigned, midnight))
|
||||
@@ -647,7 +647,7 @@ class FTimeDeltaTests(TestCase):
|
||||
end = stime + delta4
|
||||
e4 = Experiment.objects.create(name='e4',
|
||||
assigned=sday - datetime.timedelta(10), start=stime, end=end,
|
||||
completed=end.date())
|
||||
completed=end.date(), estimated_time=delta4 - datetime.timedelta(1))
|
||||
self.deltas.append(delta4)
|
||||
self.delays.append(e4.start -
|
||||
datetime.datetime.combine(e4.assigned, midnight))
|
||||
@@ -675,6 +675,10 @@ class FTimeDeltaTests(TestCase):
|
||||
Experiment.objects.filter(end__lt=F('start') + delta)]
|
||||
self.assertEqual(test_set, self.expnames[:i])
|
||||
|
||||
test_set = [e.name for e in
|
||||
Experiment.objects.filter(end__lt=delta + F('start'))]
|
||||
self.assertEqual(test_set, self.expnames[:i])
|
||||
|
||||
test_set = [e.name for e in
|
||||
Experiment.objects.filter(end__lte=F('start') + delta)]
|
||||
self.assertEqual(test_set, self.expnames[:i + 1])
|
||||
@@ -756,42 +760,29 @@ class FTimeDeltaTests(TestCase):
|
||||
self.assertEqual(expected_ends, new_ends)
|
||||
self.assertEqual(expected_durations, new_durations)
|
||||
|
||||
def test_delta_invalid_op_mult(self):
|
||||
raised = False
|
||||
try:
|
||||
repr(Experiment.objects.filter(end__lt=F('start') * self.deltas[0]))
|
||||
except TypeError:
|
||||
raised = True
|
||||
self.assertTrue(raised, "TypeError not raised on attempt to multiply datetime by timedelta.")
|
||||
def test_invalid_operator(self):
|
||||
with self.assertRaises(DatabaseError):
|
||||
list(Experiment.objects.filter(start=F('start') * datetime.timedelta(0)))
|
||||
|
||||
def test_delta_invalid_op_div(self):
|
||||
raised = False
|
||||
try:
|
||||
repr(Experiment.objects.filter(end__lt=F('start') / self.deltas[0]))
|
||||
except TypeError:
|
||||
raised = True
|
||||
self.assertTrue(raised, "TypeError not raised on attempt to divide datetime by timedelta.")
|
||||
def test_durationfield_add(self):
|
||||
zeros = [e.name for e in
|
||||
Experiment.objects.filter(start=F('start') + F('estimated_time'))]
|
||||
self.assertEqual(zeros, ['e0'])
|
||||
|
||||
def test_delta_invalid_op_mod(self):
|
||||
raised = False
|
||||
try:
|
||||
repr(Experiment.objects.filter(end__lt=F('start') % self.deltas[0]))
|
||||
except TypeError:
|
||||
raised = True
|
||||
self.assertTrue(raised, "TypeError not raised on attempt to modulo divide datetime by timedelta.")
|
||||
end_less = [e.name for e in
|
||||
Experiment.objects.filter(end__lt=F('start') + F('estimated_time'))]
|
||||
self.assertEqual(end_less, ['e2'])
|
||||
|
||||
def test_delta_invalid_op_and(self):
|
||||
raised = False
|
||||
try:
|
||||
repr(Experiment.objects.filter(end__lt=F('start').bitand(self.deltas[0])))
|
||||
except TypeError:
|
||||
raised = True
|
||||
self.assertTrue(raised, "TypeError not raised on attempt to binary and a datetime with a timedelta.")
|
||||
delta_math = [e.name for e in
|
||||
Experiment.objects.filter(end__gte=F('start') + F('estimated_time') + datetime.timedelta(hours=1))]
|
||||
self.assertEqual(delta_math, ['e4'])
|
||||
|
||||
def test_delta_invalid_op_or(self):
|
||||
raised = False
|
||||
try:
|
||||
repr(Experiment.objects.filter(end__lt=F('start').bitor(self.deltas[0])))
|
||||
except TypeError:
|
||||
raised = True
|
||||
self.assertTrue(raised, "TypeError not raised on attempt to binary or a datetime with a timedelta.")
|
||||
@skipUnlessDBFeature("has_native_duration_field")
|
||||
def test_date_subtraction(self):
|
||||
under_estimate = [e.name for e in
|
||||
Experiment.objects.filter(estimated_time__gt=F('end') - F('start'))]
|
||||
self.assertEqual(under_estimate, ['e2'])
|
||||
|
||||
over_estimate = [e.name for e in
|
||||
Experiment.objects.filter(estimated_time__lt=F('end') - F('start'))]
|
||||
self.assertEqual(over_estimate, ['e4'])
|
||||
|
||||
Reference in New Issue
Block a user