Use client browser timezone instead of server timezone for agent tools#40
Conversation
The agent's datetime tools (get_current_datetime, datetime_math, list_events, search_events) were using the server's timezone via DateTime.now().zoneName, which is wrong when the server runs in a different timezone than the user. Similarly, create/update event tools had no timezone context, causing calendarService to fall back to UTC. Fix: the frontend sends its IANA timezone (from Intl.DateTimeFormat) with every WebSocket message. The backend threads it through chatService → createCalendarAgent → all tools that need timezone awareness. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
PR Review - Timezone Support ImplementationCritical Issues1. Security: Missing Timezone Validation (HIGH SEVERITY)Location: The client-provided timezone is accepted without validation: const timezone = typeof parsed.timezone === 'string' ? parsed.timezone : undefined;Risk: A malicious client could send arbitrary strings that may cause crashes or unexpected behavior in Luxon's Fix Required: Validate the timezone string against IANA timezone database before use: import { DateTime } from 'luxon';
function isValidTimezone(tz: string): boolean {
try {
return DateTime.local().setZone(tz).isValid;
} catch {
return false;
}
}
const timezone = typeof parsed.timezone === 'string' && isValidTimezone(parsed.timezone)
? parsed.timezone
: undefined;2. Bug: Ambiguous Datetime HandlingLocation: The regex pattern used to detect timezone-aware datetimes is fragile: if (clientTimezone && !input.datetime!.match(/[+-]\d{2}:\d{2}$|Z$/)) {
dt = DateTime.fromISO(input.datetime!, { zone: clientTimezone });
}Issues:
Recommendation: Use Luxon's timezone detection capabilities more robustly or explicitly require timezone info in the input. Code Quality Issues3. Missing Integration Tests (Per CLAUDE.md requirement)According to
4. Incomplete Error HandlingLocation: Multiple tool files If Example in const now = clientTimezone
? DateTime.now().setZone(clientTimezone)
: DateTime.now();Should be: const now = clientTimezone
? DateTime.now().setZone(clientTimezone)
: DateTime.now();
if (!now.isValid) {
return JSON.stringify({ error: 'Invalid timezone provided' });
}Minor Issues5. Inconsistent Fallback BehaviorThe PR description mentions "graceful fallback" when timezone is not provided, but the actual fallback varies:
This inconsistency could lead to confusion. SummaryMust Fix Before Merge:
Recommended: The core approach is sound, but security validation and test coverage are critical gaps that must be addressed. |
Summary
get_current_datetime,datetime_math,list_events,search_events) were using the server's timezone viaDateTime.now().zoneName, producing wrong results when server and user are in different timezonescreate_eventandupdate_eventtools had no timezone context, socalendarServicefell back toUTCfor the Google Calendar APItimeZonefieldIntl.DateTimeFormat().resolvedOptions().timeZone(IANA timezone, e.g."America/New_York") with every WebSocket messagews.ts→chatService→createCalendarAgent→ all timezone-aware toolsFiles changed
app/src/services/chatService.ts— addstimezoneto WS payloadbackend/src/ws.ts— extracts timezone from message, passes to chatServicebackend/src/services/chat.service.ts— forwards timezone to agentbackend/src/agent/agent.ts— passes timezone to all toolsbackend/src/agent/tools/getCurrentDateTimeTool.ts— uses client timezone forDateTime.now()backend/src/agent/tools/dateTimeMathTool.ts— uses client timezone for ambiguous datetimesbackend/src/agent/tools/listEventsTool.ts— converts event times to client timezonebackend/src/agent/tools/searchEventsTool.ts— converts event times to client timezonebackend/src/agent/tools/createEventTool.ts— injects client timezone into pending action payloadbackend/src/agent/tools/updateEventTool.ts— injects client timezone into pending action payloadTest plan
get_current_datetimereturns the user's local time, not the server's🤖 Generated with Claude Code