There was some code, it looked a bit like this:
from django.utils import timezone
start_date = timezone.datetime(2015, 1, 1)
Django recommends that you use, for example, django.utils.timezone.now
to ensure you always get “the right now” (i.e. timezone-aware).
So you might, as with the code example above,
extrapolate that timezone.datetime(2015, 1, 1)
will give you a timezone-aware “1st of January 2015” datetime
object.
This is not what happens.
You get a naïve datetime
object.
Why?
Hint: django.utils.timezone
doesn’t provide datetime
as part of its API.
So how does that code work, I hear you ask?
Line 9 of django/utils/timezone.py
:
from datetime import datetime, timedelta, tzinfo
That’s it.
django.utils.timezone.datetime
is effectively just another binding for the vanilla datetime.datetime
:
>>> from django.utils import timezone
>>> timezone.datetime
<type 'datetime.datetime'>
>>> import datetime
>>> timezone.datetime == datetime.datetime
True
This is useful behaviour if you’re looking to move your code around while providing compatibility support for old imports.
This is not so useful behaviour when you suddenly find that, surprise, that thing you thought was a library feature turns out to be the stdlib in disguise.
Does this fall foul of “explicit is better than implicit”? It certainly gets me on the Principle of Least Astonishment; I understand why it happens, but it’s still surprising from a user’s perspective!
Incidentally, we found this when
we added warnings.simplefilter("error")
to our tests so that any generated warnings would cause test failure.
You probably want to do that too.