使用pytz比较US/Pacific时间为何结果偏差1小时?
问题根因
这是pytz库的经典反直觉设计坑,由它和Python标准库datetime的交互逻辑导致:
- pytz的时区对象不存储固定UTC偏移,它保存的是对应地区全量历史、当前、未来的时区偏移规则(包括早年的本地平均时间、历次夏令时调整、时区变更记录)。只有调用pytz提供的
fromutc()、localize()方法绑定到具体时间点时,它才会匹配该时间点实际生效的偏移值。 - 你直接给datetime构造函数传
tzinfo=tz时,datetime不会触发pytz的偏移匹配逻辑,只会取pytz时区对象默认的初始偏移——也就是19世纪时区标准化之前的LMT(本地平均时间)偏移,对应UTC-7:53,和你6月实际生效的PDT(UTC-7)偏移差了整整53分钟。
这就是为什么两个时间明明用了同一个tz变量,tzinfo却不一样:tmp_date是通过fromutc()转换来的,拿到的是2022年6月正确的PDT偏移;tmp_date_threshold是直接构造的,拿到的是早就废弃的LMT偏移。
两个时间的基准偏移差了53分钟,所以哪怕PDT时间到了9点33分,换算到LMT的时间基准下还没到9点,比较结果自然是False,要等到PDT时间9点53分(也就是你感知的接近10点)才会返回True,和你观察到的现象完全一致。
修复方法
构造带pytz时区的时间时,永远不要直接给datetime构造函数传tzinfo参数,正确做法是先生成不带时区的naive时间,再用tz.localize()绑定时区,让pytz自动匹配对应时间的正确偏移:
from datetime import datetime, timedelta import pytz tz = pytz.timezone('US/Pacific') # 获取当前US/Pacific时间,也可以直接用datetime.now(tz) tmp_date = tz.fromutc(datetime.utcnow()) # 构造当天9点的阈值:先生成无时区时间,再localize naive_9am = datetime(year=tmp_date.year, month=tmp_date.month, day=tmp_date.day, hour=9) tmp_date_threshold = tz.localize(naive_9am) # 此时两个时间的tzinfo都是对应日期生效的PDT偏移,比较结果完全准确 tmp_date = tmp_date + timedelta(hours=3) print(tmp_date > tmp_date_threshold) # 时间过了9点就会返回True,无偏移误差
额外提示:如果使用Python 3.9及以上版本,优先用标准库自带的
zoneinfo模块替代pytz,没有这类反直觉的设计问题,可以直接在datetime构造时传入时区对象,自动匹配对应时间的正确偏移。
内容的提问来源于stack exchange,提问作者user37216
相关产品推荐
相关产品推荐

