如何从带时区的DatetimeIndex提取可运算绘图的时钟时间并规避偏移误差
偏移问题根因
你遇到的1小时偏移是pytz的使用错误导致:直接将pytz时区对象传入datetime的tzinfo参数时,pytz会默认使用该时区的标准时间(CET为UTC+1),不会自动适配对应日期的夏令时规则(6月CET为UTC+2),才会导致t0和a的时区偏移不一致,运算结果出错。pytz官方要求必须使用localize()方法为原生datetime绑定时区,才能正确匹配对应日期的夏令时规则。
可行解决方案
完全满足保留CET本地时钟时间、可直接运算、可直接绘图三个要求,具体实现如下:
第一步:统一处理时区,消除夏令时偏差
无论原始时间序列是否带时区,先统一转换为适配夏令时的CET时区时间:
import pandas as pd import datetime as dt import pytz import matplotlib.pyplot as plt cet_tz = pytz.timezone("CET") # 原始时间序列a # 示例生成代码:a = pd.date_range("2021-06-01", "2021-06-04", freq="30min").tz_localize("CET") # 统一转成正确的CET时区时间 if a.tz is None: a_cet = a.tz_localize(cet_tz) else: a_cet = a.tz_convert(cet_tz) # 正确生成对应日期的CET时区0点,适配夏令时 naive_t0 = dt.datetime.combine(a_cet[0].date(), dt.time(0,0)) t0 = cet_tz.localize(naive_t0)
此时执行a_cet - t0得到的首项结果为0,和预期一致。
第二步:按需求选择时钟时间转换方案
方案1:转换为当日累计秒数/ timedelta(优先推荐,运算最方便)
直接提取每个CET时间对应当日0点的时间差,数值类型可直接加减比较,也可直接绘图:
# 方法1:转成当日累计秒数,范围0~86399,数值类型 a_time_sec = (a_cet - a_cet.dt.floor('D')).dt.total_seconds() # 运算示例:计算两个时间的差直接相减即可,比较大小也直接用比较运算符 # 方法2:转成timedelta类型,matplotlib可直接识别绘图 a_timedelta = a_cet - a_cet.dt.floor('D') # 绘图示例,x轴自动显示时分格式 plt.plot(a_timedelta, 你的数据列) # 自定义x轴显示样式 plt.gca().xaxis.set_major_formatter(lambda x, pos: f"{int(x//3600):02d}:{int((x%3600)//60):02d}") plt.show()
方案2:映射到固定日期的datetime(和a.time效果一致,保留datetime类型特性)
如果希望保留datetime类型的所有方法,可将所有时间的日期统一替换为无夏令时切换的固定日期,仅保留CET本地的时分秒信息:
# 所有时间的日期统一替换为1970-01-01,保留时分秒和CET时区 a_fake_datetime = a_cet.apply(lambda x: dt.datetime(1970, 1, 1, x.hour, x.minute, x.second, x.microsecond, tzinfo=x.tzinfo))
该序列可直接做加减、比较运算,也可直接传入matplotlib绘图,x轴默认会显示时分秒,不需要额外格式转换。
内容的提问来源于stack exchange,提问作者Marses
相关产品推荐
相关产品推荐

