ZhuLinsen/daily_stock_analysis · error · ValueError

unknown timezone: {raw}

Error message

unknown timezone: {raw}

What it means

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.

Source

Thrown at src/notification_noise.py:122

    match = _QUIET_HOURS_RE.match(raw)
    if not match:
        raise ValueError("NOTIFICATION_QUIET_HOURS must be in HH:MM-HH:MM format")

    start_hour, start_minute, end_hour, end_minute = [int(group) for group in match.groups()]
    return start_hour * 60 + start_minute, end_hour * 60 + end_minute


def validate_notification_timezone(value: Optional[str]) -> None:
    """Validate an optional IANA timezone name."""
    raw = str(value or "").strip()
    if not raw:
        return
    if ZoneInfo is None:
        raise ValueError("zoneinfo is unavailable")
    try:
        ZoneInfo(raw)
    except ZoneInfoNotFoundError as exc:
        raise ValueError(f"unknown timezone: {raw}") from exc


def is_time_in_quiet_hours(now: datetime, quiet_hours: Tuple[int, int]) -> bool:
    """Return whether *now* falls inside a quiet-hours interval."""
    start_minute, end_minute = quiet_hours
    minute_of_day = now.hour * 60 + now.minute

    if start_minute == end_minute:
        return False
    if start_minute < end_minute:
        return start_minute <= minute_of_day < end_minute
    return minute_of_day >= start_minute or minute_of_day < end_minute


def _resolve_now(timezone_name: Optional[str], now: Optional[datetime]) -> datetime:
    raw_timezone = str(timezone_name or "").strip()
    if raw_timezone:
        if ZoneInfo is None:

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Use the exact IANA identifier, e.g. NOTIFICATION_TIMEZONE=Asia/Shanghai
  2. Fix typos and case — identifiers are case-sensitive (Europe/London, not europe/london)
  3. In minimal containers install the tzdata package (apk add tzdata / apt-get install tzdata) or add the Python tzdata package so valid names resolve
  4. Verify quickly with python -c "from zoneinfo import ZoneInfo; ZoneInfo('Asia/Shanghai')"

Example fix

# before
NOTIFICATION_TIMEZONE=CST

# after
NOTIFICATION_TIMEZONE=Asia/Shanghai
Defensive patterns

Strategy: validation

Validate before calling

try:
    from zoneinfo import ZoneInfo
except ImportError:
    ZoneInfo = None

def is_known_timezone(name: str) -> bool:
    if ZoneInfo is None:
        return False
    try:
        ZoneInfo(name.strip())
        return True
    except Exception:
        return False

Try / catch

try:
    validate_notification_timezone(os.getenv("NOTIFICATION_TIMEZONE"))
except ValueError as exc:
    log.error("Bad NOTIFICATION_TIMEZONE: %s — use IANA names like Asia/Shanghai", exc)
    raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/c70a0cfad1b2542e. Report an issue: GitHub.