[Django] #37108: DjangoJSONEncoder encodes time inconsistently depending on microseconds

5 views
Skip to first unread message

Django

unread,
May 19, 2026, 9:46:27 AM (2 days ago) May 19
to django-...@googlegroups.com
#37108: DjangoJSONEncoder encodes time inconsistently depending on microseconds
-------------------------------------+-------------------------------------
Reporter: Roman Donchenko | Type:
| Uncategorized
Status: new | Component: Core
| (Serialization)
Version: 6.0 | Severity: Normal
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Consider this script:

{{{
import json
from datetime import datetime, time, UTC
from django.core.serializers.json import DjangoJSONEncoder

print(json.dumps(datetime.fromtimestamp(0, UTC), cls=DjangoJSONEncoder))
print(json.dumps(datetime.fromtimestamp(0.000001, UTC),
cls=DjangoJSONEncoder))

print(json.dumps(time(), cls=DjangoJSONEncoder))
print(json.dumps(time(microsecond=1), cls=DjangoJSONEncoder))
}}}

It outputs the following:

{{{
"1970-01-01T00:00:00Z"
"1970-01-01T00:00:00.000Z"
"00:00:00"
"00:00:00.000"
}}}

In other words, even though the encoded value is logically the same,
`DjangoJSONEncoder` either adds ".000" or doesn't depending on the number
of microseconds.

This is not a big problem, but it is mildly annoying. Imagine you save a
fixture with `dumpdata`, load it back, then save again. The first time,
the encoder adds ".000", then after loading the number of microseconds is
set to 0, so after the second save, the encoder ''doesn't'' add ".000".
Now there's an unnecessary change in the saved fixture.

Seems like the encoder should either always add ".000" or always omit it.
--
Ticket URL: <https://code.djangoproject.com/ticket/37108>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
May 19, 2026, 4:05:17 PM (2 days ago) May 19
to django-...@googlegroups.com
#37108: DjangoJSONEncoder encodes time inconsistently depending on microseconds
-------------------------------------+-------------------------------------
Reporter: Roman Donchenko | Owner: (none)
Type: Uncategorized | Status: new
Component: Core | Version: 6.0
(Serialization) |
Severity: Normal | Resolution:
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Richard Autry):

To add some more information after looking into this, this behavior is due
to the truncation of microseconds which was added as part of the timezone
effort back in 2011:

https://github.com/django/django/commit/9b1cb755a28f020e27d4268c214b25315d4de42e
#diff-45e847dda304240b8c519e6b57aa635ae806341986b14e0f17367450f1cbe0e9R47

i.e.

{{{
>>> now.isoformat()[:23] + now.isoformat()[26:]
'2026-05-19T15:50:51.060'
}}}

When you specify a timestamp of 0.000001, it is be stepped down to the
logical ".000" you mentioned. However, whether or not microseconds is
included is a function of the standard `datetime` library and not the
actual `DjangoJSONEncoder`.

i.e.

{{{
>>> time().isoformat()
'00:00:00'
>>> time(microsecond=1).isoformat()
'00:00:00.000001'
}}}

I'm not sure `DjangoJSONEncoder` should be responsible for providing
default behavior that might override this.
--
Ticket URL: <https://code.djangoproject.com/ticket/37108#comment:1>

Django

unread,
May 19, 2026, 4:27:14 PM (2 days ago) May 19
to django-...@googlegroups.com
#37108: DjangoJSONEncoder encodes time inconsistently depending on microseconds
-------------------------------------+-------------------------------------
Reporter: Roman Donchenko | Owner: (none)
Type: Uncategorized | Status: new
Component: Core | Version: 6.0
(Serialization) |
Severity: Normal | Resolution:
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Roman Donchenko):

FWIW, nowadays `isoformat` supports a `timespec` parameter that can
control how the microseconds are represented - including an option to
truncate to millisecond precision.
--
Ticket URL: <https://code.djangoproject.com/ticket/37108#comment:2>

Django

unread,
7:05 AM (3 hours ago) 7:05 AM
to django-...@googlegroups.com
#37108: DjangoJSONEncoder encodes time inconsistently depending on microseconds
--------------------------------------+------------------------------------
Reporter: Roman Donchenko | Owner: (none)
Type: Cleanup/optimization | Status: new
Component: Core (Serialization) | Version: 6.0
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
--------------------------------------+------------------------------------
Changes (by Sarah Boyce):

* stage: Unreviewed => Accepted
* type: Uncategorized => Cleanup/optimization

Comment:

I think we should always omit `.000`, something like:
{{{#!diff
diff --git a/django/core/serializers/json.py
b/django/core/serializers/json.py
index b955939e0d..1cf24a1211 100644
--- a/django/core/serializers/json.py
+++ b/django/core/serializers/json.py
@@ -90,9 +90,8 @@ class DjangoJSONEncoder(json.JSONEncoder):
def default(self, o):
# See "Date Time String Format" in the ECMA-262 specification.
if isinstance(o, datetime.datetime):
- r = o.isoformat()
- if o.microsecond:
- r = r[:23] + r[26:]
+ r = o.isoformat(sep="T", timespec="milliseconds")
+ r = r.replace(".000", "")
if r.endswith("+00:00"):
r = r.removesuffix("+00:00") + "Z"
}}}

This is a slight breaking change and so I would recommend a release note
--
Ticket URL: <https://code.djangoproject.com/ticket/37108#comment:3>
Reply all
Reply to author
Forward
0 new messages