Timezone Management
Relevant source files
The following files were used as context for generating this wiki page:
Timezone Management in OpenPoke is a cross-stack system designed to ensure that all user interactions, email processing, and scheduled triggers are handled using the user's local time. The system automatically detects the browser's timezone on initial load, persists it to the backend, and uses it to normalize timestamps across the multi-agent architecture.
This system is critical for features like the Reminder Triggers and Conversation Summarization, where relative time phrases (e.g., "tomorrow at 9 AM") must be converted into explicit UTC-anchored timestamps that reflect the user's actual local time.
Sources: server/agents/execution_agent/system_prompt.md:76, server/services/conversation/summarization/prompt_builder.py:53
Architecture and Data Flow
The timezone management lifecycle begins in the frontend client and propagates through a Next.js API proxy to the FastAPI backend.
Detection and Persistence Flow
The system follows a specific flow for establishing the user's timezone:
- Detection: The web client uses the
Intl.DateTimeFormatAPI to detect the browser's IANA timezone string. - Proxying: The client sends this to a Next.js route (
/api/timezone), which proxies the request to the Python backend. - Storage: The backend validates the string using the
zoneinfolibrary and persists it to a local text file. - Retrieval: Subsequent backend operations (logs, triggers, agents) query the singleton
TimezoneStoreto get the current preference.
Backend Components
TimezoneStore
The TimezoneStore class is responsible for the persistence and validation of the timezone string. It uses a file-based storage mechanism located at server/data/timezone.txt.
- Validation: It ensures the provided string is a valid IANA timezone name by attempting to instantiate a
ZoneInfoobject. - Caching: It maintains an in-memory cache to avoid repeated disk reads, protected by a
threading.Lockfor thread safety.
| Method | Description | Source |
|---|---|---|
get_timezone(default) | Returns the cached timezone or a provided default (usually "UTC"). | timezone_store.py:29 |
set_timezone(name) | Validates the input, writes it to disk, and updates the cache. | timezone_store.py:33 |
_validate(name) | Checks if ZoneInfo(name) succeeds; raises ValueError if not. | timezone_store.py:53 |
Sources: server/services/timezone_store.py:14-63
Utility Functions
The backend provides utility functions to ensure consistent time handling across different services.
# server/utils/timezones.py (referenced via usage)
# server/services/triggers/utils.py:19-75
def resolve_timezone(timezone_name: Optional[str]) -> ZoneInfo:
"""Return a ZoneInfo instance, defaulting to UTC on errors."""
if timezone_name:
try:
return ZoneInfo(timezone_name)
except Exception:
return ZoneInfo("UTC")
return ZoneInfo("UTC")
Sources: server/services/triggers/utils.py:28-38
Integration with Agents and Services
Timezone management is integrated into several core services to maintain temporal accuracy:
Trigger and Reminder System
When the Execution Agent creates a trigger (reminder), it converts natural-language schedules into RRULE strings. All times within these rules are interpreted using the user's automatically detected timezone. The system uses DTSTART;TZID= metadata in the recurrence rules to ensure the scheduler fires at the correct local time even across Daylight Saving Time changes.
Sources: server/agents/execution_agent/system_prompt.md:75-76, server/services/triggers/utils.py:84-100
Conversation Logs and Summarization
Every entry in the ConversationLog and WorkingMemoryLog is timestamped using now_in_user_timezone(). During the summarization process, the "chief-of-staff memory curator" (Summarizer Agent) is explicitly instructed to convert relative time phrases like "today" or "tonight" into explicit YYYY-MM-DD timestamps based on the user's timezone.
| Context | Usage of Timezone | Source |
|---|---|---|
| Log Entry | Appends <tag timestamp="YYYY-MM-DD HH:MM:SS"> | working_memory_log.py:46 |
| Summarization | Converts "tomorrow" to explicit user-local date. | prompt_builder.py:53 |
| Email Triage | Displays "Received (user timezone)" in classification prompts. | importance_classifier.py:55 |
Sources: server/services/conversation/summarization/working_memory_log.py:43, server/services/conversation/summarization/prompt_builder.py:53, server/services/gmail/importance_classifier.py:55
Frontend Implementation
The frontend handles the initial detection and allows manual overrides via the Settings Modal.
Timezone Settings Configuration
The SettingsModal provides a text input for the timezone. While it is auto-detected, users can override it if they are traveling or want the assistant to operate in a different context.
- Component:
web/components/SettingsModal.tsx - Storage: Persisted in browser
localStorageunder the keyuser_timezone. - Auto-detection: Triggered in the root
Pagecomponent within auseEffecthook.
Sources: web/components/SettingsModal.tsx:18, web/app/page.tsx:75
Summary
Timezone Management in OpenPoke provides a unified temporal context for the multi-agent system. By automatically synchronizing the browser's timezone with the backend's persistent store, the project ensures that agents can accurately interpret time-sensitive commands, log conversations with correct local timestamps, and manage reminders across different geographic regions. This centralized management avoids "time drift" between the user's perception and the assistant's execution environment.