You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 20:11:02