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

Pandas绘图X轴显示UTC而非EDT时区问题求助

解决Matplotlib时间轴时区显示问题(Apple Health心率数据)

我正在为生活日志制作每日心率记录图表,使用Python处理Apple Health导出的数据,代码如下:

period = datetime.today().date() - timedelta(days=1)
archive = zipfile.ZipFile("/Users/steven/Downloads/export.zip", "r")
df = pd.read_xml(
    archive.open("apple_health_export/export.xml"),
    xpath="//Record",
    attrs_only=True,
    parse_dates=["creationDate", "startDate", "endDate"],
)
d1 = df[
    (df["endDate"].dt.date == period)
    & (df["type"] == "HKQuantityTypeIdentifierHeartRate")
]
d1["value"] = [pd.to_numeric(x) for x in d1["value"]]
plt.style.use("ggplot")
plt.rcParams["font.family"] = "PT Sans"
d1.sort_values(by="endDate").plot(
    x="endDate",
    y="value",
    xlabel="Time",
    ylabel="Heart Rate (BPM)",
    title="Heart Rate, Intraday",
).xaxis.set_major_formatter(mdates.DateFormatter("%-I:%M %p"))
plt.tight_layout()
plt.savefig(
    f"/Users/steven/Library/Mobile Documents/com~apple~CloudDocs/Web/gloriouslyawkwardlife.github.io/images/{period}-heartrateintraday.png",
    format="PNG",
)

目前遇到的问题是:图表X轴显示UTC时区时间,而非EDT(GMT-4)时区,尽管原始数据的endDate字段是EDT时区。尝试用df['endDate']=df['endDate'].dt.tz_convert(pytz.timezone('America/New_York'))显式转换时区,但未解决问题。去掉X轴格式化代码后时区显示正确,但时间格式不符合需求,求解决方案。


解决方案

问题根源在于mdates.DateFormatter默认不会识别datetime对象的时区信息,直接采用UTC时间格式化。以下两种方法可解决:

方法1:为DateFormatter指定时区

显式告诉格式化器使用EDT时区解析时间:

import pytz
from matplotlib.dates import DateFormatter

# 确保数据的endDate已转换为EDT时区
d1['endDate'] = d1['endDate'].dt.tz_convert(pytz.timezone('America/New_York'))

# 绘图并配置带时区的格式化器
ax = d1.sort_values(by="endDate").plot(
    x="endDate",
    y="value",
    xlabel="Time",
    ylabel="Heart Rate (BPM)",
    title="Heart Rate, Intraday",
)
# 创建格式化器时传入时区参数
formatter = DateFormatter("%-I:%M %p", tz=pytz.timezone('America/New_York'))
ax.xaxis.set_major_formatter(formatter)

# 后续保存代码不变
plt.tight_layout()
plt.savefig(
    f"/Users/steven/Library/Mobile Documents/com~apple~CloudDocs/Web/gloriouslyawkwardlife.github.io/images/{period}-heartrateintraday.png",
    format="PNG",
)

方法2:转换为本地时区的无时区datetime

将带时区的datetime转换为EDT本地时间后移除时区信息,直接用naive时间格式化:

import pytz

# 转换为EDT时区并移除时区标记
d1['endDate_local'] = d1['endDate'].dt.tz_convert(pytz.timezone('America/New_York')).dt.tz_localize(None)

# 使用本地时间列绘图并格式化
ax = d1.sort_values(by="endDate_local").plot(
    x="endDate_local",
    y="value",
    xlabel="Time",
    ylabel="Heart Rate (BPM)",
    title="Heart Rate, Intraday",
)
ax.xaxis.set_major_formatter(mdates.DateFormatter("%-I:%M %p"))

# 后续保存代码不变
plt.tight_layout()
plt.savefig(
    f"/Users/steven/Library/Mobile Documents/com~apple~CloudDocs/Web/gloriouslyawkwardlife.github.io/images/{period}-heartrateintraday.png",
    format="PNG",
)

关键说明

  • pd.read_xml解析的endDate若包含时区,会生成带时区的datetime64[ns, tz]类型,DateFormatter默认忽略时区信息,需手动指定或转换为本地时间。
  • 方法1保留时区元数据,适合需后续时区操作的场景;方法2更简洁,仅用于本地时间显示。

内容的提问来源于stack exchange,提问作者Steven Buehler

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 14:05:58