{"record":{"id":"c70a0cfad1b2542e","repo":"ZhuLinsen/daily_stock_analysis","slug":"unknown-timezone-raw","errorCode":null,"errorMessage":"unknown timezone: {raw}","messagePattern":"unknown timezone: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/notification_noise.py","lineNumber":122,"sourceCode":"    match = _QUIET_HOURS_RE.match(raw)\n    if not match:\n        raise ValueError(\"NOTIFICATION_QUIET_HOURS must be in HH:MM-HH:MM format\")\n\n    start_hour, start_minute, end_hour, end_minute = [int(group) for group in match.groups()]\n    return start_hour * 60 + start_minute, end_hour * 60 + end_minute\n\n\ndef validate_notification_timezone(value: Optional[str]) -> None:\n    \"\"\"Validate an optional IANA timezone name.\"\"\"\n    raw = str(value or \"\").strip()\n    if not raw:\n        return\n    if ZoneInfo is None:\n        raise ValueError(\"zoneinfo is unavailable\")\n    try:\n        ZoneInfo(raw)\n    except ZoneInfoNotFoundError as exc:\n        raise ValueError(f\"unknown timezone: {raw}\") from exc\n\n\ndef is_time_in_quiet_hours(now: datetime, quiet_hours: Tuple[int, int]) -> bool:\n    \"\"\"Return whether *now* falls inside a quiet-hours interval.\"\"\"\n    start_minute, end_minute = quiet_hours\n    minute_of_day = now.hour * 60 + now.minute\n\n    if start_minute == end_minute:\n        return False\n    if start_minute < end_minute:\n        return start_minute <= minute_of_day < end_minute\n    return minute_of_day >= start_minute or minute_of_day < end_minute\n\n\ndef _resolve_now(timezone_name: Optional[str], now: Optional[datetime]) -> datetime:\n    raw_timezone = str(timezone_name or \"\").strip()\n    if raw_timezone:\n        if ZoneInfo is None:","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/notification_noise.py#L104-L140","documentation":"ValueError from validate_notification_timezone when ZoneInfo(raw) raises ZoneInfoNotFoundError — the supplied NOTIFICATION_TIMEZONE string is not a resolvable IANA timezone identifier. The name must exist in the system tzdata (e.g. Asia/Shanghai, America/New_York); abbreviations and made-up names fail.","triggerScenarios":"Setting NOTIFICATION_TIMEZONE to values like 'CST', 'GMT+8', 'Beijing', 'utc+8', or 'Asia/Shangahi' (typo). Only exact IANA keys from the tz database resolve.","commonSituations":"Using a timezone abbreviation instead of the IANA name; typos in city/region keys; a slim Docker image without the tzdata package installed so even valid names fail; case mismatches.","solutions":["Use the exact IANA identifier, e.g. NOTIFICATION_TIMEZONE=Asia/Shanghai","Fix typos and case — identifiers are case-sensitive (Europe/London, not europe/london)","In minimal containers install the tzdata package (apk add tzdata / apt-get install tzdata) or add the Python tzdata package so valid names resolve","Verify quickly with python -c \"from zoneinfo import ZoneInfo; ZoneInfo('Asia/Shanghai')\""],"exampleFix":"# before\nNOTIFICATION_TIMEZONE=CST\n\n# after\nNOTIFICATION_TIMEZONE=Asia/Shanghai","handlingStrategy":"validation","validationCode":"try:\n    from zoneinfo import ZoneInfo\nexcept ImportError:\n    ZoneInfo = None\n\ndef is_known_timezone(name: str) -> bool:\n    if ZoneInfo is None:\n        return False\n    try:\n        ZoneInfo(name.strip())\n        return True\n    except Exception:\n        return False","typeGuard":null,"tryCatchPattern":"try:\n    validate_notification_timezone(os.getenv(\"NOTIFICATION_TIMEZONE\"))\nexcept ValueError as exc:\n    log.error(\"Bad NOTIFICATION_TIMEZONE: %s — use IANA names like Asia/Shanghai\", exc)\n    raise","preventionTips":["Offer timezone names from a known IANA list in UI/config, not free text","Install tzdata in slim Docker images","Smoke-test the configured timezone at container startup"],"tags":["config","notification","timezone","validation"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}