Skip to content

Commit 3b2d167

Browse files
committed
make DatetimeInterval RFC 3339 compliant
1 parent d46641e commit 3b2d167

2 files changed

Lines changed: 27 additions & 11 deletions

File tree

stapi-pydantic/src/stapi_pydantic/datetime_interval.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,32 +10,37 @@
1010
WrapSerializer,
1111
)
1212

13+
_DatetimeTuple = tuple[datetime | None, datetime | None]
1314

14-
def validate_before(
15-
value: str | tuple[datetime, datetime],
16-
) -> tuple[datetime, datetime]:
15+
16+
def validate_before(value: str | _DatetimeTuple) -> _DatetimeTuple:
1717
if isinstance(value, str):
18-
start, end = value.split("/", 1)
19-
return (datetime.fromisoformat(start), datetime.fromisoformat(end))
18+
start_str, end_str = value.split("/", 1)
19+
start = None if start_str == ".." else datetime.fromisoformat(start_str)
20+
end = None if end_str == ".." else datetime.fromisoformat(end_str)
21+
value = (start, end)
2022
return value
2123

2224

23-
def validate_after(value: tuple[datetime, datetime]) -> tuple[datetime, datetime]:
24-
if value[1] < value[0]:
25+
def validate_after(value: _DatetimeTuple) -> _DatetimeTuple:
26+
# None/date & date/None are always valid
27+
if value[1] and value[0] and value[1] < value[0]:
2528
raise ValueError("end before start")
2629
return value
2730

2831

2932
def serialize(
30-
value: tuple[datetime, datetime],
31-
serializer: Callable[[tuple[datetime, datetime]], tuple[str, str]],
33+
value: _DatetimeTuple,
34+
serializer: Callable[[_DatetimeTuple], tuple[str, str]],
3235
) -> str:
3336
del serializer # unused
34-
return f"{value[0].isoformat()}/{value[1].isoformat()}"
37+
start = value[0].isoformat() if value[0] else ".."
38+
end = value[1].isoformat() if value[1] else ".."
39+
return f"{start}/{end}"
3540

3641

3742
DatetimeInterval = Annotated[
38-
tuple[AwareDatetime, AwareDatetime],
43+
tuple[AwareDatetime | None, AwareDatetime | None],
3944
BeforeValidator(validate_before),
4045
AfterValidator(validate_after),
4146
WrapSerializer(serialize, return_type=str),
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from stapi_pydantic import DatetimeInterval
2+
3+
4+
def test_datetime_interval() -> None:
5+
"""Test the datetime interval validator."""
6+
dt1 = DatetimeInterval.__metadata__[0].func("2025-04-01T00:00:00Z/2025-04-01T23:59:59Z")
7+
dt2 = DatetimeInterval.__metadata__[0].func("2025-04-01T00:00:00Z/..")
8+
dt3 = DatetimeInterval.__metadata__[0].func("../2025-04-01T23:59:59Z")
9+
_ = DatetimeInterval.__metadata__[1].func(dt1)
10+
_ = DatetimeInterval.__metadata__[1].func(dt2)
11+
_ = DatetimeInterval.__metadata__[1].func(dt3)

0 commit comments

Comments
 (0)