Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django make_aware usage

Tags:

python

django

Django docs says:

The pytz.NonExistentTimeError exception is raised if you try to make value aware during a DST transition such that the time never occurred (when entering into DST). Setting is_dst to True or False 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 and is_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.

like image 221
André Roggeri Campos Avatar asked Feb 22 '17 18:02

André Roggeri Campos


1 Answers

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 to True or False 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 and is_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!

like image 147
Kevin Christopher Henry Avatar answered Nov 05 '22 13:11

Kevin Christopher Henry