Skip to content

Commit bf12ab9

Browse files
committed
fix(timeline): canonicalize browser timezone aliases
1 parent 5297bbe commit bf12ab9

3 files changed

Lines changed: 39 additions & 4 deletions

File tree

‎backends/advanced/src/advanced_omi_backend/routers/modules/timeline_routes.py‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""Authenticated semantic timeline APIs."""
22

33
from datetime import date
4-
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
54

65
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Query, Response
76
from pydantic import BaseModel
@@ -17,16 +16,16 @@
1716
process_timeline_analysis_runs,
1817
request_timeline_analysis,
1918
)
19+
from advanced_omi_backend.services.timeline.timezone import canonical_timezone
2020

2121
router = APIRouter(prefix="/timeline", tags=["timeline"])
2222

2323

2424
def _validate_timezone(value: str) -> str:
2525
try:
26-
ZoneInfo(value)
27-
except ZoneInfoNotFoundError as error:
26+
return canonical_timezone(value)
27+
except ValueError as error:
2828
raise HTTPException(status_code=422, detail="Unknown IANA timezone") from error
29-
return value
3029

3130

3231
def _run_payload(run: TimelineAnalysisRun | None) -> dict | None:
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""Canonical IANA timezone handling for stable local-day identity."""
2+
3+
from functools import lru_cache
4+
from pathlib import Path
5+
from zoneinfo import TZPATH, ZoneInfo, ZoneInfoNotFoundError
6+
7+
8+
@lru_cache(maxsize=1)
9+
def _timezone_aliases() -> dict[str, str]:
10+
"""Read the host tzdb link table so browser aliases share one day key."""
11+
12+
aliases: dict[str, str] = {}
13+
for root in TZPATH:
14+
link_table = Path(root) / "tzdata.zi"
15+
if not link_table.is_file():
16+
continue
17+
for line in link_table.read_text(encoding="utf-8").splitlines():
18+
if not line.startswith("L "):
19+
continue
20+
_, canonical, alias = line.split()
21+
aliases[alias] = canonical
22+
break
23+
return aliases
24+
25+
26+
def canonical_timezone(value: str) -> str:
27+
try:
28+
ZoneInfo(value)
29+
except ZoneInfoNotFoundError as error:
30+
raise ValueError("unknown IANA timezone") from error
31+
return _timezone_aliases().get(value, value)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from advanced_omi_backend.services.timeline.timezone import canonical_timezone
2+
3+
4+
def test_legacy_browser_timezone_alias_is_canonicalized():
5+
assert canonical_timezone("Asia/Calcutta") == "Asia/Kolkata"

0 commit comments

Comments
 (0)