Skip to content

Commit f84ef86

Browse files
committed
Added DatabaseFeatures.supports_microsecond_precision.
This reverts commit a80903b.
1 parent 37e5f7b commit f84ef86

File tree

11 files changed

+234
-78
lines changed

11 files changed

+234
-78
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: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
from unittest import mock
55

66
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
7-
from django.db import DEFAULT_DB_ALIAS, DatabaseError, connections, models
7+
from django.db import DEFAULT_DB_ALIAS, DatabaseError, connection, connections, models
88
from django.db.models.manager import BaseManager
99
from django.db.models.query import MAX_GET_RESULTS, EmptyQuerySet
1010
from django.test import (
1111
SimpleTestCase,
1212
TestCase,
1313
TransactionTestCase,
14+
skipIfDBFeature,
1415
skipUnlessDBFeature,
1516
)
1617
from django.utils.translation import gettext_lazy
@@ -224,6 +225,7 @@ def test_not_equal_and_equal_operators_behave_as_expected_on_instances(self):
224225
Article.objects.get(id__exact=a1.id), Article.objects.get(id__exact=a2.id)
225226
)
226227

228+
@skipUnlessDBFeature("supports_microsecond_precision")
227229
def test_microsecond_precision(self):
228230
a9 = Article(
229231
headline="Article 9",
@@ -235,6 +237,33 @@ def test_microsecond_precision(self):
235237
datetime(2005, 7, 31, 12, 30, 45, 180),
236238
)
237239

240+
@skipIfDBFeature("supports_microsecond_precision")
241+
def test_microsecond_precision_not_supported(self):
242+
# In MySQL, microsecond-level precision isn't always available. You'll
243+
# lose microsecond-level precision once the data is saved.
244+
a9 = Article(
245+
headline="Article 9",
246+
pub_date=datetime(2005, 7, 31, 12, 30, 45, 180),
247+
)
248+
a9.save()
249+
self.assertEqual(
250+
Article.objects.get(id__exact=a9.id).pub_date,
251+
datetime(2005, 7, 31, 12, 30, 45),
252+
)
253+
254+
@skipIfDBFeature("supports_microsecond_precision")
255+
def test_microsecond_precision_not_supported_edge_case(self):
256+
# If microsecond-level precision isn't available, you'll lose
257+
# microsecond-level precision once the data is saved.
258+
a = Article.objects.create(
259+
headline="Article",
260+
pub_date=datetime(2008, 12, 31, 23, 59, 59, 999999),
261+
)
262+
self.assertEqual(
263+
Article.objects.get(pk=a.pk).pub_date,
264+
datetime(2008, 12, 31, 23, 59, 59, 999000),
265+
)
266+
238267
def test_manually_specify_primary_key(self):
239268
# You can manually specify the primary key when creating a new object.
240269
a101 = Article(
@@ -818,6 +847,13 @@ def _update(self, *args, **kwargs):
818847

819848

820849
class ModelRefreshTests(TestCase):
850+
def _truncate_ms(self, val):
851+
# Some databases don't support microseconds in datetimes which causes
852+
# problems when comparing the original value to that loaded from the DB.
853+
if connection.features.supports_microsecond_precision:
854+
return val
855+
return val - timedelta(microseconds=val.microsecond)
856+
821857
def test_refresh(self):
822858
a = Article.objects.create(pub_date=datetime.now())
823859
Article.objects.create(pub_date=datetime.now())
@@ -877,7 +913,7 @@ def test_refresh_null_fk(self):
877913
self.assertEqual(s2.selfref, s1)
878914

879915
def test_refresh_unsaved(self):
880-
pub_date = datetime.now()
916+
pub_date = self._truncate_ms(datetime.now())
881917
a = Article.objects.create(pub_date=pub_date)
882918
a2 = Article(id=a.pk)
883919
with self.assertNumQueries(1):

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)