Django中的时区

博主     2020年05月26日    分类: Django   阅读:4400     评论:0

Django的settings配置中有一个USE_TZ项,默认值为True。很多人不太清楚这个配置项具体是干什么的,只知道和时区有关。下面我们就来详细聊聊它。

首先,我们要了解一下什么是offset-awareoffset-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()

这样,大家都带有时区信息,自然也是可以比较的。


评论总数: 0