如何将朴素的datetime转换为Python中的夏令时感知datetime?

21 投票
2 回答
16601 浏览
提问于 2025-04-17 05:32

我现在正在开发一个日历系统的后台,这个系统返回的是简单的Python日期时间。前端的工作方式是,用户创建各种日历事件,前端会返回他们创建的事件的简单版本。例如,如果用户选择了2020年10月5日下午3点到4点,前端会返回 datetime.datetime(2020, 10, 5, 15, 0, 0) 作为开始时间,datetime.datetime(2020, 10, 5, 16, 0, 0) 作为结束时间。

我需要做的就是把这个简单的日期时间转换成UTC格式,以便存储到数据库中。系统的每个用户已经指定了他们的时区偏好,所以这个简单的日期时间被认为是与他们的时区偏好相同的。显然,这些日期时间需要相对于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小时夏令时偏移量,因此时间最终保存得不正确。

我有什么选项可以将简单的日期时间转换为正确的PDT(或其他时区相关的夏令时)时区信息呢?

(另外,请注意,并不是所有用户都生活在观察夏令时的时区,或者可能生活在不同时间切换的时区,因此为了在保存之前进行时间差修正,我需要知道该时区是否支持夏令时,以及它在什么日期切换)。

2 个回答

10

在Python 3.9的标准库中,有一个叫做zoneinfo的东西:

from datetime import datetime
from zoneinfo import ZoneInfo

naive_datetime = datetime(2011, 10, 26, 12, 0, 0)
user_tz_preference = ZoneInfo("America/Los_Angeles") # former US/Pacific

# it is safe to replace the tzinfo:
user_datetime = naive_datetime.replace(tzinfo=user_tz_preference)
# ...or set it directly:
user_datetime = datetime(2011, 10, 26, 12, tzinfo=ZoneInfo("America/Los_Angeles"))

# astimezone works as before:
utc_datetime = user_datetime.astimezone(ZoneInfo("UTC"))

print(repr(user_datetime))
# datetime.datetime(2011, 10, 26, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='US/Pacific'))
print(user_datetime.isoformat())
# 2011-10-26T12:00:00-07:00
print(utc_datetime.isoformat())
# 2011-10-26T19:00:00+00:00

文档

25

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

撰写回答