Skip to content

Commit cd2bd34

Browse files
committed
Added DatabaseFeatures.supports_microsecond_precision.
This reverts commit a80903b.
1 parent c3064c6 commit cd2bd34

File tree

11 files changed

+241
-82
lines changed

11 files changed

+241
-82
lines changed

django/db/backends/base/features.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ class BaseDatabaseFeatures:
8787
# by returning the type used to store duration field?
8888
supports_temporal_subtraction = False
8989

90+
# Do time/datetime fields have microsecond precision?
91+
supports_microsecond_precision = True
92+
9093
# Does the __regex lookup support backreferencing and grouping?
9194
supports_regex_backreferencing = True
9295

tests/basic/tests.py

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
SimpleTestCase,
1919
TestCase,
2020
TransactionTestCase,
21+
skipIfDBFeature,
2122
skipUnlessDBFeature,
2223
)
2324
from django.test.utils import CaptureQueriesContext, ignore_warnings
@@ -378,6 +379,7 @@ def test_not_equal_and_equal_operators_behave_as_expected_on_instances(self):
378379
Article.objects.get(id__exact=a1.id), Article.objects.get(id__exact=a2.id)
379380
)
380381

382+
@skipUnlessDBFeature("supports_microsecond_precision")
381383
def test_microsecond_precision(self):
382384
a9 = Article(
383385
headline="Article 9",
@@ -389,6 +391,33 @@ def test_microsecond_precision(self):
389391
datetime(2005, 7, 31, 12, 30, 45, 180),
390392
)
391393

394+
@skipIfDBFeature("supports_microsecond_precision")
395+
def test_microsecond_precision_not_supported(self):
396+
# In MySQL, microsecond-level precision isn't always available. You'll
397+
# lose microsecond-level precision once the data is saved.
398+
a9 = Article(
399+
headline="Article 9",
400+
pub_date=datetime(2005, 7, 31, 12, 30, 45, 180),
401+
)
402+
a9.save()
403+
self.assertEqual(
404+
Article.objects.get(id__exact=a9.id).pub_date,
405+
datetime(2005, 7, 31, 12, 30, 45),
406+
)
407+
408+
@skipIfDBFeature("supports_microsecond_precision")
409+
def test_microsecond_precision_not_supported_edge_case(self):
410+
# If microsecond-level precision isn't available, you'll lose
411+
# microsecond-level precision once the data is saved.
412+
a = Article.objects.create(
413+
headline="Article",
414+
pub_date=datetime(2008, 12, 31, 23, 59, 59, 999999),
415+
)
416+
self.assertEqual(
417+
Article.objects.get(pk=a.pk).pub_date,
418+
datetime(2008, 12, 31, 23, 59, 59, 999000),
419+
)
420+
392421
def test_manually_specify_primary_key(self):
393422
# You can manually specify the primary key when creating a new object.
394423
a101 = Article(
@@ -972,6 +1001,13 @@ def _update(self, *args, **kwargs):
9721001

9731002

9741003
class ModelRefreshTests(TestCase):
1004+
def _truncate_ms(self, val):
1005+
# Some databases don't support microseconds in datetimes which causes
1006+
# problems when comparing the original value to that loaded from the DB.
1007+
if connection.features.supports_microsecond_precision:
1008+
return val
1009+
return val - timedelta(microseconds=val.microsecond)
1010+
9751011
def test_refresh(self):
9761012
a = Article.objects.create(pub_date=datetime.now())
9771013
Article.objects.create(pub_date=datetime.now())
@@ -1031,7 +1067,7 @@ def test_refresh_null_fk(self):
10311067
self.assertEqual(s2.selfref, s1)
10321068

10331069
def test_refresh_unsaved(self):
1034-
pub_date = datetime.now()
1070+
pub_date = self._truncate_ms(datetime.now())
10351071
a = Article.objects.create(pub_date=pub_date)
10361072
a2 = Article(id=a.pk)
10371073
with self.assertNumQueries(1):
@@ -1131,7 +1167,10 @@ def test_refresh_for_update(self):
11311167
)
11321168

11331169
def test_refresh_with_related(self):
1134-
a = Article.objects.create(pub_date=datetime.now())
1170+
pub_date = datetime.now()
1171+
if not connection.features.supports_microsecond_precision:
1172+
pub_date = pub_date.replace(microsecond=0)
1173+
a = Article.objects.create(pub_date=pub_date)
11351174
fa = FeaturedArticle.objects.create(article=a)
11361175

11371176
from_queryset = FeaturedArticle.objects.select_related("article")

tests/db_functions/comparison/test_greatest.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,17 @@
1111
from ..models import Article, Author, DecimalModel, Fan
1212

1313

14+
def microsecond_support(value):
15+
return (
16+
value
17+
if connection.features.supports_microsecond_precision
18+
else value.replace(microsecond=0)
19+
)
20+
21+
1422
class GreatestTests(TestCase):
1523
def test_basic(self):
16-
now = timezone.now()
24+
now = microsecond_support(timezone.now())
1725
before = now - timedelta(hours=1)
1826
Article.objects.create(
1927
title="Testing with Django", written=before, published=now
@@ -25,7 +33,7 @@ def test_basic(self):
2533

2634
@skipUnlessDBFeature("greatest_least_ignores_nulls")
2735
def test_ignores_null(self):
28-
now = timezone.now()
36+
now = microsecond_support(timezone.now())
2937
Article.objects.create(title="Testing with Django", written=now)
3038
articles = Article.objects.annotate(
3139
last_updated=Greatest("written", "published")
@@ -42,7 +50,7 @@ def test_propagates_null(self):
4250

4351
def test_coalesce_workaround(self):
4452
past = datetime(1900, 1, 1)
45-
now = timezone.now()
53+
now = microsecond_support(timezone.now())
4654
Article.objects.create(title="Testing with Django", written=now)
4755
articles = Article.objects.annotate(
4856
last_updated=Greatest(

tests/db_functions/comparison/test_least.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,17 @@
1111
from ..models import Article, Author, DecimalModel, Fan
1212

1313

14+
def microsecond_support(value):
15+
return (
16+
value
17+
if connection.features.supports_microsecond_precision
18+
else value.replace(microsecond=0)
19+
)
20+
21+
1422
class LeastTests(TestCase):
1523
def test_basic(self):
16-
now = timezone.now()
24+
now = microsecond_support(timezone.now())
1725
before = now - timedelta(hours=1)
1826
Article.objects.create(
1927
title="Testing with Django", written=before, published=now
@@ -23,7 +31,7 @@ def test_basic(self):
2331

2432
@skipUnlessDBFeature("greatest_least_ignores_nulls")
2533
def test_ignores_null(self):
26-
now = timezone.now()
34+
now = microsecond_support(timezone.now())
2735
Article.objects.create(title="Testing with Django", written=now)
2836
articles = Article.objects.annotate(
2937
first_updated=Least("written", "published"),
@@ -38,7 +46,7 @@ def test_propagates_null(self):
3846

3947
def test_coalesce_workaround(self):
4048
future = datetime(2100, 1, 1)
41-
now = timezone.now()
49+
now = microsecond_support(timezone.now())
4250
Article.objects.create(title="Testing with Django", written=now)
4351
articles = Article.objects.annotate(
4452
last_updated=Least(

0 commit comments

Comments
 (0)