Django docs says:
The
pytz.NonExistentTimeError
exception is raised if you try to makevalue
aware during a DST transition such that the time never occurred (when entering into DST). Settingis_dst
toTrue
orFalse
will avoid the exception by moving the hour backwards or forwards by 1 respectively. For example,is_dst=True
would change a non-existent time of 2:30 to 1:30 andis_dst=False
would change the time to 3:30.
So I expect the time to be shifted one forward or backward depending on the value of is_dst
.
But from my tests, the timezone is being shifted, not the time. Am I misunderstanding the Django docs or this is a bug ?
I tested the following :
import pytz
import datetime
from django.utils.timezone import make_aware
tz = pytz.timezone('America/Sao_Paulo')
dt = datetime.datetime(2017, 10, 15 ,0 ,0)
print(make_aware(dt, tz)) # NonExistentTimeError
print(make_aware(dt, tz, True)) # 2017-10-15 00:00:00-02:00
print(make_aware(dt, tz, False)) # 2017-10-15 00:00:00-03:00
2017-10-15 is when DST will begin in that America/Sao_Paulo
timezone, so when the clock reaches 00:00 it should jump to 01:00. The datetimes returned by the make_aware
method are inexistent.
From the source code we can see that Django is just wrapping pytz. So if there's a problem here it's either a pytz bug or a Django documentation bug.
Now, since you're passing localize()
a non-existent time, it's going to have to change either the wall-clock time or the tzinfo
to return a valid time. Although the pytz documentation isn't very precise on this point, it appears that its contract is to always return a datetime
with the same wall-clock time as what was passed in. That makes sense, especially when you consider the parallel case of an ambiguous time.
So I think the Django documentation is in error when it says:
Setting
is_dst
toTrue
orFalse
will avoid the exception by moving the hour backwards or forwards by 1 respectively. For example,is_dst=True
would change a non-existent time of 2:30 to 1:30 andis_dst=False
would change the time to 3:30.
Note, though, that the actual moment in time is the same as what the documentation describes:
tz = pytz.timezone('America/Sao_Paulo')
dt = datetime.datetime(2017, 10, 15, 0)
dt_plus_one = datetime.datetime(2017, 10, 15, 1)
dt_minus_one = datetime.datetime(2017, 10, 14, 23)
make_aware(dt, tz, True) == make_aware(dt_minus_one, tz) # True
make_aware(dt, tz, False) == make_aware(dt_plus_one, tz) # True
Please consider filing a ticket about this!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With