Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/docs/addition_subtraction.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,16 @@ Each method returns a new `DateTime` instance.

Passing negative values to `add()` is also possible and will act exactly
like `subtract()`

## Time arithmetic

`Time` instances support adding a `datetime.timedelta` on either side of `+`.
As with `Time.add_timedelta()`, the duration must not have a nonzero `days` component.

```python
>>> from datetime import timedelta
>>> pendulum.time(12, 30) + timedelta(minutes=15)
Time(12, 45, 0)
>>> timedelta(minutes=15) + pendulum.time(12, 30)
Time(12, 45, 0)
```
3 changes: 3 additions & 0 deletions src/pendulum/time.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ def __add__(self, other: datetime.timedelta) -> Time:

return self.add_timedelta(other)

def __radd__(self, other: datetime.timedelta) -> Time:
return self.__add__(other)

@overload
def __sub__(self, other: time) -> pendulum.Duration: ...

Expand Down
50 changes: 50 additions & 0 deletions tests/time/test_add.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,56 @@ def test_add_timedelta_with_days():
d.add_timedelta(delta)


@pytest.mark.parametrize("duration_type", [timedelta, pendulum.duration])
@pytest.mark.parametrize(
"time_args, duration_args, expected_args",
[
(
(3, 12, 15, 654321),
{"seconds": 45, "microseconds": 123456},
(3, 13, 0, 777777),
),
((12, 0), {}, (12, 0)),
((23, 59, 59, 999999), {"microseconds": 1}, (0, 0)),
((12, 0), {"hours": 23}, (11, 0)),
],
)
def test_reflected_addition(duration_type, time_args, duration_args, expected_args):
value = pendulum.time(*time_args)
delta = duration_type(**duration_args)
expected = pendulum.time(*expected_args)

assert value + delta == expected
result = delta + value
assert result == expected
assert isinstance(result, pendulum.Time)


@pytest.mark.parametrize("days", [-1, 1])
def test_reflected_addition_rejects_days(days):
value = pendulum.time(12, 0)
delta = timedelta(days=days)

with pytest.raises(TypeError):
value + delta
with pytest.raises(TypeError):
delta + value


def test_reflected_addition_delegates_to_subclass():
calls = []
expected = pendulum.time(6, 0)

class CustomTime(pendulum.Time):
def __add__(self, other):
calls.append(other)
return expected

delta = timedelta(minutes=5)
assert delta + CustomTime(12, 0) is expected
assert calls == [delta]


def test_addition_invalid_type():
d = pendulum.time(3, 12, 15, 654321)

Expand Down
Loading