Skip to content

Commit c769dcc

Browse files
committed
make DurationField store milliseconds instead of microseconds
MongoDB's arithmetic operators like $add treat numbers are milliseconds.
1 parent 4791968 commit c769dcc

File tree

6 files changed

+28
-1
lines changed

6 files changed

+28
-1
lines changed

.github/workflows/test-python.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ jobs:
8282
expressions.tests.ExpressionsTests.test_insensitive_patterns_escape
8383
expressions.tests.ExpressionsTests.test_patterns_escape
8484
expressions.tests.FieldTransformTests.test_transform_in_values
85+
expressions.tests.FTimeDeltaTests.test_date_minus_duration
8586
expressions.tests.NegatedExpressionTests
8687
expressions_case
8788
defer

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ Migrations for 'admin':
118118

119119
- Queries with joins aren't supported.
120120

121-
- `DateTimeField` doesn't support microsecond precision.
121+
- `DateTimeField` doesn't support microsecond precision, and correspondingly,
122+
`DurationField` stores milliseconds rather than microseconds.
122123

123124
- The following database functions aren't supported:
124125
- `Chr`

django_mongodb/expressions.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ def value(self, compiler, connection): # noqa: ARG001
7070
elif isinstance(value, datetime.date):
7171
# Turn dates into datetimes since BSON doesn't support dates.
7272
value = datetime.datetime.combine(value, datetime.datetime.min.time())
73+
elif isinstance(value, datetime.timedelta):
74+
# DurationField stores milliseconds rather than microseconds.
75+
value /= datetime.timedelta(milliseconds=1)
7376
return {"$literal": value}
7477

7578

django_mongodb/fields/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
from .auto import MongoAutoField
2+
from .duration import register_duration_field
23
from .json import register_json_field
34

45
__all__ = ["register_fields", "MongoAutoField"]
56

67

78
def register_fields():
9+
register_duration_field()
810
register_json_field()

django_mongodb/fields/duration.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from django.db.models.fields import DurationField
2+
3+
_get_db_prep_value = DurationField.get_db_prep_value
4+
5+
6+
def get_db_prep_value(self, value, connection, prepared=False):
7+
"""DurationField stores milliseconds rather than microseconds."""
8+
value = _get_db_prep_value(self, value, connection, prepared)
9+
if connection.vendor == "mongodb" and value is not None:
10+
value //= 1000
11+
return value
12+
13+
14+
def register_duration_field():
15+
DurationField.get_db_prep_value = get_db_prep_value

django_mongodb/operations.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ def convert_decimalfield_value(self, value, expression, connection):
101101
value = value.to_decimal()
102102
return value
103103

104+
def convert_durationfield_value(self, value, expression, connection):
105+
if value is not None:
106+
value = datetime.timedelta(milliseconds=value)
107+
return value
108+
104109
def convert_jsonfield_value(self, value, expression, connection):
105110
"""
106111
Convert dict data to a string so that JSONField.from_db_value() can

0 commit comments

Comments
 (0)