关于 django 在 views 里面 timezone 的疑问?

2016-09-05 19:32:00 +08:00
 hao1032
项目的相关配置:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': name,
'USER': user,
'PASSWORD': pwd,
'HOST': host,
'PORT': port,
}
}

LANGUAGE_CODE = 'zh-CN'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = True

在 view 里面写的查询语句:
now = timezone.now()
query = models.AllItem.objects.filter(add_time__day=now.day)
print query.count()

生成的 sql :
SELECT COUNT(*) AS `__count` FROM `allitem` WHERE EXTRACT(DAY FROM CONVERT_TZ(`allitem`.`add_time`, 'UTC', 'Asia/Shanghai')) = 5

疑问:
1.now 是用 timezone.now()获取的,这时 now 已经是 utc 时间
2.由于配置中 USE_TZ 设置为 True ,那么保存在 add_time 里的时间也是 utc 时间( add time 在 model 中设置为 auto now add 为 True )

那么,为什么在执行查询的时候, django 还要使用 CONVERT_TZ 函数去转换时间?导致我查询的结果都是错误的。

真心求教真相。
3908 次点击
所在节点    Python
11 条回复
tinypig
2016-09-05 23:02:14 +08:00
from django.utils import timezone ?
gkiwi
2016-09-05 23:22:02 +08:00
1.是对的
2. 由于配置中 USE_TZ 设置为 True ,那么保存在 add_time 里的时间也是 utc 时间( add time 在 model 中设置为 auto now add 为 True )
这个是不对的,参照这里: https://docs.djangoproject.com/en/1.10/ref/settings/#time-zone

This allows interacting with third-party databases that store datetimes in local time rather than UTC. To avoid issues around DST changes, you shouldn ’ t set this option for databases managed by Django.

When USE_TZ is True and the database doesn ’ t support time zones (e.g. SQLite, MySQL, Oracle), Django reads and writes datetimes in local time according to this option if it is set and in UTC if it isn ’ t.


也就是说你数据库里面的存在不是 UTC ,而是基于 TIME_ZONE 的地区时间;所以后面才有 CONVERT_TZ 的时区转换;
gkiwi
2016-09-05 23:22:57 +08:00


django.db.backends.mysql _convert_field_to_tz
hao1032
2016-09-06 10:16:15 +08:00
@gkiwi 感谢你的回复,一看就是常用 Django 的。我用的 Django 是 1.8 的版本,在对应的文档中没有 1.10 中描述: https://docs.djangoproject.com/en/1.8/ref/settings/#time-zone
我在 site-packages\django\db\backends\mysql 这个文件夹下的 py 文件里面也没有找到你截图的函数。
那么在 1.8 版本中的处理是否也是你解释的这样呢?
gkiwi
2016-09-06 12:35:53 +08:00
@hao1032

抱歉关于 TIME_ZONE 这个理解错了。


整体重新说一下,虽然 1.8 版本的说明和 1.9 , 1.10 不一样,但是数据库存储的时间都是标准的 UTC 时间。(之前说的是存储的是基于 TIME_ZONE 的时间,这个是错误的)

我本地跑了下 1.8 , 1.9 ,分别设置 TIME_ZONE 为「 UTC 」和「 Asia/Shanghai 」,后台登陆,看数据库里面的时间,也确实是 UTC 时间,现在我电脑时间是, 2016 年 09 月 06 日 12:22:37 ,但是数据库中存储的都是凌晨 4 点的,正好-8 ;

至于你的为什么会出现 CONVERT_TZ ,是因为你开启了 USE_TIME 。
你用的 now.day 是 UTC 时间,而 Django 认为这个时间是 TIME_ZONE 时间;
所以生成 sql 时候,先把数据库中数据时间,转成 TIME_ZONE 时间,然后和你的 day 进行比较;

所以你应该使用


from django.conf import settings
from django.utils import timezone
import pytz

now = timezone.now()
tzinfo = pytz.timezone(settings.TIME_ZONE)
now = now.replace(tzinfo=tzinfo)


然后传入此时的 now.day ,获取到的应该就是正确的数据了
gkiwi
2016-09-06 12:38:44 +08:00
append :

源码部分, django/db/backends/mysql/operations.py , 1.8 和 1.9 处理逻辑一致;
如果是 linux 的话,可以用 ack 搜下 CONVERT_TZ 就出来了。
gkiwi
2016-09-06 12:39:43 +08:00
USE_TIME ==> USE_TZ
gkiwi
2016-09-06 12:44:21 +08:00
如果你服务器时间就是 Asia/Shanghai 的话,直接使用 datetime.now()就好了;
hao1032
2016-09-09 10:07:00 +08:00
@gkiwi 最后我也发现问题了, now 使用 timezone.localtime(timezone.now()) 这样转换一下就行。

我就是疑惑,就像你说的‘你用的 now.day 是 UTC 时间,而 Django 认为这个时间是 TIME_ZONE 时间;’
django 为什么会把 utc 时间理解为 localtime ,这是一个 django 的 bug ,还是就是这样设计的?
gkiwi
2016-09-09 11:50:11 +08:00
@hao1032 不是 bug ;
你传入的是 now.day ,这个是个 int~~
hao1032
2016-09-09 13:21:41 +08:00
@gkiwi 有道理,多谢。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/304119

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX