python/cpython · error · ValueError
hour must be in 0..23, not {hour}
Error message
hour must be in 0..23, not {hour} What it means
Raised by _check_time_fields() when the hour argument to time()/datetime() is not in 0..23 (after __index__ coercion). Python time objects use a 24-hour representation, so 24 or negative hours are invalid — unlike some locales/APIs where '24:00' is a valid end-of-day marker.
Source
Thrown at Lib/_pydatetime.py:587
year = _index(year)
month = _index(month)
day = _index(day)
if not MINYEAR <= year <= MAXYEAR:
raise ValueError(f"year must be in {MINYEAR}..{MAXYEAR}, not {year}")
if not 1 <= month <= 12:
raise ValueError(f"month must be in 1..12, not {month}")
dim = _days_in_month(year, month)
if not 1 <= day <= dim:
raise ValueError(f"day {day} must be in range 1..{dim} for month {month} in year {year}")
return year, month, day
def _check_time_fields(hour, minute, second, microsecond, fold):
hour = _index(hour)
minute = _index(minute)
second = _index(second)
microsecond = _index(microsecond)
if not 0 <= hour <= 23:
raise ValueError(f"hour must be in 0..23, not {hour}")
if not 0 <= minute <= 59:
raise ValueError(f"minute must be in 0..59, not {minute}")
if not 0 <= second <= 59:
raise ValueError(f"second must be in 0..59, not {second}")
if not 0 <= microsecond <= 999999:
raise ValueError(f"microsecond must be in 0..999999, not {microsecond}")
if fold not in (0, 1):
raise ValueError(f"fold must be either 0 or 1, not {fold}")
return hour, minute, second, microsecond, fold
def _check_tzinfo_arg(tz):
if tz is not None and not isinstance(tz, tzinfo):
raise TypeError(
"tzinfo argument must be None or of a tzinfo subclass, "
f"not {type(tz).__name__!r}"
)
def _divide_and_round(a, b):View on GitHub (pinned to bc6749cc3b)
Solutions
- Normalize '24:00' to time(0, 0) of the next day when parsing
- Apply % 24 and carry to days when doing hour arithmetic
- Validate 0 <= hour <= 23 on parsed input before construction
Example fix
// before t = time(24, 0) # ValueError // after d = datetime(y, m, d, 0, 0) + timedelta(days=1) # represents 24:00 as next midnight
Defensive patterns
Strategy: validation
Validate before calling
if not 0 <= hour <= 23:
raise ValueError('hour must be 0..23') Type guard
def valid_hour(h) -> bool:
return 0 <= h <= 23 Prevention
- Map ISO 8601 '24:00' to next-day midnight instead of time(24, 0)
- Use % 24 with day carry after hour arithmetic
When it happens
Trigger: time(24, 0, 0); datetime(2024, 1, 1, 25); converting '24:00' from ISO 8601 strings that permit it; hour arithmetic like (h + duration) % 25 bugs.
Common situations: Parsing ISO 8601 durations/intervals that legally use 24:00; spreadsheet or business-software exports with 24:00; forgetting to modulo hour arithmetic after adding offsets.
Related errors
- minute must be in 0..59, not {minute}
- second must be in 0..59, not {second}
- microsecond must be in 0..999999, not {microsecond}
- month must be in 1..12, not {month}
- day {day} must be in range 1..{dim} for month {month} in yea
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/33a342e0267596c4.
Report an issue: GitHub.