Display calendar events in local timezone (PST)

- Query using local "today/tomorrow/week" boundaries converted to UTC
- Display event times converted to America/Los_Angeles timezone
- Headers show local dates (Dec 31, 2025 instead of Jan 1, 2026)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
OpenCode Test
2025-12-31 22:20:52 -08:00
parent f09d74dd96
commit 0f48149a22

View File

@@ -19,6 +19,10 @@ import argparse
import subprocess import subprocess
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from pathlib import Path from pathlib import Path
from zoneinfo import ZoneInfo
# Local timezone for display
LOCAL_TZ = ZoneInfo('America/Los_Angeles')
# OAuth setup - reuse Gmail credentials location # OAuth setup - reuse Gmail credentials location
CREDENTIALS_PATH = Path.home() / ".gmail-mcp" / "credentials.json" CREDENTIALS_PATH = Path.home() / ".gmail-mcp" / "credentials.json"
@@ -92,9 +96,11 @@ def fetch_events(start: datetime, end: datetime, max_results: int = 50) -> list[
start_raw = event['start'].get('dateTime', event['start'].get('date')) start_raw = event['start'].get('dateTime', event['start'].get('date'))
if 'T' in start_raw: if 'T' in start_raw:
start_dt = datetime.fromisoformat(start_raw.replace('Z', '+00:00')) start_dt = datetime.fromisoformat(start_raw.replace('Z', '+00:00'))
start_local = start_dt.astimezone(LOCAL_TZ)
all_day = False all_day = False
else: else:
start_dt = datetime.strptime(start_raw, '%Y-%m-%d') start_dt = datetime.strptime(start_raw, '%Y-%m-%d')
start_local = start_dt # All-day events don't need TZ conversion
all_day = True all_day = True
# Parse end time # Parse end time
@@ -121,7 +127,7 @@ def fetch_events(start: datetime, end: datetime, max_results: int = 50) -> list[
'id': event['id'], 'id': event['id'],
'title': event.get('summary', '(no title)'), 'title': event.get('summary', '(no title)'),
'start': start_raw, 'start': start_raw,
'start_formatted': start_dt.strftime('%I:%M %p').lstrip('0') if not all_day else 'All day', 'start_formatted': start_local.strftime('%I:%M %p').lstrip('0') if not all_day else 'All day',
'end': end_raw, 'end': end_raw,
'duration_mins': duration_mins, 'duration_mins': duration_mins,
'all_day': all_day, 'all_day': all_day,
@@ -129,8 +135,8 @@ def fetch_events(start: datetime, end: datetime, max_results: int = 50) -> list[
'meeting_link': meeting_link, 'meeting_link': meeting_link,
'attendee_count': attendee_count, 'attendee_count': attendee_count,
'description': (event.get('description', '') or '')[:100], 'description': (event.get('description', '') or '')[:100],
'date': start_dt.strftime('%Y-%m-%d'), 'date': start_local.strftime('%Y-%m-%d'),
'day_name': start_dt.strftime('%A'), 'day_name': start_local.strftime('%A'),
}) })
return events return events
@@ -197,18 +203,23 @@ def delegate(model: str, system: str, prompt: str, max_tokens: int = 4096) -> di
def cmd_today() -> dict: def cmd_today() -> dict:
"""Get today's events.""" """Get today's events."""
now = datetime.now(timezone.utc) # Use local time to determine "today"
start = now.replace(hour=0, minute=0, second=0, microsecond=0) now_local = datetime.now(LOCAL_TZ)
end = start + timedelta(days=1) start_local = now_local.replace(hour=0, minute=0, second=0, microsecond=0)
end_local = start_local + timedelta(days=1)
events = fetch_events(start, end) # Convert to UTC for API query
start_utc = start_local.astimezone(timezone.utc)
end_utc = end_local.astimezone(timezone.utc)
events = fetch_events(start_utc, end_utc)
return { return {
"tier": "haiku", "tier": "haiku",
"operation": "today", "operation": "today",
"date": start.strftime('%Y-%m-%d'), "date": start_local.strftime('%Y-%m-%d'),
"day_name": start.strftime('%A'), "day_name": start_local.strftime('%A'),
"display_date": start.strftime('%A, %b %d'), "display_date": start_local.strftime('%A, %b %d'),
"events": events, "events": events,
"count": len(events) "count": len(events)
} }
@@ -216,18 +227,23 @@ def cmd_today() -> dict:
def cmd_tomorrow() -> dict: def cmd_tomorrow() -> dict:
"""Get tomorrow's events.""" """Get tomorrow's events."""
now = datetime.now(timezone.utc) # Use local time to determine "tomorrow"
start = (now + timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0) now_local = datetime.now(LOCAL_TZ)
end = start + timedelta(days=1) start_local = (now_local + timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0)
end_local = start_local + timedelta(days=1)
events = fetch_events(start, end) # Convert to UTC for API query
start_utc = start_local.astimezone(timezone.utc)
end_utc = end_local.astimezone(timezone.utc)
events = fetch_events(start_utc, end_utc)
return { return {
"tier": "haiku", "tier": "haiku",
"operation": "tomorrow", "operation": "tomorrow",
"date": start.strftime('%Y-%m-%d'), "date": start_local.strftime('%Y-%m-%d'),
"day_name": start.strftime('%A'), "day_name": start_local.strftime('%A'),
"display_date": start.strftime('%A, %b %d'), "display_date": start_local.strftime('%A, %b %d'),
"events": events, "events": events,
"count": len(events) "count": len(events)
} }
@@ -235,16 +251,21 @@ def cmd_tomorrow() -> dict:
def cmd_week() -> dict: def cmd_week() -> dict:
"""Get next 7 days of events, grouped by day.""" """Get next 7 days of events, grouped by day."""
now = datetime.now(timezone.utc) # Use local time to determine the week
start = now.replace(hour=0, minute=0, second=0, microsecond=0) now_local = datetime.now(LOCAL_TZ)
end = start + timedelta(days=7) start_local = now_local.replace(hour=0, minute=0, second=0, microsecond=0)
end_local = start_local + timedelta(days=7)
events = fetch_events(start, end, max_results=100) # Convert to UTC for API query
start_utc = start_local.astimezone(timezone.utc)
end_utc = end_local.astimezone(timezone.utc)
# Group by date events = fetch_events(start_utc, end_utc, max_results=100)
# Group by date (using local dates)
by_day = {} by_day = {}
for i in range(7): for i in range(7):
day = start + timedelta(days=i) day = start_local + timedelta(days=i)
day_str = day.strftime('%Y-%m-%d') day_str = day.strftime('%Y-%m-%d')
by_day[day_str] = { by_day[day_str] = {
"date": day_str, "date": day_str,
@@ -261,9 +282,9 @@ def cmd_week() -> dict:
return { return {
"tier": "haiku", "tier": "haiku",
"operation": "week", "operation": "week",
"start_date": start.strftime('%Y-%m-%d'), "start_date": start_local.strftime('%Y-%m-%d'),
"end_date": end.strftime('%Y-%m-%d'), "end_date": end_local.strftime('%Y-%m-%d'),
"display_range": f"{start.strftime('%b %d')} - {end.strftime('%b %d')}", "display_range": f"{start_local.strftime('%b %d')} - {end_local.strftime('%b %d')}",
"days": list(by_day.values()), "days": list(by_day.values()),
"total_events": len(events) "total_events": len(events)
} }