{"record":{"id":"02a16e7d242cb2f5","repo":"python/cpython","slug":"minute-second-and-microsecond-must-be-0-when-hou","errorCode":null,"errorMessage":"Minute, second, and microsecond must be 0 when hour is 24","messagePattern":"Minute, second, and microsecond must be 0 when hour is 24","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Lib/_pydatetime.py","lineNumber":1656,"sourceCode":"            raise TypeError('fromisoformat: argument must be str')\n\n        # The spec actually requires that time-only ISO 8601 strings start with\n        # T, but the extended format allows this to be omitted as long as there\n        # is no ambiguity with date strings.\n        time_string = time_string.removeprefix('T')\n\n        try:\n            time_components, _, error_from_components, error_from_tz = (\n                _parse_isoformat_time(time_string)\n            )\n        except ValueError:\n            raise ValueError(\n                f'Invalid isoformat string: {time_string!r}') from None\n        else:\n            if error_from_tz:\n                raise error_from_tz\n            if error_from_components:\n                raise ValueError(\n                    \"Minute, second, and microsecond must be 0 when hour is 24\"\n                )\n\n            return cls(*time_components)\n\n    def strftime(self, format):\n        \"\"\"Format using strftime().  The date part of the timestamp passed\n        to underlying strftime should not be used.\n\n        For a list of supported format codes, see the documentation:\n            https://docs.python.org/3/library/datetime.html#format-codes\n        \"\"\"\n        # The year must be >= 1000 else Python's strftime implementation\n        # can raise a bogus exception.\n        timetuple = (1900, 1, 1,\n                     self._hour, self._minute, self._second,\n                     0, 1, -1)\n        return _wrap_strftime(self, format, timetuple)","sourceCodeStart":1638,"sourceCodeEnd":1674,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pydatetime.py#L1638-L1674","documentation":"Raised by time.fromisoformat() when the string uses hour 24 (ISO 8601 permits 24:00:00 to mean midnight at the end of a day) but minute, second, or microsecond are non-zero. The parser accepts hour 24 only for the exact end-of-day value; any additional time components make the value ambiguous/invalid, so a dedicated ValueError (instead of the generic 'Invalid isoformat string') is raised.","triggerScenarios":"time.fromisoformat('24:00:01'); time.fromisoformat('24:15:00'); time.fromisoformat('24:00:00.000001'); data feeds that compute durations as hours and emit 24:xx when a task crosses midnight.","commonSituations":"Duration-like 'HH:MM:SS' strings fed to a time parser — 25+ hours or 24:30 from overnight shifts, media timestamps, or elapsed-time reports; duration fields mis-modeled as clock times.","solutions":["Parse durations as timedelta, not time: convert '24:30:00' by splitting components into timedelta(hours=24, minutes=30)","If the value is genuinely end-of-day, use exactly '24:00:00' or normalize to time(0,0)","Validate hour<24 (or ==24 with zero remainder) before calling fromisoformat"],"exampleFix":"# before\nt = time.fromisoformat('24:30:00')  # ValueError\n\n# after\nfrom datetime import timedelta\nh, m, s = map(int, '24:30:00'.split(':'))\nd = timedelta(hours=h, minutes=m, seconds=s)","handlingStrategy":"validation","validationCode":"def parse_hour24(s: str):\n    h, rest = int(s[0:2]), s[2:]\n    if h == 24 and rest not in (':00:00', ':00:00.000000', ''):\n        raise ValueError('hour 24 requires zero minutes/seconds/microseconds')\n    return time.fromisoformat(s) if h < 24 else time(0, 0)","typeGuard":"def is_duration_like(s: str) -> bool:\n    return int(s[0:2]) >= 24","tryCatchPattern":"try:\n    t = time.fromisoformat(s)\nexcept ValueError as e:\n    if 'hour is 24' in str(e):\n        h, m, sec = map(int, s.split(':'))\n        t = timedelta(hours=h, minutes=m, seconds=sec)  # reinterpret as duration\n    else:\n        raise","preventionTips":["Model elapsed/overnight values as timedelta, never time","Validate hour <= 23 (or exactly 24:00:00) in ingestion schemas","Reject or remap 24:xx strings from external feeds before they reach parsers"],"tags":["datetime","time","fromisoformat","valueerror","duration"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}