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 对“日期”查找没有影响的主要内容,如果未能解决你的问题,请参考以下文章