33"""
44
55import logging
6+ from datetime import datetime
67from typing import Annotated
78
89from fastapi import (
3132)
3233logger = logging .getLogger (__name__ )
3334
35+
3436# Read methods
3537
3638
39+ def normalize_datetime_to_server_timezone (dt : datetime ) -> datetime :
40+ """
41+ Normalize datetime to server timezone for consistent comparison.
42+
43+ If the datetime has timezone info, convert to server native timezone.
44+ If it's naive (no timezone), assume it's already in server timezone.
45+
46+ Args:
47+ dt: Input datetime (may be timezone-aware or naive)
48+
49+ Returns:
50+ Datetime in server native timezone (timezone-aware)
51+ """
52+ if dt .tzinfo is not None :
53+ # Timezone-aware: convert to server native timezone
54+ return dt .astimezone (None )
55+ else :
56+ # Naive datetime: assume it's already in server timezone
57+ return dt
58+
59+
3760@event_router .get ("/search" , responses = {404 : {"description" : "Conversation not found" }})
3861async def search_conversation_events (
3962 page_id : Annotated [
@@ -54,12 +77,33 @@ async def search_conversation_events(
5477 EventSortOrder ,
5578 Query (title = "Sort order for events" ),
5679 ] = EventSortOrder .TIMESTAMP ,
80+ timestamp__gte : Annotated [
81+ datetime | None ,
82+ Query (title = "Filter: event timestamp >= this datetime" ),
83+ ] = None ,
84+ timestamp__lt : Annotated [
85+ datetime | None ,
86+ Query (title = "Filter: event timestamp < this datetime" ),
87+ ] = None ,
5788 event_service : EventService = Depends (get_event_service ),
5889) -> EventPage :
5990 """Search / List local events"""
6091 assert limit > 0
6192 assert limit <= 100
62- return await event_service .search_events (page_id , limit , kind , sort_order )
93+
94+ # Normalize timezone-aware datetimes to server timezone
95+ normalized_gte = (
96+ normalize_datetime_to_server_timezone (timestamp__gte )
97+ if timestamp__gte
98+ else None
99+ )
100+ normalized_lt = (
101+ normalize_datetime_to_server_timezone (timestamp__lt ) if timestamp__lt else None
102+ )
103+
104+ return await event_service .search_events (
105+ page_id , limit , kind , sort_order , normalized_gte , normalized_lt
106+ )
63107
64108
65109@event_router .get ("/count" , responses = {404 : {"description" : "Conversation not found" }})
@@ -70,10 +114,29 @@ async def count_conversation_events(
70114 title = "Optional filter by event kind/type (e.g., ActionEvent, MessageEvent)"
71115 ),
72116 ] = None ,
117+ timestamp__gte : Annotated [
118+ datetime | None ,
119+ Query (title = "Filter: event timestamp >= this datetime" ),
120+ ] = None ,
121+ timestamp__lt : Annotated [
122+ datetime | None ,
123+ Query (title = "Filter: event timestamp < this datetime" ),
124+ ] = None ,
73125 event_service : EventService = Depends (get_event_service ),
74126) -> int :
75127 """Count local events matching the given filters"""
76- count = await event_service .count_events (kind )
128+ # Normalize timezone-aware datetimes to server timezone
129+ normalized_gte = (
130+ normalize_datetime_to_server_timezone (timestamp__gte )
131+ if timestamp__gte
132+ else None
133+ )
134+ normalized_lt = (
135+ normalize_datetime_to_server_timezone (timestamp__lt ) if timestamp__lt else None
136+ )
137+
138+ count = await event_service .count_events (kind , normalized_gte , normalized_lt )
139+
77140 return count
78141
79142
0 commit comments