{"record":{"id":"4547d42cfad0aa67","repo":"dgtlmoon/changedetection.io","slug":"invalid-timezone-str-timezone-str-must-be-a","errorCode":null,"errorMessage":"Invalid timezone_str: '{timezone_str}'. Must be a valid timezone identifier.","messagePattern":"Invalid timezone_str: '(.+?)'\\. Must be a valid timezone identifier\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"changedetectionio/time_handler.py","lineNumber":53,"sourceCode":"    # Parse the target day of the week\n    try:\n        target_weekday = Weekday[day_of_week.capitalize()]\n    except KeyError:\n        raise ValueError(f\"Invalid day_of_week: '{day_of_week}'. Must be a valid weekday name.\")\n\n    # Parse the start time\n    try:\n        hour, minute = map(int, time_str.split(':'))\n        if not (0 <= hour <= 23 and 0 <= minute <= 59):\n            raise ValueError\n    except (ValueError, AttributeError):\n        raise ValueError(f\"Invalid time_str: '{time_str}'. Must be in 'HH:MM' format.\")\n\n    # Get the current time in the specified timezone\n    try:\n        now_tz = arrow.now(timezone_str.strip())\n    except Exception as e:\n        raise ValueError(f\"Invalid timezone_str: '{timezone_str}'. Must be a valid timezone identifier.\")\n\n    # Check if the current day matches the target day or overlaps due to duration\n    current_weekday = now_tz.weekday()\n    # Create start datetime for today in target timezone\n    start_datetime_tz = now_tz.replace(hour=hour, minute=minute, second=0, microsecond=0)\n\n    # Handle previous day's overlap\n    if target_weekday == (current_weekday - 1) % 7:\n        # Calculate start and end times for the overlap from the previous day\n        start_datetime_tz = start_datetime_tz.shift(days=-1)\n        end_datetime_tz = start_datetime_tz.shift(minutes=duration)\n        if start_datetime_tz <= now_tz <= end_datetime_tz:\n            return True\n\n    # Handle current day's range\n    if target_weekday == current_weekday:\n        end_datetime_tz = start_datetime_tz.shift(minutes=duration)\n        if start_datetime_tz <= now_tz <= end_datetime_tz:","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/dgtlmoon/changedetection.io/blob/5d9c7c6da76340597243e8163c4f2439237fa0e8/changedetectionio/time_handler.py#L35-L71","documentation":"ValueError raised by am_i_inside_time when arrow.now(timezone_str.strip()) throws — i.e. the timezone string is not a recognized identifier. The arrow library accepts tz database names ('UTC', 'Europe/Berlin', 'US/Eastern') and offsets; anything else (or a bad offset form) raises internally and is re-raised with this message.","triggerScenarios":"Calling am_i_inside_time with timezone_str like 'GMT+2' (wrong syntax), 'est' ambiguity aside — typical failures are 'PST', 'UTC+02', 'berlin', empty string after strip, or None; arrow cannot resolve them to a zone.","commonSituations":"Watch timezone settings entered as free text; users typing Windows-style zones ('Central Standard Time'); old data using abbreviations like 'CET' or 'EST' that the tz database lookup rejects; trailing quotes from JSON copy-paste.","solutions":["Use exact IANA tz database names: 'UTC', 'Europe/Berlin', 'America/New_York'","Validate with zoneinfo.available_timezones() (Python 3.9+) or a try/except around arrow.now() before saving","Offer a dropdown of valid zones in your UI instead of a free-text field","Fix existing watch configs in bulk: map common abbreviations to IANA names and re-save"],"exampleFix":"# before\nam_i_inside_time('monday', '09:00', '1', 'GMT+2')\n# after\nam_i_inside_time('monday', '09:00', '1', 'Europe/Berlin')","handlingStrategy":"validation","validationCode":"from zoneinfo import ZoneInfo, available_timezones\ntry:\n    ZoneInfo(tz.strip())\n    ok = tz.strip() in available_timezones() or True\nexcept Exception:\n    ok = False\nif not ok:\n    raise ValueError('timezone must be an IANA name like Europe/Berlin')","typeGuard":"from zoneinfo import ZoneInfo, available_timezones\n_TZS = available_timezones()\ndef is_valid_timezone(s: str) -> bool:\n    return s.strip() in _TZS","tryCatchPattern":"try:\n    inside = am_i_inside_time(day, '09:00', duration, tz)\nexcept ValueError as e:\n    return bad_request(str(e))","preventionTips":["Offer a timezone dropdown populated from zoneinfo.available_timezones()","Store IANA names ('Europe/Berlin'), never abbreviations or Windows zone names","Normalize user input with .strip() before persisting"],"tags":["timezone","schedule","arrow","value-error"],"backgroundTag":"invalid-timezone-identifier","analyzedSha":"5d9c7c6da76340597243e8163c4f2439237fa0e8","analyzedAt":"2026-08-27T19:41:16.067Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}