python/cpython · error · ValueError
Missing colon in %:z before '{rest}', got '{data_string}'
Error message
Missing colon in %:z before '{rest}', got '{data_string}' What it means
Specific to the %:z (colon-separated UTC offset) directive: the regex accepts an offset like +05 without requiring the colon, so when the overall match succeeds but leaves unparsed remainder whose first character is not ':', _strptime raises this f-string ValueError pointing at the leftover text.
Source
Thrown at Lib/_strptime.py:584
if not bad_directive:
raise ValueError("stray %% in format '%s'" % format) from None
bad_directive = bad_directive.replace('\\', '', 1)
raise ValueError("'%s' is a bad directive in format '%s'" %
(bad_directive, format)) from None
_regex_cache[format] = format_regex
found = format_regex.match(data_string)
if not found:
raise ValueError("time data %r does not match format %r" %
(data_string, format))
if len(data_string) != found.end():
rest = data_string[found.end():]
# Specific check for '%:z' directive
if (
"colon_z" in found.re.groupindex
and found.group("colon_z") is not None
and rest[0] != ":"
):
raise ValueError(
f"Missing colon in %:z before '{rest}', got '{data_string}'"
)
raise ValueError("unconverted data remains: %s" % rest)
iso_year = year = None
month = day = 1
hour = minute = second = fraction = 0
tz = -1
gmtoff = None
gmtoff_fraction = 0
iso_week = week_of_year = None
week_of_year_start = None
# weekday and julian defaulted to None so as to signal need to calculate
# values
weekday = julian = None
found_dict = found.groupdict()
if locale_time.LC_alt_digits:
def parse_int(s):View on GitHub (pinned to bc6749cc3b)
Solutions
- Use %z for compact ±HHMM offsets — it accepts both '+0500' and (on most platforms) '+05:00'
- Keep %:z only when the input genuinely uses colons: '+05:00'
- Prefer datetime.fromisoformat() for full ISO 8601 timestamps including offsets
Example fix
# before
>>> datetime.strptime('+0500', '%:z')
ValueError: Missing colon in %:z before '00', got '+0500'
# after
>>> datetime.strptime('+0500', '%z')
datetime.datetime(2025, 8, 14, 0, 0, tzinfo=datetime.timezone(datetime.timedelta(hours=5))) Defensive patterns
Strategy: validation
Validate before calling
import re
def pick_offset_fmt(s: str) -> str:
return '%:z' if re.search(r'[+-]\d{2}:', s) else '%z' Try / catch
try:
dt = datetime.strptime(s, '%:z')
except ValueError as e:
if 'Missing colon' in str(e):
dt = datetime.strptime(s, '%z')
else:
raise Prevention
- Use %z by default; it accepts both compact and colon forms on modern CPython
- Reserve %:z for inputs guaranteed to carry colons
- Prefer fromisoformat() for ISO 8601 timestamps with offsets
When it happens
Trigger: datetime.strptime('+0500', '%:z') or '+05' style compact offsets parsed with %:z; also '+05:00' mis-typed as '+0500:' or any %:z input missing the colon after the hours.
Common situations: Assuming %:z and %z are interchangeable; feeding ISO 8601 basic-format offsets (±HHMM) into a format written for extended format (±HH:MM).
Related errors
- Inconsistent use of : in {found_dict[group_key]}
- timezone changed during initialization
- Day of month directive '%d' may not be used without a year d
- stray %% in format '%s'
- '%s' is a bad directive in format '%s'
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/7824e48f524f4c7a.
Report an issue: GitHub.