diff --git a/src/database/models.py b/src/database/models.py index 03e9317..7df0899 100644 --- a/src/database/models.py +++ b/src/database/models.py @@ -1,10 +1,15 @@ """SQLModel database models for caching PIM data.""" -from datetime import datetime +from datetime import datetime, timezone from typing import Optional from sqlmodel import SQLModel, Field +def utc_now() -> datetime: + """Return timezone-aware UTC datetime.""" + return datetime.now(timezone.utc) + + class CacheMeta(SQLModel, table=True): """Generic key-value cache metadata.""" @@ -29,7 +34,7 @@ class EmailCache(SQLModel, table=True): is_flagged: bool = False snippet: Optional[str] = None full_data: Optional[str] = Field(default=None, description="JSON blob of full email data") - cached_at: datetime = Field(default_factory=datetime.utcnow) + cached_at: datetime = Field(default_factory=utc_now) class EventCache(SQLModel, table=True): @@ -43,7 +48,7 @@ class EventCache(SQLModel, table=True): start_time: Optional[datetime] = Field(default=None, index=True) end_time: Optional[datetime] = None full_data: Optional[str] = Field(default=None, description="JSON blob of full event data") - cached_at: datetime = Field(default_factory=datetime.utcnow) + cached_at: datetime = Field(default_factory=utc_now) class ContactCache(SQLModel, table=True): @@ -56,7 +61,7 @@ class ContactCache(SQLModel, table=True): display_name: Optional[str] = Field(default=None, index=True) primary_email: Optional[str] = None full_data: Optional[str] = Field(default=None, description="JSON blob of full contact data") - cached_at: datetime = Field(default_factory=datetime.utcnow) + cached_at: datetime = Field(default_factory=utc_now) class SyncState(SQLModel, table=True): @@ -86,7 +91,7 @@ class SeenEmail(SQLModel, table=True): from_address: Optional[str] = Field(default=None, description="Sender email for logging") subject: Optional[str] = Field(default=None, description="Subject for logging") email_date: Optional[datetime] = Field(default=None, description="Email date") - seen_at: datetime = Field(default_factory=datetime.utcnow) + seen_at: datetime = Field(default_factory=utc_now) notification_sent: bool = Field(default=False) notification_sent_at: Optional[datetime] = None notification_error: Optional[str] = Field( diff --git a/src/services/email_monitor.py b/src/services/email_monitor.py index a9cf872..42099c1 100644 --- a/src/services/email_monitor.py +++ b/src/services/email_monitor.py @@ -2,7 +2,7 @@ import asyncio import logging -from datetime import datetime +from datetime import datetime, timezone from typing import Optional from imapclient import IMAPClient @@ -275,7 +275,7 @@ class EmailMonitor: ) -> tuple[bool, Optional[str]]: """Send webhook notification for a new email.""" payload = EmailNotificationPayload( - timestamp=datetime.utcnow(), + timestamp=datetime.now(timezone.utc), email_id=email.id, mailbox=mailbox, from_email=email.from_address.email, @@ -353,7 +353,7 @@ class EmailMonitor: subject=email.subject, email_date=email.date, notification_sent=notification_sent, - notification_sent_at=datetime.utcnow() if notification_sent else None, + notification_sent_at=datetime.now(timezone.utc) if notification_sent else None, ) session.add(seen) await session.commit() @@ -375,7 +375,7 @@ class EmailMonitor: seen = result.scalars().first() if seen: seen.notification_sent = success - seen.notification_sent_at = datetime.utcnow() if success else None + seen.notification_sent_at = datetime.now(timezone.utc) if success else None seen.notification_error = error session.add(seen) await session.commit()