diff --git a/datapi/catalogue.py b/datapi/catalogue.py index c6184e8..48fbd79 100644 --- a/datapi/catalogue.py +++ b/datapi/catalogue.py @@ -23,7 +23,7 @@ import datapi -from . import config +from . import config, utils from .processing import ApiResponse, ApiResponsePaginated, RequestKwargs @@ -46,28 +46,24 @@ def begin_datetime(self) -> datetime.datetime | None: """Begin datetime of the collection.""" if (value := self._json_dict["extent"]["temporal"]["interval"][0][0]) is None: return value - return datetime.datetime.fromisoformat(value.replace("Z", "+00:00")) + return utils.string_to_datetime(value) @property def end_datetime(self) -> datetime.datetime | None: """End datetime of the collection.""" if (value := self._json_dict["extent"]["temporal"]["interval"][0][1]) is None: return value - return datetime.datetime.fromisoformat(value.replace("Z", "+00:00")) + return utils.string_to_datetime(value) @property def published_at(self) -> datetime.datetime: """When the collection was first published.""" - return datetime.datetime.fromisoformat( - self._json_dict["published"].replace("Z", "+00:00") - ) + return utils.string_to_datetime(self._json_dict["published"]) @property def updated_at(self) -> datetime.datetime: """When the collection was last updated.""" - return datetime.datetime.fromisoformat( - self._json_dict["updated"].replace("Z", "+00:00") - ) + return utils.string_to_datetime(self._json_dict["updated"]) @property def title(self) -> str: diff --git a/datapi/processing.py b/datapi/processing.py index 6c0d669..9b99839 100644 --- a/datapi/processing.py +++ b/datapi/processing.py @@ -34,7 +34,7 @@ import datapi -from . import config +from . import config, utils T_ApiResponse = TypeVar("T_ApiResponse", bound="ApiResponse") @@ -444,12 +444,12 @@ def status(self) -> str: @property def updated_at(self) -> datetime.datetime: """When the job was last updated.""" - return datetime.datetime.fromisoformat(self.json["updated"]) + return utils.string_to_datetime(self.json["updated"]) @property def created_at(self) -> datetime.datetime: """When the job was created.""" - return datetime.datetime.fromisoformat(self.json["created"]) + return utils.string_to_datetime(self.json["created"]) @property def creation_datetime(self) -> datetime.datetime: @@ -465,7 +465,7 @@ def creation_datetime(self) -> datetime.datetime: def started_at(self) -> datetime.datetime | None: """When the job started. If None, the job has not started.""" value = self.json.get("started") - return value if value is None else datetime.datetime.fromisoformat(value) + return value if value is None else utils.string_to_datetime(value) @property def start_datetime(self) -> datetime.datetime | None: @@ -481,7 +481,7 @@ def start_datetime(self) -> datetime.datetime | None: def finished_at(self) -> datetime.datetime | None: """When the job finished. If None, the job has not finished.""" value = self.json.get("finished") - return value if value is None else datetime.datetime.fromisoformat(value) + return value if value is None else utils.string_to_datetime(value) @property def end_datetime(self) -> datetime.datetime | None: diff --git a/datapi/utils.py b/datapi/utils.py new file mode 100644 index 0000000..8b4ce46 --- /dev/null +++ b/datapi/utils.py @@ -0,0 +1,9 @@ +import datetime + + +def string_to_datetime(string: str) -> datetime.datetime: + string = string.replace("Z", "+00:00") # Support python<3.11 + date_time = datetime.datetime.fromisoformat(string) + if date_time.tzinfo is None: + date_time = date_time.replace(tzinfo=datetime.timezone.utc) + return date_time diff --git a/tests/test_20_processing.py b/tests/test_20_processing.py index 1ed1129..93aef35 100644 --- a/tests/test_20_processing.py +++ b/tests/test_20_processing.py @@ -381,12 +381,12 @@ def test_submit(cat: catalogue.Catalogue) -> None: assert remote.status == "successful" assert remote.results_ready is True - assert remote.created_at.isoformat() == "2022-09-02T17:30:48.201213" - assert remote.updated_at.isoformat() == "2022-09-02T17:32:54.308116" + assert remote.created_at.isoformat() == "2022-09-02T17:30:48.201213+00:00" + assert remote.updated_at.isoformat() == "2022-09-02T17:32:54.308116+00:00" assert remote.started_at is not None - assert remote.started_at.isoformat() == "2022-09-02T17:32:43.890617" + assert remote.started_at.isoformat() == "2022-09-02T17:32:43.890617+00:00" assert remote.finished_at is not None - assert remote.finished_at.isoformat() == "2022-09-02T17:32:54.308120" + assert remote.finished_at.isoformat() == "2022-09-02T17:32:54.308120+00:00" @responses.activate @@ -403,15 +403,17 @@ def test_depracations(cat: catalogue.Catalogue) -> None: with pytest.warns( DeprecationWarning, match="`creation_datetime` has been deprecated" ): - assert remote.creation_datetime.isoformat() == "2022-09-02T17:30:48.201213" + assert ( + remote.creation_datetime.isoformat() == "2022-09-02T17:30:48.201213+00:00" + ) with pytest.warns(DeprecationWarning, match="`start_datetime` has been deprecated"): assert remote.start_datetime is not None - assert remote.start_datetime.isoformat() == "2022-09-02T17:32:43.890617" + assert remote.start_datetime.isoformat() == "2022-09-02T17:32:43.890617+00:00" with pytest.warns(DeprecationWarning, match="`end_datetime` has been deprecated"): assert remote.end_datetime is not None - assert remote.end_datetime.isoformat() == "2022-09-02T17:32:54.308120" + assert remote.end_datetime.isoformat() == "2022-09-02T17:32:54.308120+00:00" @responses.activate @@ -449,11 +451,11 @@ def test_wait_on_result_failed(cat: catalogue.Catalogue) -> None: ): remote._wait_on_results() - assert remote.created_at.isoformat() == "2022-09-02T17:30:48.201213" + assert remote.created_at.isoformat() == "2022-09-02T17:30:48.201213+00:00" assert remote.started_at is not None - assert remote.started_at.isoformat() == "2022-09-02T17:32:43.890617" + assert remote.started_at.isoformat() == "2022-09-02T17:32:43.890617+00:00" assert remote.finished_at is not None - assert remote.finished_at.isoformat() == "2022-09-02T17:32:54.308120" + assert remote.finished_at.isoformat() == "2022-09-02T17:32:54.308120+00:00" @responses.activate