5

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?

1

1 Answer 1

3

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           │
    └────────────┴──────────┴──────────────┴──────────────────────┴────────────────────┘
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.