Как вы конвертируете наивное datetime в DAT-время datetime в 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 часов вместо ожидаемого смещения DST на 7 часов, поэтому время заканчивается тем, что оно сохраняется некорректно.

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

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

Ответ 1

Функция Pytz localize может сделать это: http://pytz.sourceforge.net/#localized-times-and-date-arithmetic

from datetime import datetime
import pytz    

tz = pytz.timezone('US/Pacific')
naive_dt = datetime(2020, 10, 5, 15, 0, 0) 
utc_dt = tz.localize(naive_dt, is_dst=None).astimezone(pytz.utc)
# -> 2020-10-05 22:00:00+00:00