diff --git a/pyproject.toml b/pyproject.toml index eb64b915..44c29bc3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -130,9 +130,6 @@ ignore = [ # Typing migration requires runtime annotation compatibility review: # https://github.com/microsoft/durabletask-python/issues/322 "UP006", "UP007", "UP035", "UP037", "UP040", "UP043", "UP045", "UP047", - # Repository-wide UTC alias cleanup: - # https://github.com/microsoft/durabletask-python/issues/323 - "UP017", ] [tool.ruff.lint.per-file-ignores] diff --git a/tests/azure-functions-durable/e2e/apps/dtask_style/client_routes.py b/tests/azure-functions-durable/e2e/apps/dtask_style/client_routes.py index 61e06e10..1befc975 100644 --- a/tests/azure-functions-durable/e2e/apps/dtask_style/client_routes.py +++ b/tests/azure-functions-durable/e2e/apps/dtask_style/client_routes.py @@ -16,7 +16,7 @@ import asyncio import json import logging -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from typing import Any import azure.functions as func @@ -246,7 +246,7 @@ async def signal_entity( signal_time = None delay_seconds = body.get("delay_seconds") if delay_seconds: - signal_time = datetime.now(timezone.utc) + timedelta(seconds=float(delay_seconds)) + signal_time = datetime.now(UTC) + timedelta(seconds=float(delay_seconds)) await client.signal_entity( entity_id, req.route_params["op"], input=body.get("input"), signal_time=signal_time) return func.HttpResponse(status_code=202) diff --git a/tests/azure-functions-durable/e2e/apps/dtask_style/history_export_routes.py b/tests/azure-functions-durable/e2e/apps/dtask_style/history_export_routes.py index eeedd316..a277260a 100644 --- a/tests/azure-functions-durable/e2e/apps/dtask_style/history_export_routes.py +++ b/tests/azure-functions-durable/e2e/apps/dtask_style/history_export_routes.py @@ -20,7 +20,7 @@ import json import tempfile from collections.abc import Mapping -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from pathlib import Path from typing import Any, Optional @@ -111,7 +111,7 @@ async def start_export( req: func.HttpRequest, client: df.DurableFunctionsClient) -> func.HttpResponse: body = req.get_json() or {} export = _export_client(client) - now = datetime.now(timezone.utc) + now = datetime.now(UTC) # Callers can narrow the completed-time window so an export only covers the # instances they just produced (rather than every terminal instance in the # last hour, which would make the fan-out unbounded). diff --git a/tests/azure-functions-durable/e2e/test_dtask_history_export_e2e.py b/tests/azure-functions-durable/e2e/test_dtask_history_export_e2e.py index 808d94b3..51cadb04 100644 --- a/tests/azure-functions-durable/e2e/test_dtask_history_export_e2e.py +++ b/tests/azure-functions-durable/e2e/test_dtask_history_export_e2e.py @@ -11,7 +11,7 @@ """ import time -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from pathlib import Path import pytest @@ -23,7 +23,7 @@ def test_history_export_batch(dtask_app): # Scope the export to instances completed from just before this test, so the # fan-out only covers the instances we create here (not every terminal # instance produced by the wider session). - completed_from = (datetime.now(timezone.utc) - timedelta(seconds=5)).isoformat() + completed_from = (datetime.now(UTC) - timedelta(seconds=5)).isoformat() # Produce a couple of terminal orchestrations to export. instance_ids = [] diff --git a/tests/azure-functions-durable/e2e/test_v1_client_e2e.py b/tests/azure-functions-durable/e2e/test_v1_client_e2e.py index 9debba38..8cfce488 100644 --- a/tests/azure-functions-durable/e2e/test_v1_client_e2e.py +++ b/tests/azure-functions-durable/e2e/test_v1_client_e2e.py @@ -9,7 +9,7 @@ ``wait_for_completion_or_create_check_status_response``, and ``rewind``. """ -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta import pytest @@ -67,7 +67,7 @@ def test_get_status_by_runtime_status(v1_app): def test_purge_instance_history_by(v1_app): - created_from = (datetime.now(timezone.utc) - timedelta(minutes=1)).isoformat() + created_from = (datetime.now(UTC) - timedelta(minutes=1)).isoformat() instance_id = v1_app.start_orchestration("activity_chain") v1_app.wait_for_completion(instance_id) diff --git a/tests/azure-functions-durable/test_client_compat.py b/tests/azure-functions-durable/test_client_compat.py index d04d0e5e..9d81cf00 100644 --- a/tests/azure-functions-durable/test_client_compat.py +++ b/tests/azure-functions-durable/test_client_compat.py @@ -3,7 +3,7 @@ import json import pickle -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from types import SimpleNamespace from unittest.mock import AsyncMock, Mock, patch @@ -216,7 +216,7 @@ def test_history_preserves_unrelated_or_malformed_envelopes(monkeypatch, payload elif invalid == "signal": envelope["signal"] = "false" request = dt_history.EventSentEvent( - event_id=1, timestamp=datetime.now(timezone.utc), + event_id=1, timestamp=datetime.now(UTC), instance_id="ordinary-instance" if invalid == "target" else "@counter@one", name="application-event" if invalid == "name" else "op", input="not-json" if invalid == "json" else json.dumps(envelope)) @@ -235,7 +235,7 @@ def test_history_hydrates_signals_without_correlating_replies(monkeypatch, paylo token = json.dumps(store.upload(b'{"reference":"blob:v1:test-container:literal"}')) request_id = "63c281d7-02d7-412c-9f66-1d6d26a83948" request = dt_history.EventSentEvent( - event_id=1, timestamp=datetime.now(timezone.utc), instance_id="@counter@one", name=name, + event_id=1, timestamp=datetime.now(UTC), instance_id="@counter@one", name=name, input=json.dumps({"id": request_id, "op": "set", "signal": True, "input": token})) reply = dt_history.EventRaisedEvent( event_id=2, timestamp=request.timestamp, name=request_id, @@ -724,8 +724,8 @@ def _make_terminal_state( return SimpleNamespace( name="orch", instance_id="abc", - created_at=datetime(2026, 1, 1, tzinfo=timezone.utc), - last_updated_at=datetime(2026, 1, 1, tzinfo=timezone.utc), + created_at=datetime(2026, 1, 1, tzinfo=UTC), + last_updated_at=datetime(2026, 1, 1, tzinfo=UTC), runtime_status=runtime_status, serialized_input=None, serialized_output=serialized_output, @@ -985,8 +985,8 @@ def _fake_state( return SimpleNamespace( name="orch", instance_id="abc", - created_at=datetime(2026, 1, 1, tzinfo=timezone.utc), - last_updated_at=datetime(2026, 1, 2, tzinfo=timezone.utc), + created_at=datetime(2026, 1, 1, tzinfo=UTC), + last_updated_at=datetime(2026, 1, 2, tzinfo=UTC), runtime_status=runtime_status, serialized_input='{"in": 1}', serialized_output=serialized_output, @@ -1086,7 +1086,7 @@ async def test_get_status_missing_instance_is_falsy(): def _fake_history(): - started_at = datetime(2026, 1, 1, tzinfo=timezone.utc) + started_at = datetime(2026, 1, 1, tzinfo=UTC) return [ dt_history.OrchestratorStartedEvent( event_id=-1, timestamp=started_at), diff --git a/tests/azure-functions-durable/test_client_transport.py b/tests/azure-functions-durable/test_client_transport.py index 33578cdf..2a1ac7c7 100644 --- a/tests/azure-functions-durable/test_client_transport.py +++ b/tests/azure-functions-durable/test_client_transport.py @@ -3,7 +3,7 @@ import json from concurrent.futures import ThreadPoolExecutor -from datetime import datetime, timezone +from datetime import UTC, datetime from unittest.mock import AsyncMock, Mock, patch import grpc @@ -107,8 +107,8 @@ async def test_functions_client_reaches_grpc_server_with_url_path(local_grpc_ser ]) async def test_functions_query_limits_page_size_without_changing_query(use_async, page_size, expected): query = OrchestrationQuery( - created_time_from=datetime(2026, 1, 1, tzinfo=timezone.utc), - created_time_to=datetime(2026, 2, 1, tzinfo=timezone.utc), + created_time_from=datetime(2026, 1, 1, tzinfo=UTC), + created_time_to=datetime(2026, 2, 1, tzinfo=UTC), runtime_status=[OrchestrationStatus.RUNNING], max_instance_count=page_size, fetch_inputs_and_outputs=True, @@ -134,8 +134,8 @@ async def test_functions_query_limits_page_size_without_changing_query(use_async assert request.instanceIdPrefix.value == "prefix" assert list(request.runtimeStatus) == [pb.ORCHESTRATION_STATUS_RUNNING] assert [hub.value for hub in request.taskHubNames] == ["TestHub"] - assert request.createdTimeFrom.ToDatetime(timezone.utc) == query.created_time_from - assert request.createdTimeTo.ToDatetime(timezone.utc) == query.created_time_to + assert request.createdTimeFrom.ToDatetime(UTC) == query.created_time_from + assert request.createdTimeTo.ToDatetime(UTC) == query.created_time_to assert query.max_instance_count == page_size finally: if use_async: diff --git a/tests/azure-functions-durable/test_durable_orchestration_status_compat.py b/tests/azure-functions-durable/test_durable_orchestration_status_compat.py index 15f36810..cc414989 100644 --- a/tests/azure-functions-durable/test_durable_orchestration_status_compat.py +++ b/tests/azure-functions-durable/test_durable_orchestration_status_compat.py @@ -8,7 +8,7 @@ falsy/empty behaviour for a non-existent instance. """ -from datetime import datetime, timezone +from datetime import UTC, datetime from types import SimpleNamespace import azure.durable_functions as df @@ -39,8 +39,8 @@ def test_from_json_reconstructs_attribute_surface(): assert bool(status) is True assert status.name == "orch" assert status.instance_id == "abc" - assert status.created_time == datetime(2026, 1, 1, tzinfo=timezone.utc) - assert status.last_updated_time == datetime(2026, 1, 2, tzinfo=timezone.utc) + assert status.created_time == datetime(2026, 1, 1, tzinfo=UTC) + assert status.last_updated_time == datetime(2026, 1, 2, tzinfo=UTC) assert status.input_ == {"in": 1} assert status.output == {"out": 2} assert status.custom_status == "cs" diff --git a/tests/azure-functions-durable/test_entity_testing_support.py b/tests/azure-functions-durable/test_entity_testing_support.py index cc3b13f7..0a073e11 100644 --- a/tests/azure-functions-durable/test_entity_testing_support.py +++ b/tests/azure-functions-durable/test_entity_testing_support.py @@ -3,7 +3,7 @@ """Unit tests for host-free durable entity execution.""" -from datetime import datetime, timezone +from datetime import UTC, datetime from typing import Any import pytest @@ -105,7 +105,7 @@ def reset(self) -> str: def test_execute_entity_returns_typed_actions(): - signal_time = datetime(2030, 1, 2, 3, 4, tzinfo=timezone.utc) + signal_time = datetime(2030, 1, 2, 3, 4, tzinfo=UTC) class Relay(DurableEntity): def dispatch(self, input: dict[str, Any]) -> str: diff --git a/tests/azure-functions-durable/test_history_export_compat.py b/tests/azure-functions-durable/test_history_export_compat.py index e1a67c6d..a778e2bd 100644 --- a/tests/azure-functions-durable/test_history_export_compat.py +++ b/tests/azure-functions-durable/test_history_export_compat.py @@ -11,7 +11,7 @@ from __future__ import annotations -from datetime import datetime, timezone +from datetime import UTC, datetime from types import SimpleNamespace from unittest.mock import MagicMock @@ -29,7 +29,7 @@ import azure.durable_functions.internal.history_export_compat as hec _FROM = "2025-01-01T00:00:00+00:00" -_COMPLETED = datetime(2026, 1, 1, tzinfo=timezone.utc) +_COMPLETED = datetime(2026, 1, 1, tzinfo=UTC) def _state(instance_id: str, completed_at: datetime = _COMPLETED) -> SimpleNamespace: @@ -89,8 +89,8 @@ def test_exact_multiple_of_page_size_terminates(): def test_completed_time_window_is_applied(): - early = datetime(2020, 1, 1, tzinfo=timezone.utc) - late = datetime(2030, 1, 1, tzinfo=timezone.utc) + early = datetime(2020, 1, 1, tzinfo=UTC) + late = datetime(2030, 1, 1, tzinfo=UTC) ctx = _context_with_states([ _state("early", early), _state("in-window", _COMPLETED), _state("late", late)]) page = hec.list_terminal_instances(ctx, { @@ -158,7 +158,7 @@ def test_continuous_create_over_active_job_is_rejected_and_preserves_state(): config=ExportJobCreationOptions( mode=ExportMode.BATCH, completed_time_from=_COMPLETED, - completed_time_to=datetime(2026, 1, 2, tzinfo=timezone.utc), + completed_time_to=datetime(2026, 1, 2, tzinfo=UTC), destination=ExportDestination(container="exports", prefix="run-1"), ).to_configuration(), created_at=_COMPLETED, diff --git a/tests/azure-functions-durable/test_http_builtin_compat.py b/tests/azure-functions-durable/test_http_builtin_compat.py index 2130d694..e1360d07 100644 --- a/tests/azure-functions-durable/test_http_builtin_compat.py +++ b/tests/azure-functions-durable/test_http_builtin_compat.py @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from types import SimpleNamespace from unittest.mock import MagicMock, patch import urllib.error @@ -264,7 +264,7 @@ def create_timer(fire_at): get_input=lambda: request, call_activity=call_activity, create_timer=create_timer, - current_utc_datetime=datetime(2020, 1, 1, tzinfo=timezone.utc), + current_utc_datetime=datetime(2020, 1, 1, tzinfo=UTC), parent_instance_id=parent_instance_id, _activity_calls=activity_calls, ) @@ -419,7 +419,7 @@ def test_poll_orchestrator_resolves_relative_location(): # Retry-After parsing # --------------------------------------------------------------------------- -_NOW = datetime(2020, 1, 1, 0, 0, 0, tzinfo=timezone.utc) +_NOW = datetime(2020, 1, 1, 0, 0, 0, tzinfo=UTC) def test_retry_after_delta_seconds(): diff --git a/tests/durabletask-azuremanaged/entities/test_dts_class_based_entities_e2e.py b/tests/durabletask-azuremanaged/entities/test_dts_class_based_entities_e2e.py index ff6522f2..0fa8b625 100644 --- a/tests/durabletask-azuremanaged/entities/test_dts_class_based_entities_e2e.py +++ b/tests/durabletask-azuremanaged/entities/test_dts_class_based_entities_e2e.py @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -from datetime import datetime, timezone +from datetime import UTC, datetime import os import time @@ -67,7 +67,7 @@ def do_nothing(self, _): assert state is not None assert state.id == entity_id assert state.get_locked_by() is None - assert state.last_modified < datetime.now(timezone.utc) + assert state.last_modified < datetime.now(UTC) assert state.get_state(int) == 1 assert invoked diff --git a/tests/durabletask-azuremanaged/entities/test_dts_delayed_signals_e2e.py b/tests/durabletask-azuremanaged/entities/test_dts_delayed_signals_e2e.py index 0033bd20..f535730e 100644 --- a/tests/durabletask-azuremanaged/entities/test_dts_delayed_signals_e2e.py +++ b/tests/durabletask-azuremanaged/entities/test_dts_delayed_signals_e2e.py @@ -11,7 +11,7 @@ import threading import time import uuid -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta import pytest @@ -33,7 +33,7 @@ def __init__(self): def record(self) -> None: with self._lock: - self.times.append(datetime.now(timezone.utc)) + self.times.append(datetime.now(UTC)) @property def count(self) -> int: @@ -71,7 +71,7 @@ def test_client_delayed_signal_is_deferred(): c = DurableTaskSchedulerClient(host_address=endpoint, secure_channel=secure_channel, taskhub=taskhub_name, token_credential=None) entity_id = entities.EntityInstanceId("Recorder", f"client-delay-{uuid.uuid4().hex[:8]}") - sent_at = datetime.now(timezone.utc) + sent_at = datetime.now(UTC) c.signal_entity(entity_id, "ping", signal_time=sent_at + timedelta(seconds=4)) time.sleep(1) @@ -96,7 +96,7 @@ def signaling_orchestrator(ctx: task.OrchestrationContext, key: str): c = DurableTaskSchedulerClient(host_address=endpoint, secure_channel=secure_channel, taskhub=taskhub_name, token_credential=None) key = f"orch-delay-{uuid.uuid4().hex[:8]}" - sent_at = datetime.now(timezone.utc) + sent_at = datetime.now(UTC) instance_id = c.schedule_new_orchestration(signaling_orchestrator, input=key) c.wait_for_orchestration_completion(instance_id, timeout=30) diff --git a/tests/durabletask-azuremanaged/entities/test_dts_function_based_entities_e2e.py b/tests/durabletask-azuremanaged/entities/test_dts_function_based_entities_e2e.py index 11d61a92..7fbb1e17 100644 --- a/tests/durabletask-azuremanaged/entities/test_dts_function_based_entities_e2e.py +++ b/tests/durabletask-azuremanaged/entities/test_dts_function_based_entities_e2e.py @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -from datetime import datetime, timezone +from datetime import UTC, datetime import os import time @@ -67,7 +67,7 @@ def empty_entity(ctx: entities.EntityContext, _): assert state is not None assert state.id == entity_id assert state.get_locked_by() is None - assert state.last_modified < datetime.now(timezone.utc) + assert state.last_modified < datetime.now(UTC) assert state.get_state(int) == 1 assert invoked diff --git a/tests/durabletask-azuremanaged/scheduled/test_dts_scheduled_e2e.py b/tests/durabletask-azuremanaged/scheduled/test_dts_scheduled_e2e.py index 05f7e7e3..4027090c 100644 --- a/tests/durabletask-azuremanaged/scheduled/test_dts_scheduled_e2e.py +++ b/tests/durabletask-azuremanaged/scheduled/test_dts_scheduled_e2e.py @@ -10,7 +10,7 @@ import threading import time import uuid -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta import pytest @@ -93,7 +93,7 @@ def test_create_describe_and_run(): orchestration_name=task.get_name(target_orchestrator), interval=timedelta(seconds=1), orchestration_input="hello", - start_at=datetime.now(timezone.utc), + start_at=datetime.now(UTC), start_immediately_if_late=True, )) try: @@ -117,7 +117,7 @@ def test_pause_and_resume(): schedule_id=schedule_id, orchestration_name=task.get_name(target_orchestrator), interval=timedelta(seconds=1), - start_at=datetime.now(timezone.utc), + start_at=datetime.now(UTC), start_immediately_if_late=True, )) try: @@ -149,7 +149,7 @@ def test_update_interval_and_input(): orchestration_name=task.get_name(target_orchestrator), interval=timedelta(seconds=30), orchestration_input="before", - start_at=datetime.now(timezone.utc) + timedelta(hours=1), + start_at=datetime.now(UTC) + timedelta(hours=1), )) try: schedule.update(ScheduleUpdateOptions( @@ -177,7 +177,7 @@ def test_list_and_delete(): schedule_id=schedule_id, orchestration_name=task.get_name(target_orchestrator), interval=timedelta(seconds=30), - start_at=datetime.now(timezone.utc) + timedelta(hours=1), + start_at=datetime.now(UTC) + timedelta(hours=1), )) created.append(schedule_id) diff --git a/tests/durabletask-azuremanaged/test_dts_batch_actions.py b/tests/durabletask-azuremanaged/test_dts_batch_actions.py index a3c0c356..3990bc91 100644 --- a/tests/durabletask-azuremanaged/test_dts_batch_actions.py +++ b/tests/durabletask-azuremanaged/test_dts_batch_actions.py @@ -4,7 +4,7 @@ import asyncio import logging import os -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta import pytest from durabletask import client, entities, task @@ -120,13 +120,13 @@ def test_get_orchestration_state_by_time_range(): taskhub=taskhub_name, token_credential=None) # Get current time - before_creation = datetime.now(timezone.utc) - timedelta(seconds=5) + before_creation = datetime.now(UTC) - timedelta(seconds=5) # Schedule orchestration id = c.schedule_new_orchestration(empty_orchestrator, input="TimeTest") c.wait_for_orchestration_completion(id, timeout=30) - after_creation = datetime.now(timezone.utc) + timedelta(seconds=5) + after_creation = datetime.now(UTC) + timedelta(seconds=5) # Query by time range query = client.OrchestrationQuery( @@ -263,13 +263,13 @@ def test_purge_orchestrations_by_time_range(): taskhub=taskhub_name, token_credential=None) # Get current time - before_creation = datetime.now(timezone.utc) - timedelta(seconds=5) + before_creation = datetime.now(UTC) - timedelta(seconds=5) # Schedule orchestration id = c.schedule_new_orchestration(empty_orchestrator, input="ToPurgeByTime") c.wait_for_orchestration_completion(id, timeout=30) - after_creation = datetime.now(timezone.utc) + timedelta(seconds=5) + after_creation = datetime.now(UTC) + timedelta(seconds=5) # Verify it exists state_before = c.get_orchestration_state(id) @@ -385,14 +385,14 @@ def simple_entity(ctx: entities.EntityContext, input): taskhub=taskhub_name, token_credential=None) # Get current time - before_creation = datetime.now(timezone.utc) - timedelta(seconds=5) + before_creation = datetime.now(UTC) - timedelta(seconds=5) # Create entity entity_id = entities.EntityInstanceId("simple_entity", "timeTestEntity") c.signal_entity(entity_id, "set", "test_value") asyncio.run(asyncio.sleep(2)) # Wait for signal to be processed - after_creation = datetime.now(timezone.utc) + timedelta(seconds=5) + after_creation = datetime.now(UTC) + timedelta(seconds=5) # Query by time range query = client.EntityQuery( diff --git a/tests/durabletask-azuremanaged/test_durabletask_grpc_interceptor.py b/tests/durabletask-azuremanaged/test_durabletask_grpc_interceptor.py index e43e33fd..dbc68e38 100644 --- a/tests/durabletask-azuremanaged/test_durabletask_grpc_interceptor.py +++ b/tests/durabletask-azuremanaged/test_durabletask_grpc_interceptor.py @@ -4,7 +4,7 @@ import asyncio import unittest from concurrent import futures -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from importlib.metadata import PackageNotFoundError, version import threading import time @@ -285,7 +285,7 @@ def test_concurrent_refresh_performs_single_refresh(self): manager.get_access_token() self.assertEqual(1, credential.calls) - manager.expiry_time = datetime.now(timezone.utc) - timedelta(seconds=1) + manager.expiry_time = datetime.now(UTC) - timedelta(seconds=1) self._get_access_token_concurrently(manager) @@ -344,7 +344,7 @@ async def test_concurrent_refresh_performs_single_refresh(self): await manager.get_access_token() self.assertEqual(1, credential.calls) - manager.expiry_time = datetime.now(timezone.utc) - timedelta(seconds=1) + manager.expiry_time = datetime.now(UTC) - timedelta(seconds=1) tokens = await asyncio.gather(*(manager.get_access_token() for _ in range(16))) @@ -384,7 +384,7 @@ def test_manager_is_reusable_across_event_loops(self): first = asyncio.run(manager.get_access_token()) - manager.expiry_time = datetime.now(timezone.utc) - timedelta(seconds=1) + manager.expiry_time = datetime.now(UTC) - timedelta(seconds=1) # The second run uses a distinct event loop; the refresh guard must not stay # bound to the first, now-closed loop. diff --git a/tests/durabletask-azuremanaged/test_resource_id.py b/tests/durabletask-azuremanaged/test_resource_id.py index 8fe2b44c..c70cb90a 100644 --- a/tests/durabletask-azuremanaged/test_resource_id.py +++ b/tests/durabletask-azuremanaged/test_resource_id.py @@ -2,7 +2,7 @@ # Licensed under the MIT License. from collections.abc import Callable -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from unittest.mock import AsyncMock, Mock, call, patch import grpc @@ -51,7 +51,7 @@ (None, "api://custom/.default/.default", "api://custom/.default"), ] _INVALID_RESOURCE_IDS = [" \t ", "///", "/.default", " /.DEFAULT/// "] -_EXPIRED = datetime.now(timezone.utc) - timedelta(hours=1) +_EXPIRED = datetime.now(UTC) - timedelta(hours=1) def _set_region(monkeypatch: pytest.MonkeyPatch, region: str | None) -> None: diff --git a/tests/durabletask/entities/test_delayed_signals_e2e.py b/tests/durabletask/entities/test_delayed_signals_e2e.py index b759e5cd..1f8ce430 100644 --- a/tests/durabletask/entities/test_delayed_signals_e2e.py +++ b/tests/durabletask/entities/test_delayed_signals_e2e.py @@ -5,7 +5,7 @@ import threading import time -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta import pytest @@ -35,7 +35,7 @@ def __init__(self): def record(self) -> None: with self._lock: - self.times.append(datetime.now(timezone.utc)) + self.times.append(datetime.now(UTC)) @property def count(self) -> int: @@ -71,8 +71,8 @@ def test_client_delayed_signal_is_deferred(): with client.TaskHubGrpcClient(host_address=HOST) as c: entity_id = entities.EntityInstanceId("Recorder", "client-delay") - signal_at = datetime.now(timezone.utc) + timedelta(seconds=2) - sent_at = datetime.now(timezone.utc) + signal_at = datetime.now(UTC) + timedelta(seconds=2) + sent_at = datetime.now(UTC) c.signal_entity(entity_id, "ping", signal_time=signal_at) # Should not have fired immediately. @@ -108,7 +108,7 @@ def signaling_orchestrator(ctx: task.OrchestrationContext, _): w.start() with client.TaskHubGrpcClient(host_address=HOST) as c: - sent_at = datetime.now(timezone.utc) + sent_at = datetime.now(UTC) instance_id = c.schedule_new_orchestration(signaling_orchestrator) c.wait_for_orchestration_completion(instance_id, timeout=30) diff --git a/tests/durabletask/extensions/history_export/test_activities.py b/tests/durabletask/extensions/history_export/test_activities.py index 3c93d190..5aaa1cfa 100644 --- a/tests/durabletask/extensions/history_export/test_activities.py +++ b/tests/durabletask/extensions/history_export/test_activities.py @@ -16,7 +16,7 @@ import gzip import json import threading -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta import pytest @@ -133,7 +133,7 @@ def test_activities_list_and_export_to_in_memory_writer(c, seeded_ids): writer = _InMemoryWriter() _holder.context = HistoryExportContext(client=c, writer=writer) - now = datetime.now(timezone.utc) + now = datetime.now(UTC) fmt = ExportFormat(kind=ExportFormatKind.JSONL_GZIP) dest = ExportDestination(container="exports", prefix="run-1") orch_input = { @@ -186,7 +186,7 @@ def write(self, **_): _holder.context = HistoryExportContext(client=c, writer=FailingWriter()) - now = datetime.now(timezone.utc) + now = datetime.now(UTC) fmt = ExportFormat(kind=ExportFormatKind.JSON) dest = ExportDestination(container="exports") orch_input = { @@ -216,7 +216,7 @@ def write(self, **_): def test_activities_fail_when_context_resolution_raises(c): # Do NOT set a context in the holder. The resolver should raise, # and the activity failure must surface on the orchestration. - now = datetime.now(timezone.utc) + now = datetime.now(UTC) fmt = ExportFormat(kind=ExportFormatKind.JSON) dest = ExportDestination(container="exports") orch_input = { @@ -343,7 +343,7 @@ def test_blob_name_matches_dotnet_hash_scheme(): _dotnet_o_format, ) - last_updated = datetime(2026, 6, 4, 17, 9, 9, 420990, tzinfo=timezone.utc) + last_updated = datetime(2026, 6, 4, 17, 9, 9, 420990, tzinfo=UTC) instance_id = "inst-1" fmt = ExportFormat(kind=ExportFormatKind.JSON) @@ -379,7 +379,7 @@ def test_blob_name_isolates_instance_ids_that_differ_only_by_slash(): """ from durabletask.extensions.history_export.activities import _blob_name_for - last_updated = datetime(2026, 6, 4, 17, 9, 9, 420990, tzinfo=timezone.utc) + last_updated = datetime(2026, 6, 4, 17, 9, 9, 420990, tzinfo=UTC) fmt = ExportFormat(kind=ExportFormatKind.JSON) name_a = _blob_name_for( @@ -398,8 +398,8 @@ def test_blob_name_changes_when_instance_terminal_timestamp_changes(): fmt = ExportFormat(kind=ExportFormatKind.JSON) instance_id = "inst-x" - earlier = datetime(2026, 6, 4, 17, 0, 0, 0, tzinfo=timezone.utc) - later = datetime(2026, 6, 4, 18, 0, 0, 0, tzinfo=timezone.utc) + earlier = datetime(2026, 6, 4, 17, 0, 0, 0, tzinfo=UTC) + later = datetime(2026, 6, 4, 18, 0, 0, 0, tzinfo=UTC) name_earlier = _blob_name_for( instance_id=instance_id, last_updated_at=earlier, prefix=None, fmt=fmt, diff --git a/tests/durabletask/extensions/history_export/test_client.py b/tests/durabletask/extensions/history_export/test_client.py index 159e129b..92af07ed 100644 --- a/tests/durabletask/extensions/history_export/test_client.py +++ b/tests/durabletask/extensions/history_export/test_client.py @@ -15,7 +15,7 @@ import json import threading from unittest.mock import AsyncMock, MagicMock -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta import pytest import grpc @@ -117,7 +117,7 @@ def seeded_terminal_instances(dt_client, w): def test_create_get_and_wait_for_job_end_to_end( dt_client, export_client, writer, seeded_terminal_instances, ): - now = datetime.now(timezone.utc) + now = datetime.now(UTC) desc = export_client.create_job( ExportJobCreationOptions( mode=ExportMode.BATCH, @@ -158,7 +158,7 @@ async def test_async_client_create_list_wait_and_delete( ): async with client.AsyncTaskHubGrpcClient(host_address=HOST) as dt_client: export_client = AsyncExportHistoryClient(dt_client, writer) - now = datetime.now(timezone.utc) + now = datetime.now(UTC) desc = await export_client.create_job(ExportJobCreationOptions( mode=ExportMode.BATCH, completed_time_from=now - timedelta(hours=1), @@ -257,8 +257,8 @@ def test_wait_for_job_times_out_when_status_stays_active( local_client = ExportHistoryClient(dt_client, writer) fake_desc = ExportJobCreationOptions( mode=ExportMode.BATCH, - completed_time_from=datetime.now(timezone.utc) - timedelta(hours=1), - completed_time_to=datetime.now(timezone.utc) + timedelta(hours=1), + completed_time_from=datetime.now(UTC) - timedelta(hours=1), + completed_time_to=datetime.now(UTC) + timedelta(hours=1), destination=ExportDestination(container="c"), format=ExportFormat(kind=ExportFormatKind.JSON), ).to_configuration() @@ -269,8 +269,8 @@ def fake_get_job(self, job_id): return ExportJobDescription( job_id=job_id, status=ExportJobStatus.ACTIVE, - created_at=datetime.now(timezone.utc), - last_modified_at=datetime.now(timezone.utc), + created_at=datetime.now(UTC), + last_modified_at=datetime.now(UTC), config=fake_desc, orchestrator_instance_id="orch-1", scanned_instances=0, @@ -288,7 +288,7 @@ def fake_get_job(self, job_id): def test_delete_job_clears_entity_state(dt_client, export_client): - now = datetime.now(timezone.utc) + now = datetime.now(UTC) desc = export_client.create_job( ExportJobCreationOptions( mode=ExportMode.BATCH, @@ -312,7 +312,7 @@ def test_delete_job_clears_entity_state(dt_client, export_client): def test_list_jobs_returns_created_jobs_and_supports_status_filter(export_client): - now = datetime.now(timezone.utc) + now = datetime.now(UTC) completed_ids: list[str] = [] for _ in range(3): d = export_client.create_job( @@ -350,7 +350,7 @@ def test_list_jobs_returns_created_jobs_and_supports_status_filter(export_client def test_export_history_job_client_round_trip(export_client): - now = datetime.now(timezone.utc) + now = datetime.now(UTC) desc = export_client.create_job( ExportJobCreationOptions( mode=ExportMode.BATCH, diff --git a/tests/durabletask/extensions/history_export/test_entity.py b/tests/durabletask/extensions/history_export/test_entity.py index 434d9cbc..c9c566eb 100644 --- a/tests/durabletask/extensions/history_export/test_entity.py +++ b/tests/durabletask/extensions/history_export/test_entity.py @@ -10,7 +10,7 @@ from __future__ import annotations -from datetime import datetime, timezone +from datetime import UTC, datetime from typing import Callable, Optional import pytest @@ -35,8 +35,8 @@ PORT = find_free_port() HOST = f"localhost:{PORT}" -_WINDOW_START = datetime(2025, 1, 1, tzinfo=timezone.utc) -_WINDOW_END = datetime(2025, 1, 2, tzinfo=timezone.utc) +_WINDOW_START = datetime(2025, 1, 1, tzinfo=UTC) +_WINDOW_END = datetime(2025, 1, 2, tzinfo=UTC) def _no_op_orchestrator(ctx: task.OrchestrationContext, _input): diff --git a/tests/durabletask/extensions/history_export/test_models.py b/tests/durabletask/extensions/history_export/test_models.py index 8144a13a..c56de1ed 100644 --- a/tests/durabletask/extensions/history_export/test_models.py +++ b/tests/durabletask/extensions/history_export/test_models.py @@ -3,7 +3,7 @@ """Tests for the history-export public data models.""" -from datetime import datetime, timezone, timedelta +from datetime import UTC, datetime, timedelta import pytest @@ -27,8 +27,8 @@ ) -_WINDOW_START = datetime(2025, 1, 1, tzinfo=timezone.utc) -_WINDOW_END = datetime(2025, 1, 2, tzinfo=timezone.utc) +_WINDOW_START = datetime(2025, 1, 1, tzinfo=UTC) +_WINDOW_END = datetime(2025, 1, 2, tzinfo=UTC) def _basic_destination() -> ExportDestination: @@ -213,7 +213,7 @@ def test_naive_datetimes_are_treated_as_utc(self) -> None: f = ExportFilter(completed_time_from=naive, completed_time_to=_WINDOW_END) d = f.to_dict() restored = ExportFilter.from_dict(d) - assert restored.completed_time_from == naive.replace(tzinfo=timezone.utc) + assert restored.completed_time_from == naive.replace(tzinfo=UTC) class TestDescriptionFromState: diff --git a/tests/durabletask/extensions/history_export/test_orchestrator.py b/tests/durabletask/extensions/history_export/test_orchestrator.py index 5298e176..19d4ba67 100644 --- a/tests/durabletask/extensions/history_export/test_orchestrator.py +++ b/tests/durabletask/extensions/history_export/test_orchestrator.py @@ -14,7 +14,7 @@ import gzip import json import threading -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta import pytest @@ -155,7 +155,7 @@ def seeded_ids(dt_client, w): def test_orchestrator_exports_all_terminal_instances_and_marks_completed( dt_client, export_client, writer, seeded_ids, ): - now = datetime.now(timezone.utc) + now = datetime.now(UTC) desc = export_client.create_job( ExportJobCreationOptions( mode=ExportMode.BATCH, @@ -200,7 +200,7 @@ def test_orchestrator_exits_when_entity_is_deleted_mid_run( running" rather than the old "orchestration completes with status Cancelled". """ - now = datetime.now(timezone.utc) + now = datetime.now(UTC) desc = export_client.create_job( ExportJobCreationOptions( mode=ExportMode.CONTINUOUS, @@ -256,7 +256,7 @@ def test_orchestrator_records_failure_when_context_resolution_fails( # orchestrator's activities run; the autouse ``_default_context`` # fixture restores the good context afterwards. _holder.context = None - now = datetime.now(timezone.utc) + now = datetime.now(UTC) desc = export_client.create_job( ExportJobCreationOptions( mode=ExportMode.BATCH, @@ -296,7 +296,7 @@ def test_batch_failure_marks_job_failed_without_invalid_transition( client=dt_client, writer=_AlwaysFailingWriter(), ) with caplog.at_level("WARNING", logger="durabletask.extensions.history_export"): - now = datetime.now(timezone.utc) + now = datetime.now(UTC) desc = export_client.create_job( ExportJobCreationOptions( mode=ExportMode.BATCH, diff --git a/tests/durabletask/extensions/history_export/test_serialization.py b/tests/durabletask/extensions/history_export/test_serialization.py index d4479355..78822389 100644 --- a/tests/durabletask/extensions/history_export/test_serialization.py +++ b/tests/durabletask/extensions/history_export/test_serialization.py @@ -7,7 +7,7 @@ import gzip import json -from datetime import datetime, timezone +from datetime import UTC, datetime import pytest @@ -25,7 +25,7 @@ def _sample_events() -> list[history.HistoryEvent]: - ts = datetime(2025, 1, 2, 3, 4, 5, tzinfo=timezone.utc) + ts = datetime(2025, 1, 2, 3, 4, 5, tzinfo=UTC) return [ history.OrchestratorStartedEvent(event_id=-1, timestamp=ts), history.ExecutionStartedEvent( @@ -59,7 +59,7 @@ class TestEventToDict: def test_includes_type_discriminator(self) -> None: e = history.OrchestratorStartedEvent( event_id=0, - timestamp=datetime(2025, 1, 1, tzinfo=timezone.utc), + timestamp=datetime(2025, 1, 1, tzinfo=UTC), ) d = event_to_dict(e) assert d["event_type"] == "OrchestratorStartedEvent" @@ -71,7 +71,7 @@ def test_failure_details_is_json_safe(self) -> None: # JSON-safe dict rather than a raw FailureDetails object. e = history.ExecutionCompletedEvent( event_id=-1, - timestamp=datetime(2025, 1, 1, tzinfo=timezone.utc), + timestamp=datetime(2025, 1, 1, tzinfo=UTC), orchestration_status=3, result=None, failure_details=task.FailureDetails( @@ -159,7 +159,7 @@ def test_unknown_kind_rejected(self) -> None: class TestMetadataEmbedding: def _state(self) -> client_module.OrchestrationState: - ts = datetime(2025, 1, 2, 3, 4, 5, tzinfo=timezone.utc) + ts = datetime(2025, 1, 2, 3, 4, 5, tzinfo=UTC) return client_module.OrchestrationState( instance_id="inst-meta", name="MyOrch", diff --git a/tests/durabletask/scheduled/test_client_filter_and_capability.py b/tests/durabletask/scheduled/test_client_filter_and_capability.py index 4de3ccbd..1c6c0b12 100644 --- a/tests/durabletask/scheduled/test_client_filter_and_capability.py +++ b/tests/durabletask/scheduled/test_client_filter_and_capability.py @@ -3,7 +3,7 @@ """Unit tests for ScheduleClient filtering and scheduled-tasks worker capability.""" -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from types import SimpleNamespace from unittest.mock import AsyncMock, MagicMock @@ -30,7 +30,7 @@ def _state_created_at(created_at: datetime) -> ScheduleState: class TestMatchesFilter: def test_naive_bound_does_not_crash_against_aware_created_at(self): - state = _state_created_at(datetime(2026, 1, 15, tzinfo=timezone.utc)) + state = _state_created_at(datetime(2026, 1, 15, tzinfo=UTC)) # A naive bound is normalized by ScheduleQuery, so the comparison must # not raise "can't compare offset-naive and offset-aware". q = ScheduleQuery(created_from=datetime(2026, 1, 1, 0, 0, 0)) @@ -39,32 +39,32 @@ def test_naive_bound_does_not_crash_against_aware_created_at(self): def test_created_from_is_exclusive(self): # Bounds match .NET's exclusive semantics: a schedule created exactly at # the bound is excluded. - boundary = datetime(2026, 1, 1, tzinfo=timezone.utc) + boundary = datetime(2026, 1, 1, tzinfo=UTC) state = _state_created_at(boundary) q = ScheduleQuery(created_from=boundary) assert ScheduledTaskClient.matches_filter(state, q) is False def test_created_to_is_exclusive(self): - boundary = datetime(2026, 1, 1, tzinfo=timezone.utc) + boundary = datetime(2026, 1, 1, tzinfo=UTC) state = _state_created_at(boundary) q = ScheduleQuery(created_to=boundary) assert ScheduledTaskClient.matches_filter(state, q) is False def test_inside_window_matches(self): - state = _state_created_at(datetime(2026, 1, 15, tzinfo=timezone.utc)) + state = _state_created_at(datetime(2026, 1, 15, tzinfo=UTC)) q = ScheduleQuery( - created_from=datetime(2026, 1, 1, tzinfo=timezone.utc), - created_to=datetime(2026, 2, 1, tzinfo=timezone.utc), + created_from=datetime(2026, 1, 1, tzinfo=UTC), + created_to=datetime(2026, 2, 1, tzinfo=UTC), ) assert ScheduledTaskClient.matches_filter(state, q) is True def test_outside_window_is_excluded(self): - state = _state_created_at(datetime(2026, 3, 1, tzinfo=timezone.utc)) - q = ScheduleQuery(created_to=datetime(2026, 2, 1, tzinfo=timezone.utc)) + state = _state_created_at(datetime(2026, 3, 1, tzinfo=UTC)) + q = ScheduleQuery(created_to=datetime(2026, 2, 1, tzinfo=UTC)) assert ScheduledTaskClient.matches_filter(state, q) is False def test_status_filter(self): - state = _state_created_at(datetime(2026, 1, 1, tzinfo=timezone.utc)) + state = _state_created_at(datetime(2026, 1, 1, tzinfo=UTC)) assert ScheduledTaskClient.matches_filter(state, ScheduleQuery(status=ScheduleStatus.ACTIVE)) is True assert ScheduledTaskClient.matches_filter(state, ScheduleQuery(status=ScheduleStatus.PAUSED)) is False diff --git a/tests/durabletask/scheduled/test_models.py b/tests/durabletask/scheduled/test_models.py index a655668d..cc48001a 100644 --- a/tests/durabletask/scheduled/test_models.py +++ b/tests/durabletask/scheduled/test_models.py @@ -3,7 +3,7 @@ """Unit tests for scheduled tasks models, validation, and serialization.""" -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta import pytest @@ -46,9 +46,9 @@ def test_valid_options(self): class TestCreationOptionsSerialization: @pytest.mark.parametrize("timestamp, expected", [ - ("2026-01-01T12:34:56Z", datetime(2026, 1, 1, 12, 34, 56, tzinfo=timezone.utc)), - ("2026-01-01T12:34:56+00:00", datetime(2026, 1, 1, 12, 34, 56, tzinfo=timezone.utc)), - ("2026-01-01T14:34:56+02:00", datetime(2026, 1, 1, 12, 34, 56, tzinfo=timezone.utc)), + ("2026-01-01T12:34:56Z", datetime(2026, 1, 1, 12, 34, 56, tzinfo=UTC)), + ("2026-01-01T12:34:56+00:00", datetime(2026, 1, 1, 12, 34, 56, tzinfo=UTC)), + ("2026-01-01T14:34:56+02:00", datetime(2026, 1, 1, 12, 34, 56, tzinfo=UTC)), ("2026-01-01T12:34:56", datetime(2026, 1, 1, 12, 34, 56)), ]) def test_parse_iso_timestamps(self, timestamp: str, expected: datetime) -> None: @@ -64,8 +64,8 @@ def test_parse_iso_timestamps(self, timestamp: str, expected: datetime) -> None: assert options.start_at.isoformat() == timestamp.replace("Z", "+00:00") def test_round_trip_through_json(self): - start = datetime(2026, 1, 1, tzinfo=timezone.utc) - end = datetime(2026, 2, 1, tzinfo=timezone.utc) + start = datetime(2026, 1, 1, tzinfo=UTC) + end = datetime(2026, 2, 1, tzinfo=UTC) options = ScheduleCreationOptions( schedule_id="s1", orchestration_name="orch", interval=timedelta(minutes=5), orchestration_input={"key": "value"}, orchestration_instance_id="inst-1", @@ -101,8 +101,8 @@ class TestScheduleConfiguration: def test_from_create_options_rejects_start_after_end(self): options = ScheduleCreationOptions( schedule_id="s1", orchestration_name="orch", interval=timedelta(seconds=5), - start_at=datetime(2026, 2, 1, tzinfo=timezone.utc), - end_at=datetime(2026, 1, 1, tzinfo=timezone.utc)) + start_at=datetime(2026, 2, 1, tzinfo=UTC), + end_at=datetime(2026, 1, 1, tzinfo=UTC)) with pytest.raises(ValueError): ScheduleConfiguration.from_create_options(options) @@ -127,11 +127,11 @@ def test_config_round_trip(self): config = ScheduleConfiguration.from_create_options( ScheduleCreationOptions(schedule_id="s1", orchestration_name="orch", interval=timedelta(seconds=5), - start_at=datetime(2026, 1, 1, tzinfo=timezone.utc))) + start_at=datetime(2026, 1, 1, tzinfo=UTC))) restored = converter.deserialize(converter.serialize(config), ScheduleConfiguration) assert restored.schedule_id == "s1" assert restored.interval == timedelta(seconds=5) - assert restored.start_at == datetime(2026, 1, 1, tzinfo=timezone.utc) + assert restored.start_at == datetime(2026, 1, 1, tzinfo=UTC) def test_to_json_uses_dotnet_compatible_shape(self): # The Durable Task Scheduler dashboard deserializes the raw entity state @@ -210,7 +210,7 @@ class TestScheduleState: def test_round_trip_and_description(self): state = ScheduleState() state.status = ScheduleStatus.ACTIVE - state.schedule_created_at = datetime(2026, 1, 1, tzinfo=timezone.utc) + state.schedule_created_at = datetime(2026, 1, 1, tzinfo=UTC) state.schedule_configuration = ScheduleConfiguration.from_create_options( ScheduleCreationOptions(schedule_id="s1", orchestration_name="orch", interval=timedelta(seconds=5))) @@ -218,7 +218,7 @@ def test_round_trip_and_description(self): # The nested ``ScheduleConfiguration`` round-trips automatically. restored = converter.deserialize(converter.serialize(state), ScheduleState) assert restored.status == ScheduleStatus.ACTIVE - assert restored.schedule_created_at == datetime(2026, 1, 1, tzinfo=timezone.utc) + assert restored.schedule_created_at == datetime(2026, 1, 1, tzinfo=UTC) assert restored.schedule_configuration is not None assert restored.schedule_configuration.interval == timedelta(seconds=5) @@ -301,13 +301,13 @@ def test_naive_bounds_are_coerced_to_aware_utc(self): created_to=datetime(2026, 2, 1, 0, 0, 0), ) assert q.created_from is not None and q.created_to is not None - assert q.created_from == datetime(2026, 1, 1, tzinfo=timezone.utc) - assert q.created_to == datetime(2026, 2, 1, tzinfo=timezone.utc) - assert q.created_from.tzinfo is timezone.utc - assert q.created_to.tzinfo is timezone.utc + assert q.created_from == datetime(2026, 1, 1, tzinfo=UTC) + assert q.created_to == datetime(2026, 2, 1, tzinfo=UTC) + assert q.created_from.tzinfo is UTC + assert q.created_to.tzinfo is UTC def test_aware_bounds_are_preserved(self): - start = datetime(2026, 1, 1, tzinfo=timezone.utc) + start = datetime(2026, 1, 1, tzinfo=UTC) q = ScheduleQuery(created_from=start) assert q.created_from == start diff --git a/tests/durabletask/scheduled/test_schedule_entity.py b/tests/durabletask/scheduled/test_schedule_entity.py index 91733579..01406162 100644 --- a/tests/durabletask/scheduled/test_schedule_entity.py +++ b/tests/durabletask/scheduled/test_schedule_entity.py @@ -4,7 +4,7 @@ """Unit tests for the Schedule entity behavior, driven through _EntityExecutor.""" import logging -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from typing import Any import pytest @@ -145,7 +145,7 @@ def test_update_no_change_does_not_signal(self): class TestRunSchedule: def test_runs_orchestration_when_due_and_rearms(self): h = Harness() - past = datetime.now(timezone.utc) - timedelta(hours=1) + past = datetime.now(UTC) - timedelta(hours=1) h.run("create_schedule", _creation_options(start_at=past, start_immediately_if_late=True)) _, actions = h.run("run_schedule", h.token) @@ -169,7 +169,7 @@ def test_ignores_stale_token(self): def test_future_start_does_not_run_yet(self): h = Harness() - future = datetime.now(timezone.utc) + timedelta(days=1) + future = datetime.now(UTC) + timedelta(days=1) h.run("create_schedule", _creation_options(start_at=future)) _, actions = h.run("run_schedule", h.token) assert len(_start_actions(actions)) == 0 @@ -180,8 +180,8 @@ def test_future_start_does_not_run_yet(self): def test_past_end_time_deletes(self): h = Harness() - start = datetime.now(timezone.utc) - timedelta(hours=2) - end = datetime.now(timezone.utc) - timedelta(hours=1) + start = datetime.now(UTC) - timedelta(hours=2) + end = datetime.now(UTC) - timedelta(hours=1) h.run("create_schedule", _creation_options(start_at=start, end_at=end)) _, actions = h.run("run_schedule", h.token) delete_signals = [a for a in _signal_actions(actions) if a.sendSignal.name == "delete"] diff --git a/tests/durabletask/scheduled/test_scheduled_e2e.py b/tests/durabletask/scheduled/test_scheduled_e2e.py index f0397ad4..e0950cc7 100644 --- a/tests/durabletask/scheduled/test_scheduled_e2e.py +++ b/tests/durabletask/scheduled/test_scheduled_e2e.py @@ -5,7 +5,7 @@ import threading import time -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta import pytest @@ -91,7 +91,7 @@ def test_create_describe_and_run(): orchestration_name=task.get_name(target_orchestrator), interval=timedelta(seconds=1), orchestration_input="hello", - start_at=datetime.now(timezone.utc), + start_at=datetime.now(UTC), start_immediately_if_late=True, )) @@ -123,7 +123,7 @@ async def test_async_client_create_list_and_delete(): schedule_id="async-schedule", orchestration_name=task.get_name(target_orchestrator), interval=timedelta(seconds=30), - start_at=datetime.now(timezone.utc) + timedelta(hours=1), + start_at=datetime.now(UTC) + timedelta(hours=1), )) assert (await scheduled.get_schedule("async-schedule")) is not None @@ -148,7 +148,7 @@ def test_pause_and_resume(): schedule_id="sched-pause", orchestration_name=task.get_name(target_orchestrator), interval=timedelta(seconds=1), - start_at=datetime.now(timezone.utc), + start_at=datetime.now(UTC), start_immediately_if_late=True, )) @@ -180,7 +180,7 @@ def test_update_interval_and_input(): orchestration_name=task.get_name(target_orchestrator), interval=timedelta(seconds=30), orchestration_input="before", - start_at=datetime.now(timezone.utc) + timedelta(hours=1), + start_at=datetime.now(UTC) + timedelta(hours=1), )) schedule.update(ScheduleUpdateOptions( @@ -205,13 +205,13 @@ def test_list_schedules_with_prefix_and_status(): schedule_id=f"group-a-{i}", orchestration_name=task.get_name(target_orchestrator), interval=timedelta(seconds=30), - start_at=datetime.now(timezone.utc) + timedelta(hours=1), + start_at=datetime.now(UTC) + timedelta(hours=1), )) scheduled.create_schedule(ScheduleCreationOptions( schedule_id="group-b-0", orchestration_name=task.get_name(target_orchestrator), interval=timedelta(seconds=30), - start_at=datetime.now(timezone.utc) + timedelta(hours=1), + start_at=datetime.now(UTC) + timedelta(hours=1), )) all_schedules = scheduled.list_schedules() @@ -237,7 +237,7 @@ def test_delete_removes_schedule(): schedule_id="sched-delete", orchestration_name=task.get_name(target_orchestrator), interval=timedelta(seconds=30), - start_at=datetime.now(timezone.utc) + timedelta(hours=1), + start_at=datetime.now(UTC) + timedelta(hours=1), )) assert scheduled.get_schedule("sched-delete") is not None diff --git a/tests/durabletask/test_batch_actions.py b/tests/durabletask/test_batch_actions.py index c0721eba..53b2f7c5 100644 --- a/tests/durabletask/test_batch_actions.py +++ b/tests/durabletask/test_batch_actions.py @@ -7,7 +7,7 @@ import logging import time -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta import pytest from durabletask import client, entities, task @@ -134,13 +134,13 @@ def test_get_orchestration_state_by_time_range(backend): try: with TaskHubGrpcClient(host_address=HOST) as c: # Get current time - before_creation = datetime.now(timezone.utc) - timedelta(seconds=5) + before_creation = datetime.now(UTC) - timedelta(seconds=5) # Schedule orchestration id = c.schedule_new_orchestration(empty_orchestrator, input="TimeTest") c.wait_for_orchestration_completion(id, timeout=30) - after_creation = datetime.now(timezone.utc) + timedelta(seconds=5) + after_creation = datetime.now(UTC) + timedelta(seconds=5) # Query by time range query = client.OrchestrationQuery( @@ -345,13 +345,13 @@ def test_purge_orchestrations_by_time_range(backend): try: with TaskHubGrpcClient(host_address=HOST) as c: # Get current time - before_creation = datetime.now(timezone.utc) - timedelta(seconds=5) + before_creation = datetime.now(UTC) - timedelta(seconds=5) # Schedule orchestration id = c.schedule_new_orchestration(empty_orchestrator, input="ToPurgeByTime") c.wait_for_orchestration_completion(id, timeout=30) - after_creation = datetime.now(timezone.utc) + timedelta(seconds=5) + after_creation = datetime.now(UTC) + timedelta(seconds=5) # Verify it exists state_before = c.get_orchestration_state(id) @@ -390,7 +390,7 @@ def test_list_instance_ids_paginates_terminal_instances(backend): failed_id = c.schedule_new_orchestration(failing_orchestrator) failed_state = c.wait_for_orchestration_completion(failed_id, timeout=30) - window_start = datetime.now(timezone.utc) - timedelta(minutes=1) + window_start = datetime.now(UTC) - timedelta(minutes=1) first_page = c.list_instance_ids( runtime_status=[client.OrchestrationStatus.COMPLETED, client.OrchestrationStatus.FAILED], completed_time_from=window_start, @@ -545,14 +545,14 @@ def simple_entity(ctx: entities.EntityContext, input): try: with TaskHubGrpcClient(host_address=HOST) as c: # Get current time - before_creation = datetime.now(timezone.utc) - timedelta(seconds=5) + before_creation = datetime.now(UTC) - timedelta(seconds=5) # Create entity entity_id = entities.EntityInstanceId("simple_entity", "timeTestEntity") c.signal_entity(entity_id, "set", input="test_value") time.sleep(3) # Wait for signal to be processed - after_creation = datetime.now(timezone.utc) + timedelta(seconds=5) + after_creation = datetime.now(UTC) + timedelta(seconds=5) # Query by time range query = client.EntityQuery( diff --git a/tests/durabletask/test_client.py b/tests/durabletask/test_client.py index ff9feada..ae53f163 100644 --- a/tests/durabletask/test_client.py +++ b/tests/durabletask/test_client.py @@ -7,7 +7,7 @@ import grpc import pytest from collections import namedtuple -from datetime import datetime, timezone +from datetime import UTC, datetime from unittest.mock import ANY, AsyncMock, MagicMock, call, patch from google.protobuf import wrappers_pb2 @@ -1338,7 +1338,7 @@ def test_list_instance_ids_returns_page(): history_client = TaskHubGrpcClient() page = history_client.list_instance_ids( runtime_status=[OrchestrationStatus.COMPLETED], - completed_time_from=datetime(2025, 1, 1, tzinfo=timezone.utc), + completed_time_from=datetime(2025, 1, 1, tzinfo=UTC), page_size=2, continuation_token='prev', ) diff --git a/tests/durabletask/test_delayed_signal_normalization.py b/tests/durabletask/test_delayed_signal_normalization.py index c2d1cbfc..53fe9580 100644 --- a/tests/durabletask/test_delayed_signal_normalization.py +++ b/tests/durabletask/test_delayed_signal_normalization.py @@ -5,7 +5,7 @@ timer lifecycle (regression coverage for PR #160 review feedback).""" import time -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta import durabletask.internal.orchestrator_service_pb2 as pb from durabletask.entities import EntityInstanceId @@ -20,7 +20,7 @@ class TestSignalTimeNormalization: def test_build_signal_entity_req_naive_matches_aware(self): entity_id = EntityInstanceId("Recorder", "k") naive = datetime(2030, 1, 1, 12, 0, 0) - aware = datetime(2030, 1, 1, 12, 0, 0, tzinfo=timezone.utc) + aware = datetime(2030, 1, 1, 12, 0, 0, tzinfo=UTC) req_naive = build_signal_entity_req(entity_id, "ping", signal_time=naive) req_aware = build_signal_entity_req(entity_id, "ping", signal_time=aware) @@ -30,7 +30,7 @@ def test_build_signal_entity_req_naive_matches_aware(self): def test_new_signal_entity_action_naive_matches_aware(self): entity_id = EntityInstanceId("Recorder", "k") naive = datetime(2030, 1, 1, 12, 0, 0) - aware = datetime(2030, 1, 1, 12, 0, 0, tzinfo=timezone.utc) + aware = datetime(2030, 1, 1, 12, 0, 0, tzinfo=UTC) a_naive = helpers.new_signal_entity_action(1, entity_id, "ping", None, "r1", naive) a_aware = helpers.new_signal_entity_action(1, entity_id, "ping", None, "r2", aware) @@ -51,7 +51,7 @@ def test_delayed_operation_does_not_fire_into_reset_backend(self): ) # Schedule the op to fire shortly in the future, then reset before it # fires so the timer wakes into a reset (new-generation) backend. - fire_at = datetime.now(timezone.utc) + timedelta(seconds=0.3) + fire_at = datetime.now(UTC) + timedelta(seconds=0.3) backend._schedule_delayed_entity_operation( # pyright: ignore[reportPrivateUsage] entity_id, event, fire_at) backend.reset() diff --git a/tests/durabletask/test_history_serialization.py b/tests/durabletask/test_history_serialization.py index 82c8a753..8119c470 100644 --- a/tests/durabletask/test_history_serialization.py +++ b/tests/durabletask/test_history_serialization.py @@ -15,16 +15,16 @@ import json from collections import defaultdict from dataclasses import asdict, dataclass -from datetime import datetime, timezone +from datetime import UTC, datetime from typing import Any, NamedTuple, cast import pytest from durabletask import history, task -_TS = datetime(2025, 1, 2, 3, 4, 5, 678901, tzinfo=timezone.utc) +_TS = datetime(2025, 1, 2, 3, 4, 5, 678901, tzinfo=UTC) _NAIVE_TS = datetime(2024, 12, 31, 23, 59, 58) -_FIRE_AT = datetime(2025, 6, 7, 8, 9, 10, tzinfo=timezone.utc) +_FIRE_AT = datetime(2025, 6, 7, 8, 9, 10, tzinfo=UTC) def _legacy_to_serializable(value: Any) -> Any: @@ -361,8 +361,8 @@ def test_timestamps_use_isoformat(self) -> None: ) assert event.to_dict() == { 'event_id': 5, - 'timestamp': _TS.isoformat(), - 'fire_at': _FIRE_AT.isoformat(), + 'timestamp': '2025-01-02T03:04:05.678901+00:00', + 'fire_at': '2025-06-07T08:09:10+00:00', 'timer_id': 3, } diff --git a/tests/durabletask/test_orchestration_state.py b/tests/durabletask/test_orchestration_state.py index fcd7797e..6fb5633d 100644 --- a/tests/durabletask/test_orchestration_state.py +++ b/tests/durabletask/test_orchestration_state.py @@ -5,7 +5,7 @@ import json from dataclasses import dataclass -from datetime import datetime, timezone +from datetime import UTC, datetime from durabletask.client import OrchestrationState, OrchestrationStatus @@ -29,7 +29,7 @@ def from_json(cls, data: dict) -> "Money": def _state(serialized_input=None, serialized_output=None, serialized_custom_status=None) -> OrchestrationState: - now = datetime.now(timezone.utc) + now = datetime.now(UTC) return OrchestrationState( instance_id="abc123", name="test", diff --git a/tests/durabletask/test_tracing.py b/tests/durabletask/test_tracing.py index 441727aa..5c75e25a 100644 --- a/tests/durabletask/test_tracing.py +++ b/tests/durabletask/test_tracing.py @@ -5,7 +5,7 @@ import json import logging -from datetime import datetime, timezone +from datetime import UTC, datetime from unittest.mock import AsyncMock, MagicMock, patch import pytest @@ -441,7 +441,7 @@ def test_emit_orchestration_span_without_otel(self): def test_emit_timer_span_without_otel(self): """emit_timer_span is a no-op when OTel is unavailable.""" with patch.object(tracing, '_OTEL_AVAILABLE', False): - tracing.emit_timer_span("orch", "inst1", 1, datetime.now(timezone.utc)) + tracing.emit_timer_span("orch", "inst1", 1, datetime.now(UTC)) def test_start_create_orchestration_span_without_otel(self): """start_create_orchestration_span yields None when OTel unavailable.""" @@ -779,7 +779,7 @@ class TestTimerSpan: """Tests for emit_timer_span.""" def test_emits_internal_span(self, otel_setup: InMemorySpanExporter): - fire_at = datetime(2025, 1, 1, 12, 0, 0, tzinfo=timezone.utc) + fire_at = datetime(2025, 1, 1, 12, 0, 0, tzinfo=UTC) tracing.emit_timer_span("MyOrch", "inst-1", 5, fire_at) spans = otel_setup.get_finished_spans() @@ -794,7 +794,7 @@ def test_emits_internal_span(self, otel_setup: InMemorySpanExporter): def test_backdated_start_time(self, otel_setup: InMemorySpanExporter): """Timer span should cover the full wait period when scheduled_time_ns is set.""" - fire_at = datetime(2025, 1, 1, 12, 0, 0, tzinfo=timezone.utc) + fire_at = datetime(2025, 1, 1, 12, 0, 0, tzinfo=UTC) created_ns = 1704067200_000_000_000 # 2024-01-01T00:00:00Z tracing.emit_timer_span( "MyOrch", "inst-1", 5, fire_at, scheduled_time_ns=created_ns, @@ -813,7 +813,7 @@ def test_parent_trace_context(self, otel_setup: InMemorySpanExporter): traceParent=traceparent, spanID="b7ad6b7169203331", ) - fire_at = datetime(2025, 1, 1, 12, 0, 0, tzinfo=timezone.utc) + fire_at = datetime(2025, 1, 1, 12, 0, 0, tzinfo=UTC) tracing.emit_timer_span( "MyOrch", "inst-1", 5, fire_at, parent_trace_context=parent_ctx,