Skip to content

Commit 87fa60c

Browse files
committed
Fix Trunc database function with tzinfo parameter
1 parent 6437d3f commit 87fa60c

File tree

5 files changed

+45
-11
lines changed

5 files changed

+45
-11
lines changed

django_mongodb_backend/features.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
5858
"model_fields.test_jsonfield.TestQuerying.test_icontains",
5959
# MongoDB gives ROUND(365, -1)=360 instead of 370 like other databases.
6060
"db_functions.math.test_round.RoundTests.test_integer_with_negative_precision",
61-
# Truncating in another timezone doesn't work becauase MongoDB converts
62-
# the result back to UTC.
63-
"db_functions.datetime.test_extract_trunc.DateFunctionWithTimeZoneTests.test_trunc_func_with_timezone",
64-
"db_functions.datetime.test_extract_trunc.DateFunctionWithTimeZoneTests.test_trunc_timezone_applied_before_truncation",
6561
# Unexpected alias_refcount in alias_map.
6662
"queries.tests.Queries1Tests.test_order_by_tables",
6763
# The $sum aggregation returns 0 instead of None for null.

django_mongodb_backend/functions.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
from datetime import datetime
2+
3+
from django.conf import settings
14
from django.db import NotSupportedError
5+
from django.db.models import DateField, DateTimeField, TimeField
26
from django.db.models.expressions import Func
37
from django.db.models.functions.comparison import Cast, Coalesce, Greatest, Least, NullIf
48
from django.db.models.functions.datetime import (
@@ -195,6 +199,33 @@ def trunc(self, compiler, connection):
195199
return {"$dateTrunc": lhs_mql}
196200

197201

202+
def trunc_convert_value(self, value, expression, connection):
203+
if connection.vendor == "mongodb":
204+
# A custom TruncBase.convert_value() for MongoDB.
205+
if value is None:
206+
return None
207+
convert_to_tz = settings.USE_TZ and self.get_tzname() != "UTC"
208+
if isinstance(self.output_field, DateTimeField):
209+
if convert_to_tz:
210+
# Unlike other databases, MongoDB returns the value in UTC,
211+
# so rather than setting the time zone equal to self.tzinfo,
212+
# the value must be converted to tzinfo.
213+
value = value.astimezone(self.tzinfo)
214+
elif isinstance(value, datetime):
215+
if isinstance(self.output_field, DateField):
216+
if convert_to_tz:
217+
value = value.astimezone(self.tzinfo)
218+
# Truncate for Trunc(..., output_field=DateField)
219+
value = value.date()
220+
elif isinstance(self.output_field, TimeField):
221+
if convert_to_tz:
222+
value = value.astimezone(self.tzinfo)
223+
# Truncate for Trunc(..., output_field=TimeField)
224+
value = value.time()
225+
return value
226+
return self.convert_value(value, expression, connection)
227+
228+
198229
def trunc_date(self, compiler, connection):
199230
# Cast to date rather than truncate to date.
200231
lhs_mql = process_lhs(self, compiler, connection)
@@ -254,6 +285,7 @@ def register_functions():
254285
Substr.as_mql = substr
255286
Trim.as_mql = trim("trim")
256287
TruncBase.as_mql = trunc
288+
TruncBase.convert_value = trunc_convert_value
257289
TruncDate.as_mql = trunc_date
258290
TruncTime.as_mql = trunc_time
259291
Upper.as_mql = preserve_null("toUpper")

django_mongodb_backend/operations.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from django.db.backends.base.operations import BaseDatabaseOperations
1111
from django.db.models import TextField
1212
from django.db.models.expressions import Combinable, Expression
13-
from django.db.models.functions import Cast
13+
from django.db.models.functions import Cast, Trunc
1414
from django.utils import timezone
1515
from django.utils.regex_helper import _lazy_re_compile
1616

@@ -97,7 +97,11 @@ def get_db_converters(self, expression):
9797
]
9898
)
9999
elif internal_type == "DateField":
100-
converters.append(self.convert_datefield_value)
100+
# Trunc(... output_field="DateField") values must remain datetime
101+
# until Trunc.convert_value() so they can be converted from UTC
102+
# before truncation.
103+
if not isinstance(expression, Trunc):
104+
converters.append(self.convert_datefield_value)
101105
elif internal_type == "DateTimeField":
102106
if settings.USE_TZ:
103107
converters.append(self.convert_datetimefield_value)
@@ -106,7 +110,11 @@ def get_db_converters(self, expression):
106110
elif internal_type == "JSONField":
107111
converters.append(self.convert_jsonfield_value)
108112
elif internal_type == "TimeField":
109-
converters.append(self.convert_timefield_value)
113+
# Trunc(... output_field="TimeField") values must remain datetime
114+
# until Trunc.convert_value() so they can be converted from UTC
115+
# before truncation.
116+
if not isinstance(expression, Trunc):
117+
converters.append(self.convert_timefield_value)
110118
elif internal_type == "UUIDField":
111119
converters.append(self.convert_uuidfield_value)
112120
return converters

docs/source/releases/5.1.x.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Django MongoDB Backend 5.1.x
1010
- Added support for a field's custom lookups and transforms in
1111
``EmbeddedModelField``, e.g. ``ArrayField``’s ``contains``,
1212
``contained__by``, ``len``, etc.
13+
- Fixed the results of queries that use the ``tzinfo`` parameter of the
14+
``Trunc`` database functions.
1315

1416
.. _django-mongodb-backend-5.1.0-beta-2:
1517

docs/source/topics/known-issues.rst

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,6 @@ Database functions
7474
:class:`~django.db.models.functions.SHA512`
7575
- :class:`~django.db.models.functions.Sign`
7676

77-
- The ``tzinfo`` parameter of the :class:`~django.db.models.functions.Trunc`
78-
database functions doesn't work properly because MongoDB converts the result
79-
back to UTC.
80-
8177
Transaction management
8278
======================
8379

0 commit comments

Comments
 (0)