Django 在 DATABASES 中设置 TIME_ZONE 对“日期”查找没有影响

Posted

技术标签:

【中文标题】Django 在 DATABASES 中设置 TIME_ZONE 对“日期”查找没有影响【英文标题】:Django setting TIME_ZONE in DATABASES has no effect with 'date' lookup 【发布时间】:2017-12-06 23:45:59 【问题描述】:

2019 年 4 月 8 日更新

这是 django 的一个已知错误PR 起已修复

==================================

(我们假设 mysql 后端)

我可以在settings.py中设置TIME_ZONE多次,全局django应用一次,每个数据库一次(见https://docs.djangoproject.com/en/1.11/ref/settings/#time-zone(ref1))

典型的用法是用于日期时间不以 UTC 存储的旧数据库。

没有日期查询

查询我的数据库会考虑此设置,例如:

settings.py

USE_TZ = True
TIME_ZONE = 'Europe/Paris' # tz1
DATABASES = 
    'legacy': 
        'ENGINE': 'django.db.backends.mysql',
        'OPTIONS': 
            'read_default_file': '....cnf',
        ,
        'TIME_ZONE': 'Europe/Paris', # tz2
    ,
    'default' : 
        'ENGINE': 'django.db.backends.mysql',
        'OPTIONS': 
            'read_default_file': '....cnf',
        ,
    

manage.py shell

>>> dt = timezone.make_aware(datetime.datetime(2017, 7, 6, 20, 50))
>>> dt
datetime.datetime(2017, 7, 6, 20, 50, tzinfo=<DstTzInfo 'Europe/Paris' CEST+2:00:00 DST>)
>>> MyModel.objects.filter(my_datetime_field=dt).exists()
True

这是因为我的数据库读取'2017-07-06 20:50:00'

带日期查询

相关文档https://docs.djangoproject.com/en/1.11/ref/models/querysets/#date (ref2)

但这不起作用,虽然它在逻辑上应该这样做

>>> MyModel.objects.filter(my_datetime_field__date=dt.date()).exists()
False*

来自 DEBUG 的相关 SQL 查询是:

SELECT (1) AS `a` FROM `my_model` WHERE DATE(CONVERT_TZ(`my_model`.`my_datetime_field`, 'UTC', 'Europe/Paris')) = '2017-07-06' LIMIT 1;

(*) 请注意,我没有填写 MySQL 中的时区表,因此在这种情况下结果应该是 True,但可能是接近午夜时的 False。 相关文档为https://dev.mysql.com/doc/refman/5.7/en/mysql-tzinfo-to-sql.html

有两件事是错误的。首先,转换应该是从巴黎到巴黎,而不是UTC到巴黎。转换应该从数据库时区 tz2 到 django 应用程序一个 tz1。

确实来自 ref1:

当 USE_TZ 为 True 并且数据库不支持时区(例如 SQLite、MySQL、Oracle)时,Django 读取并根据此选项在本地时间写入日期时间(如果已设置且在如果不是 UTC。

和 ref2 :

当 USE_TZ 为 True 时,字段在过滤前转换为当前时区

其次,当 tz1 == tz2 时,应该不需要使用CONVERT_TZ,并且查询将在 MySQL 中没有时区表的情况下工作。

显式查询是:

mysql> SELECT (1) AS `a` FROM `my_model` WHERE `my_model`.`my_datetime_field` = '2017-07-06 20:50:00' LIMIT 1;
+---+
| a |
+---+
| 1 |
+---+
1 row in set (0.00 sec)

mysql> SELECT (1) AS `a` FROM `my_model` WHERE DATE(`my_model`.`my_datetime_field`) = '2017-07-06' LIMIT 1;
+---+
| a |
+---+
| 1 |
+---+
1 row in set (0.00 sec)

为什么'UTC' 出现在查询中?不应该是'Europe/Paris'吗?

我误解了文档中的某些内容,还是错误?

谢谢。

编辑:我的系统 tz 不是 UTC,如果这有帮助的话

mysql> SELECT @@global.time_zone, @@session.time_zone, @@system_time_zone;
+--------------------+---------------------+--------------------+
| @@global.time_zone | @@session.time_zone | @@system_time_zone |
+--------------------+---------------------+--------------------+
| SYSTEM             | SYSTEM              | CEST               |
+--------------------+---------------------+--------------------+

【问题讨论】:

您的问题由于某种原因令人困惑。您能否添加一个示例,说明您所期望的以及(意外)正在发生的事情? @AshishNitinPatil 感谢您的回复,因此我进行了编辑 【参考方案1】:

自 Django's code reads 以来,这种行为是意料之中的

django/db/backends/mysql/operations.py

def _convert_field_to_tz(self, field_name, tzname):
    if settings.USE_TZ:
        field_name = "CONVERT_TZ(%s, 'UTC', %%s)" % field_name
        params = [tzname]
    else:
        params = []
    return field_name, params

为了'UTC' 的利益,忽略了数据库特定的时区。

为了它的价值,我开了一个 ticket on djangoproject 和它相关的 pull request

替换为:

def _convert_field_to_tz(self, field_name, tzname):
    if settings.USE_TZ and self.connection.timezone_name != tzname:
        field_name = "CONVERT_TZ(%s, '%s', %%s)" % (field_name, self.connection.timezone_name)
        params = [tzname]
    else:
        params = []
    return field_name, params

【讨论】:

【参考方案2】:

来自documentation-

当 USE_TZ 为 True 且数据库不支持时区(例如 SQLite、MySQL、Oracle)时,Django 在本地时间读取和写入日期时间如果它被设置并且在如果不是 UTC。

因此,看起来(从您的“我看到”代码)您的服务器时区(= db tz = 本地时间,因为旧版)是 UTC。因此,由于您的设置 (USE_TZ=True & TZ=Paris),正在发生从 UTC 到巴黎 TZ 的转换。

因此,您误解了文档,这不是错误。

【讨论】:

谢谢,你说得对,Django 从 DATABASES 读取和写入提供的 TIME_ZONE 中的日期时间,但不使用日期查找。我重写了问题以更具体地说明问题。 服务器时区与此问题无关。数据库中的值从 tz2 aka Paris 读取,并再次转换为 Django tz1 aka Paris。 UTC 不应该出现在我的理解中。 您的数据库的默认时区是 UTC,因为您的服务器时区(默认)是 UTC。试试“现在选择();”直接在您的数据库中,这应该会告诉您确切的数据库时区。 我的生产服务器的 tz 是预期的 UTC,但我提出的问题发生在 tz 为 CEST (UTC+2/Paris) 的本地。我会把这个添加到问题中。

以上是关于Django 在 DATABASES 中设置 TIME_ZONE 对“日期”查找没有影响的主要内容,如果未能解决你的问题,请参考以下文章

Django知识4----models层

python - django (ORM使用步骤)

django 连接mysql

Django 配置Mysql数据库

django 连接mysql 数据库

Django中mysql配置设置