diff --git a/cognite/client/_api/simulators/models_revisions.py b/cognite/client/_api/simulators/models_revisions.py index 5c6e3ae479..51164e8042 100644 --- a/cognite/client/_api/simulators/models_revisions.py +++ b/cognite/client/_api/simulators/models_revisions.py @@ -73,8 +73,8 @@ async def list( >>> res = client.simulators.models.revisions.list( ... model_external_ids=["model1", "model2"], ... all_versions=True, - ... created_time=TimestampRange(min=0, max=1000000), - ... last_updated_time=TimestampRange(min=0, max=1000000), + ... created_time=TimestampRange(min="1d-ago", max="now"), + ... last_updated_time=TimestampRange(min="1d-ago", max="now"), ... sort=PropertySort(order="asc", property="createdTime"), ... limit=10 ... ) diff --git a/cognite/client/_api/simulators/runs.py b/cognite/client/_api/simulators/runs.py index 57e6309616..c9086fd5ca 100644 --- a/cognite/client/_api/simulators/runs.py +++ b/cognite/client/_api/simulators/runs.py @@ -196,8 +196,8 @@ async def list( Filter runs by time ranges: >>> from cognite.client.data_classes.shared import TimestampRange >>> res = client.simulators.runs.list( - ... created_time=TimestampRange(min=0, max=1_700_000_000_000), - ... simulation_time=TimestampRange(min=0, max=1_700_000_000_000), + ... created_time=TimestampRange(min="1d-ago", max="now"), + ... simulation_time=TimestampRange(min="1d-ago", max="now"), ... ) """ diff --git a/cognite/client/_sync_api/simulators/models_revisions.py b/cognite/client/_sync_api/simulators/models_revisions.py index 09593107c8..a7d9b0c47e 100644 --- a/cognite/client/_sync_api/simulators/models_revisions.py +++ b/cognite/client/_sync_api/simulators/models_revisions.py @@ -1,6 +1,6 @@ """ =============================================================================== -1d02275dfbb16469b08dc17b88cc9d14 +13a7de2b1a61099448b86ddf1135519c This file is auto-generated from the Async API modules, - do not edit manually! =============================================================================== """ @@ -72,8 +72,8 @@ def list( >>> res = client.simulators.models.revisions.list( ... model_external_ids=["model1", "model2"], ... all_versions=True, - ... created_time=TimestampRange(min=0, max=1000000), - ... last_updated_time=TimestampRange(min=0, max=1000000), + ... created_time=TimestampRange(min="1d-ago", max="now"), + ... last_updated_time=TimestampRange(min="1d-ago", max="now"), ... sort=PropertySort(order="asc", property="createdTime"), ... limit=10 ... ) diff --git a/cognite/client/_sync_api/simulators/runs.py b/cognite/client/_sync_api/simulators/runs.py index 5f687a8491..247f5aca46 100644 --- a/cognite/client/_sync_api/simulators/runs.py +++ b/cognite/client/_sync_api/simulators/runs.py @@ -1,6 +1,6 @@ """ =============================================================================== -627f42fbdbc933e30799270aba58ef0d +8368bdd19f65023e757c7ab61239919c This file is auto-generated from the Async API modules, - do not edit manually! =============================================================================== """ @@ -184,8 +184,8 @@ def list( Filter runs by time ranges: >>> from cognite.client.data_classes.shared import TimestampRange >>> res = client.simulators.runs.list( - ... created_time=TimestampRange(min=0, max=1_700_000_000_000), - ... simulation_time=TimestampRange(min=0, max=1_700_000_000_000), + ... created_time=TimestampRange(min="1d-ago", max="now"), + ... simulation_time=TimestampRange(min="1d-ago", max="now"), ... ) """ return run_sync( diff --git a/cognite/client/data_classes/shared.py b/cognite/client/data_classes/shared.py index c3de2032a4..ca40ef07d8 100644 --- a/cognite/client/data_classes/shared.py +++ b/cognite/client/data_classes/shared.py @@ -1,25 +1,29 @@ from __future__ import annotations from collections.abc import Collection, Sequence +from datetime import datetime from typing import Any, Literal from typing_extensions import Self from cognite.client.data_classes._base import CogniteFilter, CogniteResource, UnknownCogniteResource +from cognite.client.utils._time import timestamp_to_ms class TimestampRange(CogniteResource): """Range between two timestamps. Args: - max (int | None): The number of milliseconds since 00:00:00 Thursday, 1 January 1970, Coordinated Universal Time (UTC), minus leap seconds. - min (int | None): The number of milliseconds since 00:00:00 Thursday, 1 January 1970, Coordinated Universal Time (UTC), minus leap seconds. + max (int | float | str | datetime | None): The number of milliseconds since 00:00:00 Thursday, 1 January 1970, Coordinated Universal Time (UTC), minus leap seconds, a string in time-shift format or a datetime object. + min (int | float | str | datetime | None): The number of milliseconds since 00:00:00 Thursday, 1 January 1970, Coordinated Universal Time (UTC), minus leap seconds, a string in time-shift format or a datetime object. **_ (Any): No description. """ - def __init__(self, max: int | None = None, min: int | None = None, **_: Any) -> None: - self.max = max - self.min = min + def __init__( + self, max: int | float | str | datetime | None = None, min: int | float | str | datetime | None = None, **_: Any + ) -> None: + self.max = timestamp_to_ms(max) if max is not None else None + self.min = timestamp_to_ms(min) if min is not None else None @classmethod def _load(cls, resource: dict[str, Any]) -> Self: diff --git a/poetry.lock b/poetry.lock index 27e7ba3eb9..054c2de432 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3330,14 +3330,14 @@ zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] [[package]] name = "virtualenv" -version = "20.37.0" +version = "20.39.0" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "virtualenv-20.37.0-py3-none-any.whl", hash = "sha256:5d3951c32d57232ae3569d4de4cc256c439e045135ebf43518131175d9be435d"}, - {file = "virtualenv-20.37.0.tar.gz", hash = "sha256:6f7e2064ed470aa7418874e70b6369d53b66bcd9e9fd5389763e96b6c94ccb7c"}, + {file = "virtualenv-20.39.0-py3-none-any.whl", hash = "sha256:44888bba3775990a152ea1f73f8e5f566d49f11bbd1de61d426fd7732770043e"}, + {file = "virtualenv-20.39.0.tar.gz", hash = "sha256:a15f0cebd00d50074fd336a169d53422436a12dfe15149efec7072cfe817df8b"}, ] [package.dependencies] @@ -3346,10 +3346,6 @@ filelock = {version = ">=3.24.2,<4", markers = "python_version >= \"3.10\""} platformdirs = ">=3.9.1,<5" typing-extensions = {version = ">=4.13.2", markers = "python_version < \"3.11\""} -[package.extras] -docs = ["furo (>=2023.7.26)", "pre-commit-uv (>=4.1.4)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinx-autodoc-typehints (>=3.6.2)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2025.12.21.14)", "sphinxcontrib-mermaid (>=2)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "pytest-xdist (>=3.5)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] - [[package]] name = "wcwidth" version = "0.6.0" diff --git a/tests/tests_unit/test_api/test_data_sets.py b/tests/tests_unit/test_api/test_data_sets.py index dd18a2c1ab..e97ea4b068 100644 --- a/tests/tests_unit/test_api/test_data_sets.py +++ b/tests/tests_unit/test_api/test_data_sets.py @@ -1,6 +1,7 @@ from __future__ import annotations import re +from datetime import datetime, timezone from typing import TYPE_CHECKING, Any import pytest @@ -65,8 +66,11 @@ def test_retrieve_multiple( assert isinstance(res, DataSetList) assert [example_data_set] == res.dump(camel_case=True) - def test_list_with_timestamp_range(self, cognite_client: CogniteClient, mock_ds_response: HTTPXMock) -> None: - cognite_client.data_sets.list(created_time=TimestampRange(min=20)) + @pytest.mark.parametrize("min_time", [20, datetime.fromtimestamp(20 / 1000, timezone.utc)]) + def test_list_with_timestamp_range( + self, cognite_client: CogniteClient, mock_ds_response: HTTPXMock, min_time: int | datetime + ) -> None: + cognite_client.data_sets.list(created_time=TimestampRange(min=min_time)) assert 20 == jsgz_load(mock_ds_response.get_requests()[0].content)["filter"]["createdTime"]["min"] assert "max" not in jsgz_load(mock_ds_response.get_requests()[0].content)["filter"]["createdTime"] diff --git a/tests/tests_unit/test_data_classes/test_timestamp_range.py b/tests/tests_unit/test_data_classes/test_timestamp_range.py index 907af6f5b8..7dd03522e9 100644 --- a/tests/tests_unit/test_data_classes/test_timestamp_range.py +++ b/tests/tests_unit/test_data_classes/test_timestamp_range.py @@ -1,5 +1,9 @@ from __future__ import annotations +from datetime import datetime, timedelta, timezone + +import pytest + from cognite.client.data_classes import AggregateResultItem, TimestampRange @@ -27,3 +31,19 @@ def test_camels(self) -> None: ag = AggregateResultItem(child_count=23, depth=1, path=[]) assert 23 == ag.child_count assert {"childCount": 23, "depth": 1, "path": []} == ag.dump(camel_case=True) + + def test_datetime(self) -> None: + min_time = datetime.fromtimestamp(1767222000, timezone.utc) # 2026-01-01 00:00:00 GMT+01 + max_time = datetime.fromtimestamp(1767308400, timezone.utc) # 2026-01-02 00:00:00 GMT+01 + tsr = TimestampRange(min=min_time, max=max_time) + assert tsr.min == 1767222000000 + assert tsr.max == 1767308400000 + assert {"min": 1767222000000, "max": 1767308400000} == tsr.dump() + + def test_time_shift_string(self) -> None: + now_time = int(datetime.now(timezone.utc).timestamp() * 1000) + day_ago_time = now_time - timedelta(days=1) // timedelta(milliseconds=1) + tsr = TimestampRange(min="1d-ago", max="now") + # NOTE: Using approx because of the small time difference between calls + assert tsr.min == pytest.approx(day_ago_time, 100) + assert tsr.max == pytest.approx(now_time, 100)