Django извлекает из базы данных postgres неправильные объекты времени (переопределен часовой пояс)

У меня возникла проблема с Django, psycopg и PostgreSQL, связанная с обработкой временных интервалов с учетом временных зон. Ниже приведены детали моей установки и проблема, с которой я столкнулся:

Окружение

Library Version
psycopg 3.1.18
Django 4.2.10
PostgreSQL 16.1

Описание выпуска

У меня есть модель Django с DateTimeField, которая создает колонку timestamp with timezone в PostgreSQL. Когда я создаю новый экземпляр модели с временной шкалой и сохраняю его в базе данных, временная шкала сохраняется правильно, что подтверждается просмотром в таких средствах просмотра базы данных, как pgAdmin или DataGrip.

Однако при извлечении данных обратно в Django дататайм отображается с неправильным смещением. В частности, он, похоже, переопределяет исходное смещение часового пояса на 0 (UTC), в то время как время остается неизменным, что приводит к неправильным значениям datetime.

Пример

  • Время, переданное в модель: 2024-02-18 01:46:29+01:00
  • Время, хранящееся в базе данных (просматривается с помощью DataGrip): 2024-02-18 01:46:29.000000 +01:00
  • Время, полученное в Django: 2024-02-18 01:46:29+00:00

Это расхождение говорит о том, что вместо того, чтобы просто преобразовать время в UTC, Django меняет часовой пояс на UTC, но сохраняет время из часового пояса UTC+1, что неверно.

Настройки Django

У меня следующие настройки в моем settings.py:

USE_TZ = True
TIME_ZONE = 'UTC'

Я поэкспериментировал с параметром TIME_ZONE в переменной DATABASES, установив его в значение TIME_ZONE: Europe/Zurich, что позволило исправить часовой пояс на UTC+1. Однако это решение, похоже, корректирует часовой пояс, в котором я получаю данные, а не основную проблему неправильной обработки часового пояса.

Дополнительные замечания При использовании psycopg напрямую (без Django) эта проблема не возникает, и данные возвращаются с правильным часовым поясом. Это позволяет предположить, что проблема может быть связана с обработкой часовых поясов в Django.

Вопросы Я неправильно понимаю работу Django с часовыми поясами? Может быть, я пропустил какой-то шаг настройки или такое поведение может быть ошибкой? Буду признателен за любые соображения или рекомендации по решению этой проблемы.

Я определил основную причину проблемы, с которой мы столкнулись. Проблема исходила от PostgreSQL. Однако из-за того, как Django обрабатывает ответы, связанные с датами, ошибка была заметна только на стороне Django.

Проблема в PostgreSQL:

Проблема заключалась в том, что PostgreSQL не устанавливал часовой пояс, как ожидалось. Хотя фактическое время было правильным, установка часового пояса в любое значение работала нормально, за исключением UTC. Когда указывался UTC, PostgreSQL возвращала время в системном часовом поясе, а не в UTC. Это поведение было подтверждено в ходе тестирования с использованием psql:

slick26=# SET TIME ZONE 'Europe/London';
SET
slick26=# SHOW TIMEZONE;
   TimeZone
---------------
Europe/London
(1 row)

slick26=# SELECT now();
            now
-------------------------------
2024-03-03 12:27:16.057474+00
(1 row)

slick26=#
slick26=# SET TIME ZONE 'UTC';
SET
slick26=# SHOW TIMEZONE;
TimeZone
----------
UTC
(1 row)

slick26=# SELECT now();
            now
-------------------------------
2024-03-03 13:28:54.533239+01
(1 row)

slick26=#

Основная проблема была связана с запуском PostgreSQL в контейнере. Чтобы правильно выровнять часовой пояс контейнера, я смонтировал следующие тома и установил соответствующие переменные окружения:

  • Тома:
    • /etc/timezone:/etc/timezone:ro
    • /etc/localtime:/etc/localtime:ro
  • Переменные окружения:
    • TZ: Europe/Zurich
    • PGTZ: Europe/Zurich

Однако попытка смонтировать localtime не привела к ожидаемому результату; симлинк продолжал указывать на UTC.

Проблема в Django:

Django устанавливает часовой пояс соединения как UTC. Однако, когда база данных отвечает значением времени в UTC+1, возникает несоответствие. В то время как другие клиенты отображали бы возвращаемый результат как есть, Django переопределяет его с ожидаемым часовым поясом (в данном случае UTC), что приводит к отображению неправильных значений времени.

Вернуться на верх