pytz.timezone实例比较结果不符合预期,寻求合理解决方法
问题说明
在比较pytz.timezone实例的相等性时,会出现不符合预期的结果——明明是同一个时区,某些场景下==比较却返回False。目前临时的解决办法是比较实例的zone字符串,但操作起来比较繁琐。测试环境为Python 3.9、pytz 2022.2.1、pandas 1.5.3,对比之下,标准库的zoneinfo.ZoneInfo对象相等性表现完全符合直觉。
示例1(LMT时区场景)
>>> tz = pytz.timezone("America/Chicago") >>> tz <DstTzInfo 'America/Chicago' LMT-1 day, 18:09:00 STD> >>> tz.localize(datetime.now()).tzinfo == tz False >>> pd.DataFrame({'t': [tz.localize(datetime.now())]}).t.dt.tz == tz True >>> pd.DataFrame({'t': [tz.localize(datetime.now())]}).t.dt.tz_convert(tz).dt.tz == tz True
示例2(CST时区场景)
>>> tz = pytz.timezone("America/Chicago").localize(datetime.now()).tzinfo >>> tz <DstTzInfo 'America/Chicago' CST-1 day, 18:00:00 STD> >>> tz.localize(datetime.now()).tzinfo == tz True >>> pd.DataFrame({'t': [tz.localize(datetime.now())]}).t.dt.tz == tz False >>> pd.DataFrame({'t': [tz.localize(datetime.now())]}).t.dt.tz_convert(tz).dt.tz == tz False
原因拆解
pytz的DstTzInfo实例不仅包含时区的标识符(比如America/Chicago),还会附带当前生效的偏移信息(像LMT、CST对应的时差)。当你调用localize方法后,返回的tzinfo是绑定了具体时间偏移的实例,和pytz.timezone()直接返回的初始实例(可能处于LMT这类历史偏移状态)做==比较时,会同时校验偏移信息,这就导致哪怕时区ID相同,只要偏移不一样,比较结果就是False。
而pandas的dt.tz属性在不同场景下的内部处理逻辑有差异:有时会返回剥离了具体偏移、只保留时区ID的实例,有时则保留偏移,这就出现了比较结果时对时错的情况。
zoneinfo.ZoneInfo的设计则更简单直接,相等性比较只校验时区标识符本身,所以行为完全符合预期。
解决办法
1. 直接比较时区标识符(zone属性)
这是最直接的临时方案,跳过偏移校验,只对比两个时区实例的zone字符串:
# 修正示例1的比较逻辑 tz = pytz.timezone("America/Chicago") localized_tz = tz.localize(datetime.now()).tzinfo print(localized_tz.zone == tz.zone) # 输出True # 修正示例2中pandas场景的比较 df = pd.DataFrame({'t': [tz.localize(datetime.now())]}) print(df.t.dt.tz.zone == tz.zone) # 输出True
2. 基于时区ID重新获取基础实例比较
如果需要用==直接比较,可以把带偏移的tzinfo转换为基础时区实例:
localized_tz = tz.localize(datetime.now()).tzinfo base_tz = pytz.timezone(localized_tz.zone) # 此时base_tz == pytz.timezone("America/Chicago")会返回True
注意:这种方式比较的是不带具体偏移的基础时区实例,是否适用要根据你的业务场景判断。
3. 迁移到zoneinfo(推荐)
如果你的Python版本≥3.9,强烈建议直接用标准库的zoneinfo,它的相等性逻辑直观,还不需要额外安装依赖:
from zoneinfo import ZoneInfo import datetime import pandas as pd tz = ZoneInfo("America/Chicago") localized_dt = datetime.now(tz) print(localized_dt.tzinfo == tz) # 输出True df = pd.DataFrame({'t': [localized_dt]}) print(df.t.dt.tz == tz) # 输出True df_converted = df.t.dt.tz_convert(tz) print(df_converted.dt.tz == tz) # 输出True
内容的提问来源于stack exchange,提问作者Georgi Peev

