Django的settings
配置中有一个USE_TZ
项,默认值为True。很多人不太清楚这个配置项具体是干什么的,只知道和时区有关。下面我们就来详细聊聊它。
首先,我们要了解一下什么是offset-aware
与offset-navie
offset-aware与offset-navie
在Python中,有一个datetime
模块,相信大家都很熟悉。但是很少有人知道这个模块的时间还可以分下面两种类型:
- offset-naive:不含时区的类型
- offset-aware:有时区的类型
并且,这两种类型的时间是不可以直接比较的,比如做减法、加法、时间对比等,会抛出下面的异常:
TypeError: can't compare offset-naive and offset-aware datetimes
根本的原因在于两者一个有时区信息,一个没有。
我们可以通过查看datetime
对象的tzinfo
属性,来判断具体是两者中的哪种:
>>> import datetime
>>> now = datetime.datetime.now()
>>> now
datetime.datetime(2020, 5, 26, 18, 36, 59, 811729)
>>> now.tzinfo
>>> now.tzinfo==None
True
可以很清楚的看到,datetime.datetime.now()
是一个offset-naive
类型,不含时区信息的时间对象。
我们可以借助pytz
模块,将一个offset-naive
类型的时间对象转换为一个offset-aware
类型的时间对象。
先安装pytz
模块:
pip install pytz
来测试一下:
>>> import pytz
>>> now = now.replace(tzinfo=pytz.timezone('UTC'))
>>> now
datetime.datetime(2020, 5, 26, 18, 36, 59, 811729, tzinfo=<UTC>)
>>> now.tzinfo
<UTC>
反过来,也可以将一个offset-aware
型转换为offset-naive
型:
>>> now = now.replace(tzinfo=None)
>>> now
datetime.datetime(2020, 5, 26, 18, 36, 59, 811729)
>>> now.tzinfo == None
True
USE_TZ配置
回到Django种来。我们知道,在安装Django的同时,默认会安装一个pytz
模块,然后在django.utils
下就会有一个timezone
模块,其实它就是pytz.timezone
。
首先,我们要清楚一件事。Django的settings
配置中的USE_TZ
默认值为True,也就是说,默认情况下Django数据库里面存储的日期数据是带时区信息的,也就是offset-aware
类型。
这就导致了我们有些时候,进行时间对比或者加减的操作,会抛出前面说的异常。
TypeError: can't compare offset-naive and offset-aware datetimes
那怎么办呢?
最原始朴素的方法,自然是对时间类型进行转换,采用上面的例子中的方法,统一起来,就可以进行比较了。
还有一种更简单的方法,那就是将USE_TZ
设置为False。此时,数据库里保存的时间对象就不在包含时区信息了,和原生的datetime.datetime
是一种类型,都是offset-naive
类型,自然不会发生冲突。
Tips:应该在项目开始之初,创建数据表之前就设置USE_TZ
为False,否则先前保存的时间数据可能依然带有时区信息。
其实,还有一种更适用的方法,就是USE_TZ
依然保持为True,但是不再使用datetime
库了,而是使用timezone
来获取now
时间:
from django.utils import timezone
now = timezone.now()
这样,大家都带有时区信息,自然也是可以比较的。