V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
hao1032
V2EX  ›  Python

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

  •  
  •   hao1032 · 2016-09-05 19:32:00 +08:00 · 3884 次点击
    这是一个创建于 3002 天前的主题,其中的信息可能已经有所发展或是发生改变。
    项目的相关配置:
    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 函数去转换时间?导致我查询的结果都是错误的。

    真心求教真相。
    11 条回复    2016-09-09 13:21:41 +08:00
    tinypig
        1
    tinypig  
       2016-09-05 23:02:14 +08:00
    from django.utils import timezone ?
    gkiwi
        2
    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
        3
    gkiwi  
       2016-09-05 23:22:57 +08:00


    django.db.backends.mysql _convert_field_to_tz
    hao1032
        4
    hao1032  
    OP
       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
        5
    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
        6
    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
        7
    gkiwi  
       2016-09-06 12:39:43 +08:00
    USE_TIME ==> USE_TZ
    gkiwi
        8
    gkiwi  
       2016-09-06 12:44:21 +08:00
    如果你服务器时间就是 Asia/Shanghai 的话,直接使用 datetime.now()就好了;
    hao1032
        9
    hao1032  
    OP
       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
        10
    gkiwi  
       2016-09-09 11:50:11 +08:00   ❤️ 1
    @hao1032 不是 bug ;
    你传入的是 now.day ,这个是个 int~~
    hao1032
        11
    hao1032  
    OP
       2016-09-09 13:21:41 +08:00
    @gkiwi 有道理,多谢。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1067 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 37ms · UTC 23:11 · PVG 07:11 · LAX 15:11 · JFK 18:11
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.