On , I learnt ...

Django’s JSON encoder rounds datetimes down to the nearest millisecond

Django provides a custom DjangoJSONEncoder class that can encode datetime.date, datetime.datetime, decimal.Decimal and uuid.UUID types.

Beware though: this class serializes datetime.datetime instances to millisecond precision only. Since Python’s datetime.datetime type supports microsecond precision, the serialisation process can change the datetime’s value.

Watch:

>>> import datetime
>>> import json
>>> from django.core.serializers.json import DjangoJSONEncoder
>>>
>>> # Create a datetime with sub-millisecond precision.
>>> dt = datetime.datetime(2022, 12, 1, 14, 0, 0, 1234)
>>> dt.isoformat()
"2022-12-01T14:00:00.001234"
>>>
>>> # Serialize the datetime using Django's encoder.
>>> print(json.dumps(dt, cls=DjangoJSONEncoder))
"2022-12-01T14:00:00.001"

As the above snippet illustrates, serialising a datetime.datetime effectively rounds it down to the nearest millisecond.

Why?

This rounding is deliberate.

django JSON encoder

It’s done to ensure serialized datetime strings conform with the ECMAScript 262 language specification which mandates millisecond precision. Ultimately, this is because the JavaScript Date object only supports millisecond precision.

Broken tests

This is a gotcha that can effect tests that compare datetime values that get serialized and deserialized (which is how I stumbled upon it).

If you need to perform the same rounding in Python code, use something like:

rounded_dt = dt.replace(microsecond=dt.microsecond // 1000 * 1000)