Skip to content

Commit 5ea5585

Browse files
committed
fix: replace gmail query dates with unix timestamps
Dates in the gmail API are interpreted as PST unless timestamps are used. Add code-based formatting to replace query dates with accurate timestamps based on the user's preferred timezone before calling the API. Addresses obot-platform/obot#518 Signed-off-by: Nick Hale <[email protected]>
1 parent ba8be10 commit 5ea5585

File tree

1 file changed

+63
-1
lines changed

1 file changed

+63
-1
lines changed

google/gmail/helpers.py

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import base64
22
import os
3+
import re
34
import gptscript
45
from filetype import guess_mime
6+
from datetime import datetime, timezone, timedelta
7+
from zoneinfo import ZoneInfo
58
from email.mime.text import MIMEText
69
from email.mime.multipart import MIMEMultipart
710
from email.mime.base import MIMEBase
@@ -104,6 +107,7 @@ def client(service_name: str, version: str):
104107
async def list_messages(service, query, max_results):
105108
all_messages = []
106109
next_page_token = None
110+
query = format_query_dates(service, query)
107111
try:
108112
while True:
109113
if next_page_token:
@@ -357,7 +361,7 @@ def format_reply_gmail_style(original_from, original_date, original_body_html):
357361

358362
if original_body_html is None:
359363
original_body_html = ""
360-
364+
361365
# Format date as "Mon, Mar 18, 2024 at 10:30 AM"
362366
formatted_date = original_date
363367
try:
@@ -373,3 +377,61 @@ def format_reply_gmail_style(original_from, original_date, original_body_html):
373377
reply_html = f'<br><br>On {formatted_date}, <b>{original_from}</b> wrote:<br>{quoted_body_html}'
374378

375379
return reply_html
380+
381+
def format_query_dates(service, query):
382+
"""
383+
Converts date strings in Gmail search queries to Unix timestamps for correct timezone handling.
384+
- before: uses beginning of day (00:00:00)
385+
- after: uses end of day (23:59:59)
386+
"""
387+
if not query:
388+
return query
389+
390+
# Get user's timezone
391+
user_tz_str = get_user_timezone(service)
392+
393+
# Use UTC if timezone is invalid
394+
try:
395+
user_tz = ZoneInfo(user_tz_str)
396+
except:
397+
user_tz = timezone.utc
398+
399+
def replace_date(match):
400+
operator, quote1, date_str, quote2 = match.groups()
401+
date_str = date_str.replace('/', '-')
402+
403+
try:
404+
# Parse date parts
405+
year, month, day = map(int, date_str.split('-'))
406+
407+
if operator == "before":
408+
# For 'before:', use beginning of day (00:00:00)
409+
dt = datetime(year, month, day, 0, 0, 0, tzinfo=user_tz)
410+
else: # after
411+
# For 'after:', use end of day (23:59:59)
412+
dt = datetime(year, month, day, 23, 59, 59, tzinfo=user_tz)
413+
414+
timestamp = int(dt.timestamp())
415+
return f"{operator}:{quote1}{timestamp}{quote2}"
416+
except:
417+
return match.group(0)
418+
419+
# Replace dates with timestamps
420+
pattern = r'(before|after):(["\']?)(\d{4}[-/]\d{1,2}[-/]\d{1,2})(["\']?)'
421+
return re.sub(pattern, replace_date, query)
422+
423+
424+
def get_obot_user_timezone():
425+
return os.getenv("OBOT_USER_TIMEZONE", "UTC").strip()
426+
427+
def get_user_timezone(service):
428+
"""Fetches the authenticated user's time zone from User's Google Calendar settings."""
429+
try:
430+
settings = service.settings().get(setting="timezone").execute()
431+
return settings.get("value", get_obot_user_timezone()) # Default to Obot's user timezone if not found
432+
except HttpError as err:
433+
if err.status_code == 403:
434+
raise Exception(f"HttpError retrieving user timezone: {err}")
435+
return "UTC"
436+
except Exception as e:
437+
return "UTC"

0 commit comments

Comments
 (0)