{"record":{"id":"7824e48f524f4c7a","repo":"python/cpython","slug":"missing-colon-in-z-before-rest-got-data-s","errorCode":null,"errorMessage":"Missing colon in %:z before '{rest}', got '{data_string}'","messagePattern":"Missing colon in %:z before '(.+?)', got '(.+?)'","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Lib/_strptime.py","lineNumber":584,"sourceCode":"                if not bad_directive:\n                    raise ValueError(\"stray %% in format '%s'\" % format) from None\n                bad_directive = bad_directive.replace('\\\\', '', 1)\n                raise ValueError(\"'%s' is a bad directive in format '%s'\" %\n                                    (bad_directive, format)) from None\n            _regex_cache[format] = format_regex\n    found = format_regex.match(data_string)\n    if not found:\n        raise ValueError(\"time data %r does not match format %r\" %\n                         (data_string, format))\n    if len(data_string) != found.end():\n        rest = data_string[found.end():]\n        # Specific check for '%:z' directive\n        if (\n            \"colon_z\" in found.re.groupindex\n            and found.group(\"colon_z\") is not None\n            and rest[0] != \":\"\n        ):\n            raise ValueError(\n                f\"Missing colon in %:z before '{rest}', got '{data_string}'\"\n            )\n        raise ValueError(\"unconverted data remains: %s\" % rest)\n\n    iso_year = year = None\n    month = day = 1\n    hour = minute = second = fraction = 0\n    tz = -1\n    gmtoff = None\n    gmtoff_fraction = 0\n    iso_week = week_of_year = None\n    week_of_year_start = None\n    # weekday and julian defaulted to None so as to signal need to calculate\n    # values\n    weekday = julian = None\n    found_dict = found.groupdict()\n    if locale_time.LC_alt_digits:\n        def parse_int(s):","sourceCodeStart":566,"sourceCodeEnd":602,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_strptime.py#L566-L602","documentation":"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.","triggerScenarios":"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.","commonSituations":"Assuming %:z and %z are interchangeable; feeding ISO 8601 basic-format offsets (±HHMM) into a format written for extended format (±HH:MM).","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"],"exampleFix":"# before\n>>> datetime.strptime('+0500', '%:z')\nValueError: Missing colon in %:z before '00', got '+0500'\n\n# after\n>>> datetime.strptime('+0500', '%z')\ndatetime.datetime(2025, 8, 14, 0, 0, tzinfo=datetime.timezone(datetime.timedelta(hours=5)))","handlingStrategy":"validation","validationCode":"import re\n\ndef pick_offset_fmt(s: str) -> str:\n    return '%:z' if re.search(r'[+-]\\d{2}:', s) else '%z'","typeGuard":null,"tryCatchPattern":"try:\n    dt = datetime.strptime(s, '%:z')\nexcept ValueError as e:\n    if 'Missing colon' in str(e):\n        dt = datetime.strptime(s, '%z')\n    else:\n        raise","preventionTips":["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"],"tags":["strptime","timezone","utc-offset","format-string","cpython"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}