Django’s settings configuration has a USE_TZ item with a default value of True. Many people are not quite sure what this configuration item does, except that it has to do with time zones. Let’s talk about it in detail.

First, we need to understand what offset-aware and offset-navie are.

offset-aware and offset-navie

In Python, there is a datetime module that I’m sure you’re familiar with. But few people know that this module’s time can also be divided into the following two types.

  • offset-naive: the type without time zone
  • offset-aware: the type with time zone

And, these two types of time cannot be compared directly, such as subtraction, addition, time comparison, etc. The following exception will be thrown.

1
TypeError: can't compare offset-naive and offset-aware datetimes

The fundamental reason is that one has time zone information and the other does not.

We can determine exactly which of the two is the case by looking at the tzinfo property of the datetime object.

1
2
3
4
5
6
7
>>> import datetime
>>> now = datetime.datetime.now()
>>> now
datetime.datetime(2020, 5, 26, 18, 36, 59, 811729)
>>> now.tzinfo
>>> now.tzinfo==None
True

It is clear that datetime.datetime.now() is an offset-naive type time object with no time zone information.

We can convert an offset-naive type time object to an offset-aware type time object with the help of the pytz module.

First install the pytz module: pytz module.

1
pip install pytz

To test.

1
2
3
4
5
6
>>> import pytz
>>> now = now.replace(tzinfo=pytz.timezone('UTC'))
>>> now
datetime.datetime(2020, 5, 26, 18, 36, 59, 811729, tzinfo=<UTC>)
>>> now.tzinfo
<UTC>

Conversely, it is possible to convert an offset-aware type to an offset-naive type.

1
2
3
4
5
>>> now = now.replace(tzinfo=None)
>>> now
datetime.datetime(2020, 5, 26, 18, 36, 59, 811729)
>>> now.tzinfo == None
True

USE_TZ configuration

Back to the Django species. We know that when you install Django, you install a pytz module by default, and then under django.utils you have a timezone module, which is actually pytz.timezone.

First, let’s be clear about one thing. USE_TZ defaults to True in Django’s settings configuration, which means that by default, Django databases store date data with timezone information, which is the offset-aware type.

This causes us some times to do time comparison or add or subtract operations, which will throw the exceptions mentioned earlier.

1
TypeError: can't compare offset-naive and offset-aware datetimes

So what to do?

The most primitive and simple way, naturally, is to convert the time type, using the method in the example above, and unify it so that it can be compared.

There is also a simpler way, that is to set USE_TZ to False, at this time, the time object saved in the database does not contain time zone information, and the native datetime.datetime is a type, both are offset-naive type, naturally there is no conflict.

Tips: You should set USE_TZ to False at the beginning of the project, before creating the data table, otherwise the previously saved time data may still have time zone information.

In fact, there is a more applicable method, which is to keep USE_TZ as True, but instead of using the datetime library, use timezone to get the now time.

1
2
3
from django.utils import timezone  

now = timezone.now()

In this way, everyone comes with time zone information and is naturally comparable.