Fix: reminder fires at wrong time for IST (UTC+5:30) users#7066
Fix: reminder fires at wrong time for IST (UTC+5:30) users#7066priyadarshiutkarsh wants to merge 2 commits intoBasedHardware:mainfrom
Conversation
Greptile SummaryThis PR shifts timezone responsibility from the LLM to Python: the prompt now asks the model to output a naive local-time string, and
Confidence Score: 4/5Safe to merge; the core fix is correct and the one remaining gap is a P2 observability issue in the error fallback path. No P0 issues. One P2 finding (silent ZoneInfo exception with no log). The primary fix — moving UTC conversion from the LLM to deterministic Python code — is structurally sound, the indentation and stale-check logic are correct, and the fallback to UTC on ZoneInfo failure is a graceful degradation (just unobservable). backend/utils/llm/conversation_processing.py — the silent except Exception block around ZoneInfo(tz) (lines 563–567). Important Files Changed
Sequence DiagramsequenceDiagram
participant LLM
participant extract_action_items
participant ZoneInfo
Note over extract_action_items: started_at & current_time passed as UTC ISO strings
extract_action_items->>LLM: Prompt: output due_at as LOCAL time, no suffix (e.g. "2025-10-04T10:00:00")
LLM-->>extract_action_items: due_at = naive datetime (tzinfo=None)
alt tzinfo is None (expected path)
extract_action_items->>ZoneInfo: ZoneInfo(tz)
alt valid timezone
ZoneInfo-->>extract_action_items: user_tz
extract_action_items->>extract_action_items: replace(tzinfo=user_tz).astimezone(UTC)
else ZoneInfoNotFoundError (silent fallback)
extract_action_items->>extract_action_items: replace(tzinfo=UTC) ⚠️ no log
end
else tzinfo present (LLM ignored instructions)
extract_action_items->>extract_action_items: .astimezone(UTC)
end
extract_action_items->>extract_action_items: stale check: due_at < now - 1 day → clear
extract_action_items-->>extract_action_items: action_items (all due_at in UTC)
Reviews (2): Last reviewed commit: "Fix indentation in due_at timezone conve..." | Re-trigger Greptile |
| else: | ||
| action_item.due_at = action_item.due_at.astimezone(timezone.utc) | ||
| if action_item.due_at < now - timedelta(days=1): |
There was a problem hiding this comment.
else targets the wrong if, crashing on any None due date
The else on line 569 is indented to match the outer if action_item.due_at is not None: (line 562), not the inner if action_item.due_at.tzinfo is None: (line 563). This means when due_at is None the else branch fires and calls None.astimezone(timezone.utc), raising AttributeError. That exception bubbles out of the for loop and is caught by the outer except Exception as e: on line 580, causing extract_action_items to silently return [] for any conversation where even one action item has no due date — which is the common case.
Additionally, the stale-date check on line 571 is now outside the if action_item.due_at is not None: guard, so even if the else were fixed, None < now - timedelta(days=1) would raise TypeError.
The else branch should be one indent level deeper (matching the inner if tzinfo is None:) and the stale-date check should remain inside the outer guard:
if action_item.due_at is not None:
if action_item.due_at.tzinfo is None:
try:
user_tz = ZoneInfo(tz) if tz else timezone.utc
action_item.due_at = action_item.due_at.replace(tzinfo=user_tz).astimezone(timezone.utc)
except Exception:
action_item.due_at = action_item.due_at.replace(tzinfo=timezone.utc)
else:
action_item.due_at = action_item.due_at.astimezone(timezone.utc)
if action_item.due_at < now - timedelta(days=1):
logger.warning(
f'Clearing past due_at {action_item.due_at.isoformat()} for action item: {action_item.description}'
)
action_item.due_at = None| Conversation started at: {started_at} | ||
| Current time: {current_time} |
There was a problem hiding this comment.
LLM still receives UTC reference times but must reason in local time
started_at and current_time are passed as UTC ISO strings (e.g., "2025-10-03T13:25:00Z"), but the prompt now asks the LLM to resolve "today" / "tomorrow" in the user's local timezone without converting its output to UTC. The LLM still has to mentally shift REFERENCE_TIME from UTC to local time to determine the correct calendar date (e.g., 13:25 UTC = 18:55 IST, so "tomorrow" is Oct 4 IST, not Oct 3). Passing local-time reference values (pre-converted in Python) would make the LLM's job unambiguous and reduce the remaining surface for off-by-one-day errors near midnight boundaries.
The else branch and stale-date check were misindented, causing AttributeError on action items without a due date and silently returning an empty list for the entire conversation.
|
@greptile-apps review |
Fixes #7059
Root cause: The LLM was asked to convert local time → UTC (subtract 5:30h for IST), but would output "5pm" with a Z suffix instead of "11:30am UTC", causing reminders to fire 5.5h late.
Fix: Have the LLM output local time with no suffix, and do the timezone conversion deterministically using ZoneInfo.