Как преобразовать наивное datetime в datetime с учетом DST в Python?

В настоящее время я работаю над серверной частью для системы календаря, которая возвращает наивные даты Python. Принцип работы внешнего интерфейса заключается в том, что пользователь создает различные события календаря, а интерфейс возвращает наивную версию созданного им события (например, если пользователь выбирает 5 октября 2020 г. с 15:00 до 16:00, интерфейс возвращает datetime.datetime (2020, 10, 5, 15, 0, 0) в качестве начала и datetime.datetime (2011, 10, 5, 16, 0, 0) в качестве конца.

Мне нужно сделать следующее: возьмите наивное datetime и преобразуйте его в UTC для хранения в базе данных. Каждый пользователь системы уже указал свои предпочтения часового пояса, поэтому наивное datetime считается тем же часовым поясом, что и их предпочтение часового пояса. Очевидно, что время должно быть хранится относительно UTC, так что если пользователи изменят свой часовой пояс, существующие события по-прежнему будут отображаться в правильное время, в которое они запланировали их.

Внешний интерфейс находится вне моего контроля, поэтому я не могу изменить данные, которые я получаю. Дизайн базы данных также находится вне моего контроля, поэтому я не могу изменить, какие данные хранятся и как.

Вот примерный подход, который я использовал до сих пор:

import pytz
def convert_to_UTC(naive_datetime, user_tz_preference):
    user_datetime = naive_datetime.replace(tzinfo=user_tz_preference)
    utc_datetime = user_datetime.astimezone(pytz.utc)

Проблема, с которой я столкнулся, связана с переходом на летнее время:

>>> from datetime import datetime
>>> import pytz
>>> user_tz_preference = pytz.timezone('US/Pacific')
>>> naive_datetime = datetime(2011, 10, 26, 12, 0, 0)
>>> user_datetime = naive_datetime.replace(tzinfo=user_tz_preference)
>>> user_datetime
datetime.datetime(2011, 10, 26, 12, 0, tzinfo=<DstTzInfo 'US/Pacific' PST-1 day, 16:00:00 STD>)
>>> received_utc = user_datetime.astimezone(pytz.utc)
>>> received_utc
datetime.datetime(2011, 10, 26, 20, 0, tzinfo=<UTC>)
>>> expected_utc = datetime(2011, 10, 26, 19, 0, tzinfo=pytz.utc)
>>> expected_utc == received_utc
False

Обратите внимание, что использование 'replace' устанавливает часовой пояс на PST вместо PDT независимо от даты , что дает ему смещение UTC на 8 часов вместо ожидаемого смещения на летнее время в 7 часов, поэтому время в конечном итоге сохраняется неправильно.

Какие у меня есть варианты для преобразования простого datetime в правильное PDT (или другое летнее время относительно часового пояса) tzinfo?

(Также обратите внимание, что не все пользователи живут в часовом поясе, в котором соблюдается летнее время, или могут жить в часовом поясе, который переключается в разное время, поэтому для решения, такого как временная коррекция перед сохранением, мне понадобится чтобы узнать, поддерживает ли часовой пояс DST и в какие даты он переключается).

13
задан Jay 2 November 2011 в 20:15
поделиться