Python LinuxWindowsmacOS

Python: timezone-aware datetime не сравнивается с naive datetime

  • Обновлено
  • Чтение ≈ 3 минуты

Краткий ответ

TypeError при сравнении naive и aware datetime: выясните происхождение каждого времени, задайте исходный часовой пояс явно и сравнивайте моменты в общей зоне.

В этой статье
  1. Что происходит и как распознать неисправность
  2. Как отличить причины
  3. Пошаговая диагностика и исправление
  4. Команды и примеры для проверки
  5. Как проверить результат и что делать дальше

#Что происходит и как распознать неисправность

Ошибка TypeError: can't compare offset-naive and offset-aware datetimes возникает при сравнении <, > и других операций упорядочения дат, если одно значение имеет часовой пояс, а другое нет. Простое присваивание UTC к локальному времени может незаметно сдвинуть события.

#Как отличить причины

Возможная причинаКак узнатьЧто делать
Смешаны datetime.now() и UTCdatetime.now() без аргументов создаёт naive, а API возвращает строку с +00:00.У одного объекта tzinfo равен None.Сравнивайте aware-значения с timezone.utc.
В базе хранится локальное время без зоныСтарое поле типа DATETIME не содержит offset.Всё работает до сравнения с новым API.Выясните исходную временную зону записей и корректно интерпретируйте.
Строки ISO имеют разный форматОдни содержат Z/+03:00, другие не содержат смещения.fromisoformat создаёт разные типы datetime.Нормализуйте формат входа по контракту API.

#Пошаговая диагностика и исправление

  1. Откройте место сравнения в tracebackНайдите строку с оператором > или < в вашем Python-файле, которая вызвала TypeError. Перед ней временно выведите repr(left), left.tzinfo, repr(right), right.tzinfo; используйте тестовые данные, если журнал может раскрыть личные сведения. Если одна зона None, ошибка определена.
  2. Определите, что означает время без часового поясаПосмотрите документацию источника записи: UTC ли это, локальное время сервера или локальное время пользователя? Не угадывайте по текущему часовому поясу компьютера. Запросите спецификацию API/схемы базы данных. Пока происхождение неизвестно, не мигрируйте старые даты и не меняйте их массово.
  3. Используйте aware UTC для текущего моментаЕсли вы сравниваете с текущим временем, замените datetime.now() на datetime.now(timezone.utc), предварительно импортировав datetime, timezone. Новый момент содержит tzinfo UTC. Не применяйте .replace(tzinfo=timezone.utc) к локальному времени: это не переводит часы, а меняет смысл отметки.
  4. Нормализуйте уже aware значенияКогда обе даты имеют реальный tzinfo, переводите в UTC методом astimezone(timezone.utc) для удобства анализа. Он сохраняет тот же момент времени, меняя представление. Если одна дата naive, сначала по источнику определите её зону и только затем присвойте известную исходную зону.
  5. Учитывайте местные переводы часовЕсли исходные данные записаны по местному времени, используйте zoneinfo.ZoneInfo("Europe/Tallinn") или фактическую зону источника. В час перевода часов локальное время может повторяться или отсутствовать; уточните по журналам/смещению значение fold, если событие попадает в неоднозначный интервал. Нельзя механически добавлять фиксированные 2 или 3 часа круглый год.
  6. Измените чтение ISO и проверяйте наличие зоныДля входных строк Python используйте datetime.fromisoformat(...) и проверяйте utcoffset() у результата. Если контракт API требует смещение, при его отсутствии сообщайте об ошибке входных данных, а не молча трактуйте UTC. Если API возвращает Z, проверьте соответствие вашей версии Python или нормализуйте обозначение UTC по документированному формату.
  7. Протестируйте обычную дату и переход на летнее времяСоставьте два теста: дата без перехода часов и дата рядом с переводом времени для нужной зоны. Сравнение двух aware-дат больше не должно вызывать TypeError; результат упорядочения должен соответствовать известным UTC-моментам. Если старые даты интерпретированы неверно, откатите изменение и уточните зону источника до переноса данных.

#Команды и примеры для проверки

Python
Выполняйте в отдельном файле Python 3; в результате будет True или False, но не TypeError. Здесь отметка из API уже содержит зону.
from datetime import datetime, timezone

from_api = datetime.fromisoformat("2026-11-02T05:00:00+00:00")
now = datetime.now(timezone.utc)
print(from_api < now)  # логическое значение зависит от времени запуска
print(from_api.astimezone(timezone.utc).isoformat())

#Как проверить результат и что делать дальше

Проверка: оба операнда имеют ненулевой utcoffset(), сравнение выполняется без ошибки и не меняет хронологический порядок при переводе в UTC. Для хранения новых событий предпочтительна UTC либо строка ISO со смещением.

Источники и обновления

Последнее обновление статьи: 2 ноября 2026. Версии программ и интерфейсы меняются — если шаги не совпадают с вашей версией, сверяйтесь с документацией производителя.