
Polars 当前不支持直接对 pl.time 类型列进行 pl.duration 算术运算;本文介绍一种健壮、可读性强的解决方案:将 time 与任意基准日期组合为 datetime,执行加减后提取 time 部分,自动处理 00:00 ± 小时的跨日逻辑(如 01:00 − 2h → 23:00)。
polars 当前不支持直接对 `pl.time` 类型列进行 `pl.duration` 算术运算;本文介绍一种健壮、可读性强的解决方案:将 time 与任意基准日期组合为 datetime,执行加减后提取 time 部分,自动处理 00:00 ± 小时的跨日逻辑(如 01:00 − 2h → 23:00)。
在 Polars 中操作时间(time)类型列时,一个常见需求是对起止时间进行偏移调整——例如为排班表预留缓冲时间:将 start_time 提前 n 小时,end_time 推后 n 小时。然而,Polars 的 time 类型目前不支持直接与 duration 进行加减运算(该功能仅适用于 date 和 datetime),因此需借助「日期-时间组合」技巧实现安全、闭环的时钟运算。
✅ 推荐方案:用固定日期 + dt.combine() 实现循环时间运算
核心思路是:
- 选择一个任意但确定的基准日期(如
2025-01-01); - 使用
.dt.combine()将time列与该日期拼接为datetime; - 对
datetime执行+ duration或− duration(天然支持跨日进退); - 最终用
.dt.time()提取结果中的时间部分——自动完成23:00 + 2h → 01:00、01:00 − 3h → 22:00等模 24 小时计算。
以下是封装好的可复用函数:
import polars as pl
def add_duration_to_time(time_col: pl.Expr, duration_col: pl.Expr) -> pl.Expr:
"""
对 time 表达式安全添加 duration(支持跨日循环)
示例:
05:00 + 1h → 06:00
23:00 + 2h → 01:00
01:00 - 2h → 23:00
"""
base_date = pl.date(2025, 1, 1) # 任意固定日期,无需真实存在
return (base_date.dt.combine(time_col) + duration_col).dt.time()
# 构建示例数据(含已转为 time 类型的列)
df = pl.DataFrame({
"start_time": ["09:00", "09:00", "09:00", "09:00", "09:00", "00:00", "00:00"],
"end_time": ["18:00", "18:00", "18:00", "18:00", "18:00", "00:00", "00:00"],
}).with_columns(
start_time=pl.col("start_time").str.to_time("%H:%M"),
end_time=pl.col("end_time").str.to_time("%H:%M"),
)
# 定义偏移量:提前/推后 1 小时
n_hours = 1
duration = pl.duration(hours=n_hours)
# 应用偏移:start_time 减、end_time 加
result = df.with_columns(
start_time_adjusted=add_duration_to_time(pl.col("start_time"), -duration),
end_time_adjusted=add_duration_to_time(pl.col("end_time"), duration),
)
print(result)
输出结果(关键列节选):
┌────────────┬──────────┬───────────────────┬───────────────────┐ │ start_time ┆ end_time ┆ start_time_adjusted ┆ end_time_adjusted │ │ --- ┆ --- ┆ --- ┆ --- │ │ time ┆ time ┆ time ┆ time │ ╞════════════╪══════════╪═══════════════════╪═══════════════════╡ │ 09:00:00 ┆ 18:00:00 ┆ 08:00:00 ┆ 19:00:00 │ │ 00:00:00 ┆ 00:00:00 ┆ 23:00:00 ┆ 01:00:00 │ └────────────┴──────────┴───────────────────┴───────────────────┘
⚠️ 注意事项与最佳实践
-
基准日期必须为
date(非datetime):pl.date(2025,1,1)返回的是 naive date,与time组合后生成 naive datetime,避免时区干扰;若使用pl.datetime(),可能因时区或精度引发意外行为。 -
duration单位灵活:除hours=外,也支持minutes=,seconds=,milliseconds=等,甚至可组合使用(如pl.duration(hours=1, minutes=30))。 -
空值(null)安全:该方法天然继承 Polars 的 null 传播机制——若
start_time为 null,则start_time_adjusted也为 null,无需额外处理。 -
性能考量:虽然引入了日期组合步骤,但底层基于 Arrow 高效实现,对百万级行数据仍保持优异性能;远优于 Python 循环或
apply。
? 扩展提示:若需动态偏移(如每行不同小时数),只需将
duration替换为对应数值列(如pl.col("buffer_hours").cast(pl.Int64).mul(3600_000_000)转为微秒 duration),同样适用。
此方法兼顾正确性、可维护性与 Polars 原生风格,是当前生态下操作 time 类型最推荐的工程实践。










