I have a table representing a schedule, i.e. it contains day (monday-sunday), start_time and end_time fields
df = pl.DataFrame({
"day": ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"],
"enabled": [True, True, True, True, True, False, False],
"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"],
})
df = df.with_columns(start_time = pl.col("start_time").str.to_time("%H:%M"))
df = df.with_columns(end_time = pl.col("end_time").str.to_time("%H:%M"))
print(df)
shape: (7, 4)
┌───────────┬─────────┬────────────┬──────────┐
│ day ┆ enabled ┆ start_time ┆ end_time │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ bool ┆ time ┆ time │
╞═══════════╪═════════╪════════════╪══════════╡
│ monday ┆ true ┆ 09:00:00 ┆ 18:00:00 │
│ tuesday ┆ true ┆ 09:00:00 ┆ 18:00:00 │
│ wednesday ┆ true ┆ 09:00:00 ┆ 18:00:00 │
│ thursday ┆ true ┆ 09:00:00 ┆ 18:00:00 │
│ friday ┆ true ┆ 09:00:00 ┆ 18:00:00 │
│ saturday ┆ false ┆ 00:00:00 ┆ 00:00:00 │
│ sunday ┆ false ┆ 00:00:00 ┆ 00:00:00 │
└───────────┴─────────┴────────────┴──────────┘
I need to subtract n hours from the start_time and add n hours to the end_time. I cannot find a polars operation to add/subtract hours from a pl.time
- I've tried adding a pl.duration
but that only appears to work for date
and datetime
.
One work-around I've assumed is to turn start_time / end_time into a pl.datetime
(i.e. use some constant date), do the operation and then decompose the result back to a time. This has one option of being easier to ensure I don't over/underflow (i.e. subtract 2 hours from 01:00 and end up with 23:00) but I'm wondering it's possible to add/subtracts hours/minutes to a time in polars?
You are right, arithmetic operations between time
and duration
are not implemented. So we have to do some workaround. The most straightforward method is indeed to combine time with an arbitrary date to form a datetime
object, do the math and then keep only the time part. We can avoid introducing a date by casting hours and duration to their underlying representation, but it would be much uglier.
import polars as pl
# create dataframe
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"),
duration=pl.duration(hours=1),
)
print(df)
┌────────────┬──────────┬──────────────┐
│ start_time ┆ end_time ┆ duration │
│ --- ┆ --- ┆ --- │
│ time ┆ time ┆ duration[μs] │
╞════════════╪══════════╪══════════════╡
│ 09:00:00 ┆ 18:00:00 ┆ 1h │
│ 09:00:00 ┆ 18:00:00 ┆ 1h │
│ 09:00:00 ┆ 18:00:00 ┆ 1h │
│ 09:00:00 ┆ 18:00:00 ┆ 1h │
│ 09:00:00 ┆ 18:00:00 ┆ 1h │
│ 00:00:00 ┆ 00:00:00 ┆ 1h │
│ 00:00:00 ┆ 00:00:00 ┆ 1h │
└────────────┴──────────┴──────────────┘
def add_duration_to_time(time: pl.Expr, duration: pl.Expr) -> pl.Expr:
"""
05:00 + 1h = 06:00
23:00 + 2h = 01:00
01:00 - 2h = 23:00
"""
arbitrary_naive_date = pl.date(2025, 1, 1)
time_increased = (
arbitrary_naive_date.dt.combine(time)
+ duration
).dt.time()
return time_increased
result = (
df.with_columns(
start_time_decreased=add_duration_to_time(
time=pl.col("start_date"),
duration=pl.col("duration").neg()
),
end_time_increased=add_duration_to_time(
time=pl.col("start_date"),
duration=pl.col("duration")
),
)
)
print(result)
┌────────────┬──────────┬──────────────┬──────────────────────┬────────────────────┐
│ start_time ┆ end_time ┆ duration ┆ start_time_decreased ┆ end_time_increased │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ time ┆ time ┆ duration[μs] ┆ time ┆ time │
╞════════════╪══════════╪══════════════╪══════════════════════╪════════════════════╡
│ 09:00:00 ┆ 18:00:00 ┆ 1h ┆ 08:00:00 ┆ 10:00:00 │
│ 09:00:00 ┆ 18:00:00 ┆ 1h ┆ 08:00:00 ┆ 10:00:00 │
│ 09:00:00 ┆ 18:00:00 ┆ 1h ┆ 08:00:00 ┆ 10:00:00 │
│ 09:00:00 ┆ 18:00:00 ┆ 1h ┆ 08:00:00 ┆ 10:00:00 │
│ 09:00:00 ┆ 18:00:00 ┆ 1h ┆ 08:00:00 ┆ 10:00:00 │
│ 00:00:00 ┆ 00:00:00 ┆ 1h ┆ 23:00:00 ┆ 01:00:00 │
│ 00:00:00 ┆ 00:00:00 ┆ 1h ┆ 23:00:00 ┆ 01:00:00 │
└────────────┴──────────┴──────────────┴──────────────────────┴────────────────────┘