diff --git a/src/crewai/flow/persistence/sqlite.py b/src/crewai/flow/persistence/sqlite.py index 7a6f134fa..3a4493d88 100644 --- a/src/crewai/flow/persistence/sqlite.py +++ b/src/crewai/flow/persistence/sqlite.py @@ -11,6 +11,7 @@ from typing import Any, Dict, Optional, Union from pydantic import BaseModel from crewai.flow.persistence.base import FlowPersistence +from crewai.utilities.datetime_compat import UTC class SQLiteFlowPersistence(FlowPersistence): @@ -95,7 +96,7 @@ class SQLiteFlowPersistence(FlowPersistence): """, ( flow_uuid, method_name, - datetime.utcnow().isoformat(), + datetime.now(UTC).isoformat(), json.dumps(state_dict), )) diff --git a/src/crewai/utilities/datetime_compat.py b/src/crewai/utilities/datetime_compat.py index a394e80a0..b1549a68a 100644 --- a/src/crewai/utilities/datetime_compat.py +++ b/src/crewai/utilities/datetime_compat.py @@ -1,6 +1,23 @@ -"""Compatibility module for datetime functionality across Python versions.""" +"""Compatibility module for datetime functionality across Python versions. + +This module provides timezone constants that work consistently across different +Python versions, particularly focusing on maintaining compatibility between +Python 3.10 and newer versions. + +Notes: + - In Python 3.10, datetime.UTC is not available, so we use timezone.utc + - In Python 3.11+, this provides equivalent functionality to datetime.UTC + - This implementation maintains consistent behavior across versions for + timezone-aware datetime operations + - No known limitations or edge cases between versions + - Safe to use with DST transitions and leap years + - Maintains exact timezone offset (always UTC+00:00) + +Example: + >>> from datetime import datetime + >>> from crewai.utilities.datetime_compat import UTC + >>> dt = datetime.now(UTC) # Creates timezone-aware datetime with UTC +""" from datetime import timezone -# Provide UTC timezone constant that works in Python 3.10+ -# This is equivalent to datetime.UTC in Python 3.11+ -UTC = timezone.utc +UTC = timezone.utc # Equivalent to datetime.UTC (Python 3.11+) diff --git a/tests/agent_test.py b/tests/agent_test.py index 37a20ea4c..7a5ca4f68 100644 --- a/tests/agent_test.py +++ b/tests/agent_test.py @@ -917,7 +917,7 @@ def test_tool_result_as_answer_is_the_final_answer_for_the_agent(): @pytest.mark.vcr(filter_headers=["authorization"]) def test_tool_usage_information_is_appended_to_agent(): - from datetime import datetime + from datetime import datetime, timezone from crewai.tools import BaseTool from crewai.utilities.datetime_compat import UTC @@ -930,7 +930,7 @@ def test_tool_usage_information_is_appended_to_agent(): return "Howdy!" fixed_datetime = datetime(2025, 2, 10, 12, 0, 0, tzinfo=UTC) - with patch("crewai.tools.tool_usage.datetime") as mock_datetime: + with patch("crewai.tools.tool_usage.datetime", autospec=True) as mock_datetime: mock_datetime.now.return_value = fixed_datetime mock_datetime.fromtimestamp = datetime.fromtimestamp mock_datetime.side_effect = lambda *args, **kw: datetime(*args, **kw) diff --git a/tests/utilities/test_datetime_compat.py b/tests/utilities/test_datetime_compat.py index 3edfc67c4..875cafb43 100644 --- a/tests/utilities/test_datetime_compat.py +++ b/tests/utilities/test_datetime_compat.py @@ -1,5 +1,5 @@ """Test datetime compatibility module.""" -from datetime import timezone +from datetime import datetime, timedelta, timezone from crewai.utilities.datetime_compat import UTC @@ -9,6 +9,27 @@ def test_utc_timezone_compatibility(): assert UTC == timezone.utc assert UTC.tzname(None) == "UTC" # Verify it works with datetime.now() - from datetime import datetime dt = datetime.now(UTC) assert dt.tzinfo == timezone.utc + + +def test_utc_timezone_edge_cases(): + """Test UTC timezone handling in edge cases.""" + # Test with leap year + leap_date = datetime(2024, 2, 29, tzinfo=UTC) + assert leap_date.tzinfo == timezone.utc + + # Test DST transition dates + dst_date = datetime(2024, 3, 10, 2, 0, tzinfo=UTC) # US DST start + assert dst_date.tzinfo == timezone.utc + + # Test with minimum/maximum dates + min_date = datetime.min.replace(tzinfo=UTC) + max_date = datetime.max.replace(tzinfo=UTC) + assert min_date.tzinfo == timezone.utc + assert max_date.tzinfo == timezone.utc + + # Test timezone offset calculations + dt = datetime(2024, 1, 1, tzinfo=UTC) + offset = dt.utcoffset() + assert offset == timedelta(0) # UTC should always have zero offset