python - Django 在 DATABASES 中设置 TIME_ZONE 对 'date' 查找无效

标签 python django django-settings django-database django-1.11

2019 年 4 月 8 日更新

这是 django<=2.2 的一个已知错误,已修复 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 (引用文献 2)

但这行不通,虽然它在逻辑上应该是

>>> 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:

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.

和 ref2 :

When USE_TZ is True, fields are converted to the current time zone before filtering

其次,当 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               |
+--------------------+---------------------+--------------------+

最佳答案

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' 的利益,数据库特定时区被忽略。

为了它的值(value),我打开了一个 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

关于python - Django 在 DATABASES 中设置 TIME_ZONE 对 'date' 查找无效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44890108/

相关文章:

django - 当 commit=False 时,Django 表单 save() 方法中是否需要 save_m2m() ?

python - Django-summernote 工作正常,但显示带有 HTML 标签的文本

python - GAE 项目的自定义用户身份验证

python - Django @override_settings 不允许字典?

django - 安装片段

python - 如何将 TLS 与 asyncore 一起使用?

javascript - JavaScript中字典的使用查询

python - 为什么django的设置对象是一个LazyObject?

Python将列表中的字符串转换为数字

python - 可以在没有**任何**默认环境的情况下安装 Anaconda 吗?