PAYATHON 2026

gnuplot 如何将跨日期数据的 x 轴限定为每天 04:00 至 22:00?

数据小许

结论

如果要把不同日期的数据放在同一条 04

至 22
的时间轴上,先按完整格式读取日期时间,再将其换算成从当天午夜起经过的秒数。

原来的 set xrange [14400:79200] 可以用于日内秒数坐标。主要问题是 timecolumn(1,"%H:%M") 与第一列的实际内容不匹配;字符串范围报错还与全局 timefmt 的格式有关。

为什么会出现错误

第一列的实际内容是:

2024-04-05T15:50:51+02

原来的绘图表达式是:

timecolumn(1,"%H:%M")

timecolumn 从指定列的开头解析,不会自动跳过日期去寻找小时。显式传入的 "%H:%M" 会覆盖全局 set timefmt,所以之前设置的完整日期格式无法修正这次读取。使用错误格式得到的 20

不是有效的解析结果。gnuplot 的 timecolumn 文档

set format x "%H:%M" 只控制刻度显示,不会删除 x 坐标中的日期部分。字符串形式的范围端点使用全局 timefmt 解析,而完整日期格式与 "09:00"、"22:00" 不匹配,因此需要分别设置数据和范围的解析格式。gnuplot 时间数据文档

可直接使用的代码

以下代码按输入中写出的时刻绘图,将不同日期映射到同一天的时间轴:

$Data << EOD
2024-04-05T15:50:51+02 2.459
2025-06-06T21:30:51+02 2.339
2026-04-06T09:15:10+02 3.219
EOD

set terminal wxt enhanced
set encoding utf8
set xlabel "Time"
set ylabel "Price"

# Parse the full timestamp from column 1.
datafmt = "%Y-%m-%dT%H:%M:%S+02"

# Convert a timestamp to seconds since midnight.
dayseconds(t) = 3600*tm_hour(t) + 60*tm_min(t) + tm_sec(t)

set xdata time

# Parse string range endpoints as hours and minutes.
set timefmt "%H:%M"
set xrange ["04:00":"22:00"]
set format x "%H:%M"
set xtics 7200

set yrange [0:]
set grid

plot $Data using (dayseconds(timecolumn(1,datafmt))):2 \
    with points pt 5 lc -1 notitle

tm_hour()、tm_min() 和 tm_sec() 分别提取时间戳中的小时、分钟和秒,转换后的坐标只保留一天中的时刻。gnuplot 的时间分量函数文档

按上述方法转换后,三个数据点的 x 坐标分别为:

输入时刻从午夜起经过的秒数
15:50
57051
21:30
77451
09:15
33310

这些数值都在 14400 至 79200 之间。

如何使用数值形式的范围

上述代码中的范围也可以直接替换为:

set xrange [14400:79200]

它对应 04

至 22
,前提是绘图表达式已经将完整日期时间转换为日内秒数。直接绘制完整时间戳时,不能使用这一范围。

如果希望从 09

开始,字符串写法为:

set timefmt "%H:%M"
set xrange ["09:00":"22:00"]

日期、时区和连线需要注意什么

  • 示例中的 +02 在 datafmt 中是固定匹配文本。代码按原字符串中的小时绘图,没有执行时区换算;若输入包含不同偏移,需先确定要比较的是各地时刻还是统一时区的时刻。
  • 转换后,日期信息不再参与 x 坐标。若需要保留跨年时间顺序,应直接绘制完整时间戳,并设置包含日期的范围;仅将刻度显示为 %H:%M 无法实现日内映射。
  • 示例使用散点,方便比较不同日期的日内价格。改为 with linespoints 后,数据点会按输入记录顺序连接,原数据会从 21:30
    连回 09:15
    。需要日内折线时,应先按日内时刻排序,并确认跨日期连线符合需求。

备注:内容仅供参考。