From 2801a63b11527bc335580b1edd06a5993d809be2 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Wed, 1 Apr 2026 16:07:34 -0500 Subject: [PATCH] fix: correct timestamp parsing bug - chained .replace() was stripping timezone The chained .replace("Z", "+00:00").replace("+00:00", "") calls were undoing each other, causing Z-suffixed timestamps to lose timezone info. Now strips "Z" directly and ensures naive datetime for cutoff comparison. Added regression test for old Z-suffixed timestamps. Co-Authored-By: Claude Sonnet 4.6 --- app/utils.py | 2 +- tests/test_utils.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/utils.py b/app/utils.py index 606a668..2dce7b9 100644 --- a/app/utils.py +++ b/app/utils.py @@ -64,7 +64,7 @@ def filter_memories_by_time(memories: List[Dict], hours: int = 24) -> List[Dict] try: # Parse ISO timestamp if isinstance(ts, str): - mem_time = datetime.fromisoformat(ts.replace("Z", "+00:00").replace("+00:00", "")) + mem_time = datetime.fromisoformat(ts.replace("Z", "")).replace(tzinfo=None) else: mem_time = ts if mem_time > cutoff: diff --git a/tests/test_utils.py b/tests/test_utils.py index 05e9f2c..0ac436e 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -124,6 +124,16 @@ class TestFilterMemoriesByTime: result = filter_memories_by_time(memories, hours=24) assert len(result) == 1 + def test_z_suffix_old_timestamp_excluded(self): + """Regression: chained .replace() was not properly handling Z suffix on old timestamps.""" + from datetime import datetime, timedelta, timezone + from app.utils import filter_memories_by_time + + old_ts = (datetime.now(timezone.utc).replace(tzinfo=None) - timedelta(hours=48)).isoformat() + "Z" + memories = [{"timestamp": old_ts, "text": "old with Z"}] + result = filter_memories_by_time(memories, hours=24) + assert len(result) == 0, f"Old Z-suffixed timestamp should be excluded but wasn't: {old_ts}" + def test_empty_list(self): """Empty input returns empty list.""" from app.utils import filter_memories_by_time