#Что произошло и когда проявляется ошибка
Сравнение дат Python даёт ошибку или неверный порядок, если одни datetime имеют часовой пояс, а другие — нет, либо используется локальное время без учёта смены летнего времени.
#Причины и диагностические признаки
| Возможная причина | Как узнать | Что делать |
|---|---|---|
Naive и aware одновременноУ одной даты tzinfo is None, у другой известен часовой пояс. | TypeError: can't compare offset-naive and offset-aware datetimes | Явно определить смысл исходной локальной даты. |
Неверно применяется replace(tzinfo=...)replace назначает пояс, но не преобразует момент времени. | Ожидаемое местное время сдвигается на часы | Для конвертации использовать astimezone. |
Смена летнего времениЛокальная дата без уточнения fold может быть неоднозначной. | В осенний переход есть два одинаковых локальных часа | Хранить UTC и явный пояс, уточнять источник времени. |
#Подробная пошаговая диагностика и исправление
- 1. Проверьте входные значенияВ копии скрипта выведите для обеих дат
isoformat(),tzinfo,utcoffset(); если дата содержит персональные сведения, не записывайте её в общедоступные журналы. Значение без+03:00,Zили иного смещения нужно трактовать по договорённости с источником, а не автоматически как UTC. - Пример кода по темеPythonВыполняйте после создания объектов datetime a и b; у naive datetime utcoffset() обычно None.
print(a.isoformat(), a.tzinfo, a.utcoffset()) print(b.isoformat(), b.tzinfo, b.utcoffset())
- 2. Нормализуйте даты с известными поясами в UTCДля aware-дат выполняйте
astimezone(timezone.utc)перед сравнением. Оба значения должны иметь определённый часовой пояс; это сохраняет момент времени и предотвращает путаницу между регионом и смещением. - Пример кода по темеPythonОбразец кода для файла .py в тестовой копии проекта; перед внедрением замените имена файлов на реальные.
from datetime import datetime, timezone a = datetime.fromisoformat("2026-01-01T12:00:00+03:00") b = datetime.fromisoformat("2026-01-01T09:30:00+00:00") print(a.astimezone(timezone.utc) < b.astimezone(timezone.utc))
- 3. Для исходного локального времени уточните зонуЕсли входное время заведомо задано по Москве, используйте
ZoneInfo("Europe/Moscow")только после подтверждения источника, затем приведите его к UTC. Для неизвестной исходной зоны не назначайте UTC автоматически. На некоторых системах потребуется актуальная база часовых поясов. - Пример кода по темеPythonОбразец кода для файла .py в тестовой копии проекта; перед внедрением замените имена файлов на реальные.
from datetime import datetime, timezone from zoneinfo import ZoneInfo local = datetime(2026, 11, 2, 12, 0, tzinfo=ZoneInfo("Europe/Moscow")) print(local.astimezone(timezone.utc).isoformat())
- 4. Проверьте пограничные датыСоздайте тесты для одного и того же момента с разными смещениями, переходов через полночь и дат перевода часов в фактическом регионе. Не делайте вывод о совпадении момента по совпадению часов на циферблате. Для неоднозначных местных часов согласуйте правило обработки
foldна уровне приложения.
- 5. Исправьте сохранение и отображениеДля событий храните однозначный момент времени (обычно UTC) и при показе пользователю преобразуйте через
astimezoneв нужный регион. Повторите сравнение на тестовой базе и сверяйте упорядочивание двух контрольных событий.
#Как проверить, что проблема устранена
Даты с известными часовыми поясами сортируются в порядке фактических моментов времени, без TypeError и с тестами на границах суток.
#Если исправление не помогло
Если источник присылает время без зоны, до исправления формата обмена документируйте допущение, а в неоднозначных периодах запрашивайте смещение. replace(tzinfo=timezone.utc) не преобразует произвольное местное время в UTC.