{"record":{"id":"a3a63b0fe023597c","repo":"python/cpython","slug":"invalid-time-separator-c","errorCode":null,"errorMessage":"Invalid time separator: %c","messagePattern":"Invalid time separator: %c","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Lib/_pydatetime.py","lineNumber":420,"sourceCode":"    time_comps = [0, 0, 0, 0]\n    pos = 0\n    for comp in range(0, 3):\n        if (len_str - pos) < 2:\n            raise ValueError(\"Incomplete time component\")\n\n        time_comps[comp] = int(tstr[pos:pos+2])\n\n        pos += 2\n        next_char = tstr[pos:pos+1]\n\n        if comp == 0:\n            has_sep = next_char == ':'\n\n        if not next_char or comp >= 2:\n            break\n\n        if has_sep and next_char != ':':\n            raise ValueError(\"Invalid time separator: %c\" % next_char)\n\n        pos += has_sep\n\n    if pos < len_str:\n        if tstr[pos] not in '.,':\n            raise ValueError(\"Invalid microsecond separator\")\n        else:\n            pos += 1\n            if not all(map(_is_ascii_digit, tstr[pos:])):\n                raise ValueError(\"Non-digit values in fraction\")\n\n            len_remainder = len_str - pos\n\n            if len_remainder >= 6:\n                to_parse = 6\n            else:\n                to_parse = len_remainder\n","sourceCodeStart":402,"sourceCodeEnd":438,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pydatetime.py#L402-L438","documentation":"In _parse_hh_mm_ss_ff, once the first component pair established whether separators are colons (has_sep from 'HH:'), every subsequent separator must also be ':' or absent. A different character (e.g. '12:30-05') raises ValueError('Invalid time separator: -') with the offending character interpolated.","triggerScenarios":"datetime.fromisoformat('2021-01-01T12:30-05'); time strings using '.', ' ' or '-' between minute and seconds; replacing 'T' with a space but also mangling colons during sanitization.","commonSituations":"Logs that use 'HH:MM-SS' formats; user-typed times with dashes; string replace('T',' ') pipelines that accidentally replace ':' elsewhere; locale time formats fed to fromisoformat.","solutions":["Use ':' between all time components (or no separators at all: '123005')","Sanitize with a targeted regex: re.sub(r'^(\\d{2}):(\\d{2})-(\\d{2})$', r'\\1:\\2:\\3', s)","Parse flexible input with datetime.strptime(s, '%H-%M-%S') or dateutil instead of fromisoformat"],"exampleFix":"// before\ndt = datetime.fromisoformat('2021-01-01T12:30-05')  # ValueError\n\n# after\nimport re\ns = re.sub(r'(?<=\\d\\d)-(\\d\\d)$', r':\\1', raw)\ndt = datetime.fromisoformat(s)","handlingStrategy":"validation","validationCode":"import re\ndef fix_time_separators(t: str) -> str:\n    return re.sub(r'(?<=\\d\\d)[^\\d:.,+\\-Zz]', ':', t)","typeGuard":"def has_valid_time_separators(t: str) -> bool:\n    import re\n    return re.fullmatch(r'\\d{2}(:?\\d{2}(:?\\d{2}([.,]\\d{1,6})?)?)?', t) is not None","tryCatchPattern":null,"preventionTips":["Use ':' exclusively between time components","Do bulk replaces carefully: replace('T',' ') then verify separators","Route nonstandard layouts through strptime with an explicit format"],"tags":["datetime","fromisoformat","time-parsing","separator","valueerror","stdlib"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}