@@ -467,6 +467,7 @@ def _parse_isoformat_time(tstr):
467467 hour , minute , second , microsecond = time_comps
468468 became_next_day = False
469469 error_from_components = False
470+ error_from_tz = None
470471 if (hour == 24 ):
471472 if all (time_comp == 0 for time_comp in time_comps [1 :]):
472473 hour = 0
@@ -500,14 +501,22 @@ def _parse_isoformat_time(tstr):
500501 else :
501502 tzsign = - 1 if tstr [tz_pos - 1 ] == '-' else 1
502503
503- td = timedelta (hours = tz_comps [0 ], minutes = tz_comps [1 ],
504- seconds = tz_comps [2 ], microseconds = tz_comps [3 ])
505-
506- tzi = timezone (tzsign * td )
504+ try :
505+ # This function is intended to validate datetimes, but because
506+ # we restrict time zones to ±24h, it serves here as well.
507+ _check_time_fields (hour = tz_comps [0 ], minute = tz_comps [1 ],
508+ second = tz_comps [2 ], microsecond = tz_comps [3 ],
509+ fold = 0 )
510+ except ValueError as e :
511+ error_from_tz = e
512+ else :
513+ td = timedelta (hours = tz_comps [0 ], minutes = tz_comps [1 ],
514+ seconds = tz_comps [2 ], microseconds = tz_comps [3 ])
515+ tzi = timezone (tzsign * td )
507516
508517 time_comps .append (tzi )
509518
510- return time_comps , became_next_day , error_from_components
519+ return time_comps , became_next_day , error_from_components , error_from_tz
511520
512521# tuple[int, int, int] -> tuple[int, int, int] version of date.fromisocalendar
513522def _isoweek_to_gregorian (year , week , day ):
@@ -1633,9 +1642,21 @@ def fromisoformat(cls, time_string):
16331642 time_string = time_string .removeprefix ('T' )
16341643
16351644 try :
1636- return cls (* _parse_isoformat_time (time_string )[0 ])
1637- except Exception :
1638- raise ValueError (f'Invalid isoformat string: { time_string !r} ' )
1645+ time_components , _ , error_from_components , error_from_tz = (
1646+ _parse_isoformat_time (time_string )
1647+ )
1648+ except ValueError :
1649+ raise ValueError (
1650+ f'Invalid isoformat string: { time_string !r} ' ) from None
1651+ else :
1652+ if error_from_tz :
1653+ raise error_from_tz
1654+ if error_from_components :
1655+ raise ValueError (
1656+ "Minute, second, and microsecond must be 0 when hour is 24"
1657+ )
1658+
1659+ return cls (* time_components )
16391660
16401661 def strftime (self , format ):
16411662 """Format using strftime(). The date part of the timestamp passed
@@ -1947,11 +1968,16 @@ def fromisoformat(cls, date_string):
19471968
19481969 if tstr :
19491970 try :
1950- time_components , became_next_day , error_from_components = _parse_isoformat_time (tstr )
1971+ (time_components ,
1972+ became_next_day ,
1973+ error_from_components ,
1974+ error_from_tz ) = _parse_isoformat_time (tstr )
19511975 except ValueError :
19521976 raise ValueError (
19531977 f'Invalid isoformat string: { date_string !r} ' ) from None
19541978 else :
1979+ if error_from_tz :
1980+ raise error_from_tz
19551981 if error_from_components :
19561982 raise ValueError ("minute, second, and microsecond must be 0 when hour is 24" )
19571983
0 commit comments