Установка Django TIME_ZONE в DATABASES не влияет на поиск по дате

ОБНОВЛЕНИЕ 8 апреля 2019 г.

Это известная ошибка для django ‹= 2.2, исправленная на данный момент PR

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

(Мы предполагаем бэкенды mysql)

Я могу установить TIME_ZONE несколько раз в settings.py, один для глобального приложения django и один для каждой базы данных (см. https://docs.djangoproject.com/en/1.11/ref/settings/#time-zone (ref1))

Обычно используется устаревшая база данных, где время не хранится в формате UTC.

Нет поиска по дате

Этот параметр учитывается при запросе моей базы данных, например :

In 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*

Связанный SQL-запрос от DEBUG:

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 one 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               |
+--------------------+---------------------+--------------------+

person Victor T    schedule 03.07.2017    source источник
comment
Ваш вопрос почему-то сбивает с толку. Не могли бы вы добавить пример того, чего вы ожидаете и что (неожиданно) происходит?   -  person shad0w_wa1k3r    schedule 06.07.2017
comment
@AshishNitinPatil спасибо за ваш ответ, я отредактировал соответственно   -  person Victor T    schedule 06.07.2017


Ответы (2)


Такого поведения следовало ожидать, поскольку Код Django читает

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'.

Как бы то ни было, я открыл тикет на djangoproject, и он связан с запрос на вытягивание

Заменено на:

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
person Victor T    schedule 07.07.2017

Из документации -

Когда USE_TZ имеет значение True и база данных не поддерживает часовые пояса (например, SQLite, MySQL, Oracle), Django считывает и записывает дату по местному времени в соответствии с этим параметром, если он установлен, и в формате UTC, если он нет.

Итак, похоже (из вашего кода «Я вижу»), что часовой пояс вашего сервера (= db tz = местное время, с момента устаревания) - это UTC. Следовательно, из-за ваших настроек (USE_TZ = True & TZ = Paris) происходит преобразование из UTC в Paris TZ.

Таким образом, вы неправильно поняли документацию, и это не ошибка.

person shad0w_wa1k3r    schedule 06.07.2017
comment
Спасибо, вы правы, Django читает и записывает дату в предоставленном TIME_ZONE из DATABASES, но не с поиском даты. Я переписал вопрос, чтобы подробнее рассказать о проблеме. - person Victor T; 07.07.2017
comment
Часовой пояс сервера не имеет отношения к этой проблеме. Значения из БД читаются как из tz2, также известного как Paris, и конвертируются в Django tz1, также известного как Paris. В моем понимании UTC не должно появляться. - person Victor T; 07.07.2017
comment
Часовой пояс по умолчанию для вашей базы данных будет UTC, потому что часовой пояс вашего сервера (по умолчанию) - UTC. Попробуйте выбрать сейчас (); в вашем db напрямую, это должно сказать вам точный часовой пояс db. - person shad0w_wa1k3r; 07.07.2017
comment
Как и ожидалось, tz моего производственного сервера - это UTC, но проблема, которую я поднимаю, происходит локально, где tz - это CEST (UTC + 2 / Париж). Я добавлю это к вопросу. - person Victor T; 07.07.2017