使用matplotlib mdates无法保留时区信息的问题求助
Matplotlib mdates 时区处理异常:EDT时间转换丢失4小时问题
我需要绘制补丁,借助matplotlib的mdates模块控制刻度标记。此前使用该模块时,始终无法得到预期的EDT时间(无需使用特殊时区),原本期望转换后的值与初始值一致,实际却丢失了4小时。
备注
- 本机时间:2024年11月1日 星期五 上午10:59:04 EDT
- mpl.rcParams['timezone'] = 'UTC'
相关代码
print(f"Start Date: {startDt} TYPE: {type(startDt)}") print(f"End Date: {endDt} TYPE: {type(endDt)}") startDt_utc = startDt.tz_convert('UTC') endDt_utc = endDt.tz_convert('UTC') print(f'RAW :: start: {startDt:%b-%d:: %I:%M %p %Z}, end: {endDt:%b-%d:: %I:%M %p %Z}') print(f'UTC :: start: {startDt_utc:%b-%d:: %I:%M %p %Z}, end: {endDt_utc:%b-%d:: %I:%M %p %Z}') print(f'RAW mdates :: start: {mdates.date2num(startDt)}, end: {mdates.date2num(endDt)}') print(f'UTC mdates :: start: {mdates.date2num(startDt_utc)}, end: {mdates.date2num(endDt_utc)}') s,e = mdates.date2num(startDt),mdates.date2num(endDt) sutc,eutc = mdates.date2num(startDt_utc),mdates.date2num(endDt_utc) newS,newE = mdates.num2date(s,tz=pytz.timezone('US/Eastern')), mdates.num2date(e,tz=pytz.timezone('US/Eastern')) newSutc,newEutc = mdates.num2date(sutc,tz=pytz.timezone('US/Eastern')), mdates.num2date(eutc,tz=pytz.timezone('US/Eastern')) print(f'RAW cvtr :: start: {newS:%b-%d:: %I:%M %p %Z}, end: {newE:%b-%d:: %I:%M %p %Z}') print(f'UTC cvtr :: start: {newSutc:%b-%d:: %I:%M %p %Z}, end: {newEutc:%b-%d:: %I:%M %p %Z}') ##### newS,newE = mdates.num2date(s,tz=pytz.timezone('Etc/GMT+8')), mdates.num2date(e,tz=pytz.timezone('Etc/GMT+8')) print(f'cvtr Random :: start: {newS:%b-%d:: %I:%M %p %Z}, end: {newE:%b-%d:: %I:%M %p %Z}')
代码输出
Start Date: 2024-10-31 13:52:01-04:00 TYPE: <class 'pandas._libs.tslibs.timestamps.Timestamp'> End Date: 2024-11-01 10:51:01-04:00 TYPE: <class 'pandas._libs.tslibs.timestamps.Timestamp'> RAW :: start: Oct-31:: 01:52 PM UTC-04:00, end: Nov-01:: 10:51 AM UTC-04:00 UTC :: start: Oct-31:: 05:52 PM UTC, end: Nov-01:: 02:51 PM UTC RAW mdates :: start: 20027.744456018518, end: 20028.618761574075 UTC mdates :: start: 20027.744456018518, end: 20028.618761574075 RAW cvtr :: start: Oct-31:: 05:52 PM EDT, end: Nov-01:: 02:51 PM EDT UTC cvtr :: start: Oct-31:: 05:52 PM EDT, end: Nov-01:: 02:51 PM EDT cvtr Random :: start: Oct-31:: 01:52 PM -08, end: Nov-01:: 10:51 AM -08
测试代码及输出
我尝试过输入UTC时间以及特定np.datetime64类型,但均无效,mdates似乎无法正确处理tzinfo。能单独转换单个值,但不理解mdates为何无法处理时区信息,测试代码及输出如下:
someTime = dfDeviceStatus['time'][0] print(f"INIT TIME: {someTime:'%Y-%m-%dT%H:%M:%S%z'}") utc_dt = someTime.tz_convert('UTC') print(f"CONVERT TO UTC: {utc_dt:'%Y-%m-%dT%H:%M:%S%z'}") mplDate = mdates.date2num(utc_dt) print(f"CONVERT TO mdates num: {mplDate}") newDT = mdates.num2date(mplDate) print(f"CONVERT TO mdates date: {newDT:'%Y-%m-%dT%H:%M:%S%z'}") tzInfo = timedelta(days=-1, seconds=72000) print(f"TZINFO (from inital known tzinfo): {tzInfo}") offset = dfDeviceStatus['time'][0].to_pydatetime() - newDT print(f"offset {offset}") offDt = newDT + offset + tzInfo print(f"FINAL DT {offDt:'%Y-%m-%dT%H:%M:%S%z'}")
测试输出
INIT TIME: '2024-10-31T13:52:01-0400' CONVERT TO UTC: '2024-10-31T17:52:01+0000' CONVERT TO mdates num: 20027.744456018518 CONVERT TO mdates date: '2024-10-31T21:52:01+0000' TZINFO (from inital known tzinfo): -1 day, 20:00:00 offset -1 day, 20:00:00 FINAL DT '2024-10-31T13:52:01+0000'
解决方案
问题根源
mdates.date2num在处理带时区的Pandas Timestamp时,会忽略时区信息,直接将时间的本地数值转换为Matplotlib的日期数(本质是基于UTC的浮点数)。再加上你设置了mpl.rcParams['timezone'] = 'UTC',导致num2date转换时默认用UTC解析数值,再转EDT时区就会出现4小时的偏移。
具体解决方法
方法1:统一时区转换流程
先将带时区的Timestamp转换为目标时区(US/Eastern)的naive datetime(移除tzinfo),再传入mdates处理:
import pytz import matplotlib.dates as mdates import matplotlib as mpl # 转换为US/Eastern时区的naive时间 startDt_edt = startDt.tz_convert('US/Eastern').tz_localize(None) endDt_edt = endDt.tz_convert('US/Eastern').tz_localize(None) # 转换为mdates数值 s, e = mdates.date2num(startDt_edt), mdates.date2num(endDt_edt) # 转换回时间时指定US/Eastern时区 newS, newE = mdates.num2date(s, tz=pytz.timezone('US/Eastern')), mdates.num2date(e, tz=pytz.timezone('US/Eastern')) print(f'修正后 :: start: {newS:%b-%d:: %I:%M %p %Z}, end: {newE:%b-%d:: %I:%M %p %Z}')
方法2:修改Matplotlib时区配置
直接将Matplotlib的全局时区改为目标时区,避免多次转换冲突:
mpl.rcParams['timezone'] = 'US/Eastern' # 直接使用带时区的Timestamp转换 s = mdates.date2num(startDt) newS = mdates.num2date(s) print(f'修正后 :: start: {newS:%b-%d:: %I:%M %p %Z}')
关键注意事项
mdates.date2num仅正确处理naive datetime或带UTC时区的时间,其他时区会被忽略;num2date的tz参数会将Matplotlib的UTC基准日期数转换为指定时区的时间;- Pandas的
tz_convert是转换时区,tz_localize是给naive时间添加时区,两者不要混淆。
内容的提问来源于stack exchange,提问作者Yolkions
相关产品推荐
相关产品推荐

