Skip to content

Commit 3c3236a

Browse files
committed
patch last docs/test gaps
1 parent d533587 commit 3c3236a

5 files changed

Lines changed: 43 additions & 23 deletions

File tree

.agents/skills/rust-ffi/SKILL.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ let relative_to = handle_one_kwarg("total", state.str_relative_to, kwargs)?;
114114
```
115115

116116
**Building deltas from kwargs (shift/add/subtract methods):**
117-
Use `common::shift::parse_datetime_shift_kwargs()` for full datetime units or
117+
Use `common::shift_args::parse_datetime_shift_kwargs()` for full datetime units or
118118
`parse_calendar_shift_kwargs()` for calendar-only units. They return a typed
119119
`DateTimeShift` or `CalendarShift`; the datetime parser's callback retains
120120
class-specific kwargs such as `disambiguate` and warning suppression. For a
@@ -161,8 +161,8 @@ perform Unicode comparison before later subsets have had their pointer-equality
161161
Has `.in_single_unit()` and `.in_exact_units()` for unit decomposition, and owns the pure
162162
`parse_iso()` implementation; map its parse errors to Python exceptions in the binding.
163163
- Unit types name their role: `fmt::Precision`, `round::RoundUnit`, and
164-
`CalendarUnit`/`DifferenceUnit`/`ExactUnit` in `common::math`. Keep these domains distinct unless
165-
their parsing and behavior are demonstrably identical. Keep `common::fmt` free of Python
164+
`CalendarUnit`/`DifferenceUnit`/`ExactUnit` in `domain::difference`. Keep these domains distinct
165+
unless their parsing and behavior are demonstrably identical. Keep `common::fmt` free of Python
166166
argument parsing; `common::format_args` adapts Python `format_iso` arguments to its pure types.
167167
- **ItemizedDelta/ItemizedDateDelta** use `DeltaField<T>` with the integer type's `MIN` value as
168168
the UNSET sentinel. `DeltaField` has custom `Debug` showing `<unset>` for sentinel values and

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424
`ItemizedDateDelta` values from Rust-backed operations.
2525
- Drop support for PyPy 3.10. PyPy 3.11 remains supported.
2626

27+
**Fixed**
28+
29+
- Fixed an overflow when validating extremely large rounding increments in the
30+
Rust extension. They now raise `ValueError`.
31+
2732
## 0.10.3 (2026-07-17)
2833

2934
- Fixed the pure Python implementation accepting invalid basic-format times

docs/faq.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -362,27 +362,27 @@ out [Ry](https://pypi.org/project/ry/).
362362
## Why aren't all operators supported for all types?
363363

364364
Some operators may be conspicuously missing for certain types, even
365-
though they could be implemented. For example:
365+
though they could be implemented. Whenever provides operators where their
366+
meaning is useful and can be documented clearly.
367+
368+
For example, dates and datetimes support applying itemized deltas with `+`
369+
and `-`:
366370

367371
```python
368-
>>> Date(2024, 1, 31) + ItemizedDateDelta(months=1) # Error
372+
>>> Date(2024, 1, 31) + ItemizedDateDelta(months=1)
373+
Date("2024-02-29")
369374
```
370375

371-
This is because operators are only implemented where they are *mathematically
372-
intuitive*. For example, when `a + (b + c) = (a + b) + c` and `(a + b) - b = a`.
373-
This isn't the case when working with months
374-
or years, since adding a month to January 31st gives a different result
375-
than adding a month to February 28th. To avoid confusion, these
376-
operators are simply not implemented. There are methods like
377-
`add()` and `subtract()` that can be used
378-
instead, which don't come with the same mathematical expectations:
376+
These operators use the same calendar clamping rules as `add()` and
377+
`subtract()`. As a result, adding and then subtracting the same delta is not
378+
always reversible. Itemized deltas also support `+` and `-` with each other;
379+
these perform field-wise composition and warn by default because applying the
380+
combined delta may differ from applying its parts sequentially.
379381

380-
```python
381-
>>> Date(2024, 1, 31).add(months=1)
382-
>>> Date(2024, 1, 31).add(ItemizedDateDelta(months=1))
383-
```
382+
Operators without a generally useful interpretation remain unavailable. For
383+
example, itemized deltas cannot be multiplied or divided.
384384

385-
For the same reason, the `-` operator between two datetimes always
385+
The `-` operator between two datetimes always
386386
returns a {class}`~whenever.TimeDelta`—an exact elapsed duration where
387387
subtraction is unambiguous.
388388
If you need a difference in calendar units like years, months, or days,

docs/reference/deltas.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ and their key differences. Click on the features to learn more about them.
6060
| {ref}`Convert to units <delta-in-units>` | {meth}`~TimeDelta.in_units` | {meth}`~ItemizedDateDelta.in_units` [^1] | {meth}`~ItemizedDelta.in_units` [^1] |
6161
| {ref}`Summing into one unit <delta-total>` | {meth}`~TimeDelta.total` | {meth}`~ItemizedDateDelta.total` [^1] | {meth}`~ItemizedDelta.total` [^1] |
6262
| {ref}`Comparison <delta-cmp>` | {meth}`> <TimeDelta.__gt__>` , {meth}`< <TimeDelta.__lt__>` , {meth}`>= <TimeDelta.__ge__>` , {meth}`<= <TimeDelta.__le__>` | n/a | n/a |
63-
| {ref}`Addition/subtraction <delta-add-sub>` | {meth}`~TimeDelta.add` / {meth}`~TimeDelta.subtract` | {meth}`~ItemizedDateDelta.add` / {meth}`~ItemizedDateDelta.subtract` [^1] | {meth}`~ItemizedDelta.add` / {meth}`~ItemizedDelta.subtract` [^1] |
64-
| {ref}`Operators <delta-operators>` | {meth}`+ <TimeDelta.__add__>` , {meth}`- <TimeDelta.__sub__>` , {meth}`* <TimeDelta.__mul__>` , {meth}`/ <TimeDelta.__truediv__>` , {meth}`// <TimeDelta.__floordiv__>` , {meth}`% <TimeDelta.__mod__>` | n/a | n/a |
63+
| {ref}`Addition/subtraction <delta-add-sub>` | {meth}`~TimeDelta.add` / {meth}`~TimeDelta.subtract` | {meth}`~ItemizedDateDelta.add` / {meth}`~ItemizedDateDelta.subtract` | {meth}`~ItemizedDelta.add` / {meth}`~ItemizedDelta.subtract` |
64+
| {ref}`Operators <delta-operators>` | {meth}`+ <TimeDelta.__add__>` , {meth}`- <TimeDelta.__sub__>` , {meth}`* <TimeDelta.__mul__>` , {meth}`/ <TimeDelta.__truediv__>` , {meth}`// <TimeDelta.__floordiv__>` , {meth}`% <TimeDelta.__mod__>` | `+`, `-` | `+`, `-` |
6565
| {ref}`Rounding <delta-rounding>` | {meth}`~TimeDelta.round` | with {meth}`~ItemizedDateDelta.in_units` | with {meth}`~ItemizedDelta.in_units` |
6666
| Applies to... | {class}`ZonedDateTime` <br> {class}`OffsetDateTime` <br> {class}`PlainDateTime` <br> {class}`Instant` | {class}`ZonedDateTime` <br> {class}`OffsetDateTime` <br> {class}`PlainDateTime` <br> {class}`Date` | {class}`ZonedDateTime` <br> {class}`OffsetDateTime` <br> {class}`PlainDateTime` |
6767
| Similar to... | {class}`~datetime.timedelta` | {class}`~collections.Counter` | {class}`~collections.Counter` |
@@ -282,10 +282,10 @@ of the two deltas:
282282
TimeDelta("PT3h30m")
283283
```
284284

285-
"Itemized" deltas do require a relative date or datetime context
285+
"Itemized" delta composition can use a relative date or datetime context
286286
to resolve calendar units when adding or subtracting.
287-
For example, adding "1 month" to "30 days" requires knowing the starting date
288-
to determine the resulting duration:
287+
For example, the calendar-aware composition of "1 month" and "30 days"
288+
depends on the starting date:
289289

290290
```python
291291
>>> one_month = ItemizedDateDelta(months=1)

tests/test_plain_datetime.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1714,6 +1714,21 @@ def test_century_leap(self):
17141714

17151715

17161716
class TestStartOf:
1717+
@pytest.mark.parametrize(
1718+
("unit", "expected"),
1719+
[
1720+
("year", PlainDateTime(2024, 1, 1)),
1721+
("day", PlainDateTime(2024, 8, 15)),
1722+
("hour", PlainDateTime(2024, 8, 15, 14)),
1723+
],
1724+
)
1725+
def test_str_subclass(self, unit, expected):
1726+
class StrSubclass(str):
1727+
pass
1728+
1729+
dt = PlainDateTime(2024, 8, 15, 14, 30, 45, nanosecond=123)
1730+
assert dt.start_of(StrSubclass(unit)) == expected # type: ignore[arg-type]
1731+
17171732
def test_year(self):
17181733
dt = PlainDateTime(2024, 8, 15, 14, 30, 45, nanosecond=123)
17191734
result = dt.start_of("year")

0 commit comments

Comments
 (0)