diff --git a/CHANGES.md b/CHANGES.md index 07a80ad05..f4222417d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,7 @@ - Return a `Location` header with the URI of the newly created resource for `POST /collections/{collection_id}/items` (single `Item`) and `POST /collections` in the Transaction extension, as required by the spec. `ItemCollection` payloads still return 201 without a `Location` header. The header is also added when a client returns a `Response` object directly, unless it already sets `Location` or returns an error status. ([#982](https://github.com/stac-utils/stac-fastapi/pull/982)) - Type `PartialCollection.links` as a list so `PATCH /collections/{collection_id}` merge patches carrying a `links` array are no longer rejected with 400. - Declare `ItemCollection` as a valid response of `POST /collections/{collection_id}/items` (route response model and `create_item` client return type) so a successful ItemCollection create returns 201 instead of 500 when `enable_response_models` is on. +- Return a stable `Invalid RFC3339 datetime.` error detail from `str_to_interval` for out-of-range datetime values instead of the Python-version-dependent message. ([#991](https://github.com/stac-utils/stac-fastapi/pull/991)) ## [7.0.0] - 2026-09-09 diff --git a/stac_fastapi/types/stac_fastapi/types/rfc3339.py b/stac_fastapi/types/stac_fastapi/types/rfc3339.py index f69ad3d87..ceba09a65 100644 --- a/stac_fastapi/types/stac_fastapi/types/rfc3339.py +++ b/stac_fastapi/types/stac_fastapi/types/rfc3339.py @@ -67,7 +67,12 @@ def rfc3339_str_to_datetime(s: str) -> datetime: raise ValueError("Invalid RFC3339 datetime.") # Parse with pyiso8601 - return iso8601.parse_date(s) + try: + return iso8601.parse_date(s) + except iso8601.ParseError as e: + # pyiso8601 forwards the Python-version-dependent datetime error message, + # so the public message is fixed and the original error stays in the chain + raise ValueError("Invalid RFC3339 datetime.") from e def parse_single_date(date_str: str) -> datetime: diff --git a/stac_fastapi/types/tests/test_rfc3339.py b/stac_fastapi/types/tests/test_rfc3339.py index 3b1ea2c5d..396c0d6aa 100644 --- a/stac_fastapi/types/tests/test_rfc3339.py +++ b/stac_fastapi/types/tests/test_rfc3339.py @@ -103,6 +103,20 @@ def test_str_to_interval_with_invalid_datetime(test_input): ) +@pytest.mark.parametrize( + "test_input", + [ + "2020-13-01T00:00:00Z", # month out of range + "2020-01-01T25:00:00Z", # hour out of range + "2020-01-01T00:00:00Z/2020-01-32T00:00:00Z", # day out of range in interval + ], +) +def test_str_to_interval_out_of_range_detail_is_stable(test_input): + with pytest.raises(HTTPException) as exc_info: + str_to_interval(test_input) + assert exc_info.value.detail == "Invalid RFC3339 datetime." + + @pytest.mark.parametrize("test_input", valid_intervals) def test_str_to_interval_with_valid_interval(test_input): assert isinstance(str_to_interval(test_input), tuple), (