From 8fd442868ba2616900e46558aa6770743fb41d79 Mon Sep 17 00:00:00 2001 From: Daniil Poletaev <44584010+danpoletaev@users.noreply.github.com> Date: Wed, 8 Oct 2025 16:16:48 +0200 Subject: [PATCH 1/2] feat: add started_before and started_after to run list --- src/apify_client/_http_client.py | 8 ++++++++ .../resource_clients/run_collection.py | 14 ++++++++++++++ tests/integration/test_run_collection.py | 19 +++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/src/apify_client/_http_client.py b/src/apify_client/_http_client.py index aa4d0e6c..d244e81b 100644 --- a/src/apify_client/_http_client.py +++ b/src/apify_client/_http_client.py @@ -5,6 +5,7 @@ import logging import os import sys +from datetime import datetime, timezone from http import HTTPStatus from importlib import metadata from typing import TYPE_CHECKING, Any @@ -76,6 +77,13 @@ def _parse_params(params: dict | None) -> dict | None: # Our API needs lists passed as comma-separated strings elif isinstance(value, list): parsed_params[key] = ','.join(value) + elif isinstance(value, datetime): + utc_aware_dt = value.replace(tzinfo=timezone.utc) + + # Convert to ISO 8601 string in Zulu format + zulu_date_str = utc_aware_dt.strftime('%Y-%m-%dT%H:%M:%SZ') + + parsed_params[key] = zulu_date_str elif value is not None: parsed_params[key] = value diff --git a/src/apify_client/clients/resource_clients/run_collection.py b/src/apify_client/clients/resource_clients/run_collection.py index 924daf37..77c5bc38 100644 --- a/src/apify_client/clients/resource_clients/run_collection.py +++ b/src/apify_client/clients/resource_clients/run_collection.py @@ -6,6 +6,8 @@ from apify_client.clients.base import ResourceCollectionClient, ResourceCollectionClientAsync if TYPE_CHECKING: + from datetime import datetime + from apify_shared.consts import ActorJobStatus from apify_client.clients.base.resource_collection_client import ListPage @@ -25,6 +27,8 @@ def list( offset: int | None = None, desc: bool | None = None, status: ActorJobStatus | list[ActorJobStatus] | None = None, + started_before: str | datetime | None = None, + started_after: str | datetime | None = None, ) -> ListPage[dict]: """List all Actor runs. @@ -39,6 +43,8 @@ def list( offset: What run to include as first when retrieving the list. desc: Whether to sort the runs in descending order based on their start date. status: Retrieve only runs with the provided statuses. + started_before: Only return runs started before this date (inclusive). + started_after: Only return runs started after this date (inclusive). Returns: The retrieved Actor runs. @@ -53,6 +59,8 @@ def list( offset=offset, desc=desc, status=status_param, + startedBefore=started_before, + startedAfter=started_after, ) @@ -70,6 +78,8 @@ async def list( offset: int | None = None, desc: bool | None = None, status: ActorJobStatus | list[ActorJobStatus] | None = None, + started_before: str | datetime | None = None, + started_after: str | datetime | None = None, ) -> ListPage[dict]: """List all Actor runs. @@ -84,6 +94,8 @@ async def list( offset: What run to include as first when retrieving the list. desc: Whether to sort the runs in descending order based on their start date. status: Retrieve only runs with the provided statuses. + started_before: Only return runs started before this date (inclusive). + started_after: Only return runs started after this date (inclusive). Returns: The retrieved Actor runs. @@ -98,4 +110,6 @@ async def list( offset=offset, desc=desc, status=status_param, + startedBefore=started_before, + startedAfter=started_after, ) diff --git a/tests/integration/test_run_collection.py b/tests/integration/test_run_collection.py index 3d1be6cd..f01ca2d8 100644 --- a/tests/integration/test_run_collection.py +++ b/tests/integration/test_run_collection.py @@ -1,5 +1,6 @@ from __future__ import annotations +from datetime import datetime, timezone from typing import TYPE_CHECKING import pytest @@ -54,3 +55,21 @@ async def test_run_collection_list_multiple_statuses(self, apify_client: ApifyCl assert all(run.get('status') == ActorJobStatus.SUCCEEDED for run in single_status_runs.items) self.teadown_runs(apify_client) + + # Here we test that date fields can be passed both as datetime objects and as ISO 8601 strings + async def test_run_collection_list_accept_date_range(self, apify_client: ApifyClient) -> None: + self.setup_runs(apify_client) + + run_collection = apify_client.runs() + + date_obj = datetime(2100, 1, 1, 0, 0, 0, tzinfo=timezone.utc) + iso_date_str = date_obj.strftime('%Y-%m-%dT%H:%M:%SZ') + + # Here we test that date fields can be passed both as datetime objects and as ISO 8601 strings + runs_in_range_date_format = run_collection.list(started_before=date_obj, started_after=date_obj) + runs_in_range_string_format = run_collection.list(started_before=iso_date_str, started_after=iso_date_str) + + assert hasattr(runs_in_range_date_format, 'items') + assert hasattr(runs_in_range_string_format, 'items') + + self.teadown_runs(apify_client) From dede7b6db62cda21503c8632e9f158b956bb30e2 Mon Sep 17 00:00:00 2001 From: Daniil Poletaev <44584010+danpoletaev@users.noreply.github.com> Date: Thu, 9 Oct 2025 13:33:08 +0200 Subject: [PATCH 2/2] fix: improve date conversion to UTC --- src/apify_client/_http_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apify_client/_http_client.py b/src/apify_client/_http_client.py index d244e81b..cf166059 100644 --- a/src/apify_client/_http_client.py +++ b/src/apify_client/_http_client.py @@ -78,7 +78,7 @@ def _parse_params(params: dict | None) -> dict | None: elif isinstance(value, list): parsed_params[key] = ','.join(value) elif isinstance(value, datetime): - utc_aware_dt = value.replace(tzinfo=timezone.utc) + utc_aware_dt = value.astimezone(timezone.utc) # Convert to ISO 8601 string in Zulu format zulu_date_str = utc_aware_dt.strftime('%Y-%m-%dT%H:%M:%SZ')