dgtlmoon/changedetection.io · warning · ValueError
Invalid timezone_str: '{timezone_str}'. Must be a valid time
Error message
Invalid timezone_str: '{timezone_str}'. Must be a valid timezone identifier. What it means
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.
Source
Thrown at changedetectionio/time_handler.py:53
# Parse the target day of the week
try:
target_weekday = Weekday[day_of_week.capitalize()]
except KeyError:
raise ValueError(f"Invalid day_of_week: '{day_of_week}'. Must be a valid weekday name.")
# Parse the start time
try:
hour, minute = map(int, time_str.split(':'))
if not (0 <= hour <= 23 and 0 <= minute <= 59):
raise ValueError
except (ValueError, AttributeError):
raise ValueError(f"Invalid time_str: '{time_str}'. Must be in 'HH:MM' format.")
# Get the current time in the specified timezone
try:
now_tz = arrow.now(timezone_str.strip())
except Exception as e:
raise ValueError(f"Invalid timezone_str: '{timezone_str}'. Must be a valid timezone identifier.")
# Check if the current day matches the target day or overlaps due to duration
current_weekday = now_tz.weekday()
# Create start datetime for today in target timezone
start_datetime_tz = now_tz.replace(hour=hour, minute=minute, second=0, microsecond=0)
# Handle previous day's overlap
if target_weekday == (current_weekday - 1) % 7:
# Calculate start and end times for the overlap from the previous day
start_datetime_tz = start_datetime_tz.shift(days=-1)
end_datetime_tz = start_datetime_tz.shift(minutes=duration)
if start_datetime_tz <= now_tz <= end_datetime_tz:
return True
# Handle current day's range
if target_weekday == current_weekday:
end_datetime_tz = start_datetime_tz.shift(minutes=duration)
if start_datetime_tz <= now_tz <= end_datetime_tz:View on GitHub (pinned to 5d9c7c6da7)
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
Example fix
# before
am_i_inside_time('monday', '09:00', '1', 'GMT+2')
# after
am_i_inside_time('monday', '09:00', '1', 'Europe/Berlin') Defensive patterns
Strategy: validation
Validate before calling
from zoneinfo import ZoneInfo, available_timezones
try:
ZoneInfo(tz.strip())
ok = tz.strip() in available_timezones() or True
except Exception:
ok = False
if not ok:
raise ValueError('timezone must be an IANA name like Europe/Berlin') Type guard
from zoneinfo import ZoneInfo, available_timezones
_TZS = available_timezones()
def is_valid_timezone(s: str) -> bool:
return s.strip() in _TZS Try / catch
try:
inside = am_i_inside_time(day, '09:00', duration, tz)
except ValueError as e:
return bad_request(str(e)) Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Invalid day_of_week: '{day_of_week}'. Must be a valid weekda
- Invalid time_str: '{time_str}'. Must be in 'HH:MM' format.
- "{}" is not a valid bool value
AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27).
Data as JSON: /api/errors/4547d42cfad0aa67.
Report an issue: GitHub.