@@ -431,6 +431,16 @@ def _parse_isoformat_time(tstr):
431
431
432
432
time_comps = _parse_hh_mm_ss_ff (timestr )
433
433
434
+ hour , minute , second , microsecond = time_comps
435
+ became_next_day = False
436
+ if (hour == 24 ):
437
+ if not all (time_comp == 0 for time_comp in time_comps [1 :]):
438
+ raise ValueError ("minute, second, and microsecond must be 0 when hour is 24" )
439
+
440
+ hour = 0
441
+ time_comps [0 ] = hour
442
+ became_next_day = True
443
+
434
444
tzi = None
435
445
if tz_pos == len_str and tstr [- 1 ] == 'Z' :
436
446
tzi = timezone .utc
@@ -456,14 +466,14 @@ def _parse_isoformat_time(tstr):
456
466
else :
457
467
tzsign = - 1 if tstr [tz_pos - 1 ] == '-' else 1
458
468
459
- td = timedelta (hours = tz_comps [ 0 ] , minutes = tz_comps [ 1 ] ,
460
- seconds = tz_comps [ 2 ] , microseconds = tz_comps [ 3 ] )
469
+ td = timedelta (hours = hour , minutes = minute ,
470
+ seconds = second , microseconds = microsecond )
461
471
462
472
tzi = timezone (tzsign * td )
463
473
464
474
time_comps .append (tzi )
465
475
466
- return time_comps
476
+ return time_comps , became_next_day
467
477
468
478
# tuple[int, int, int] -> tuple[int, int, int] version of date.fromisocalendar
469
479
def _isoweek_to_gregorian (year , week , day ):
@@ -1559,7 +1569,7 @@ def fromisoformat(cls, time_string):
1559
1569
time_string = time_string .removeprefix ('T' )
1560
1570
1561
1571
try :
1562
- return cls (* _parse_isoformat_time (time_string ))
1572
+ return cls (* _parse_isoformat_time (time_string )[ 0 ] )
1563
1573
except Exception :
1564
1574
raise ValueError (f'Invalid isoformat string: { time_string !r} ' )
1565
1575
@@ -1871,10 +1881,25 @@ def fromisoformat(cls, date_string):
1871
1881
1872
1882
if tstr :
1873
1883
try :
1874
- time_components = _parse_isoformat_time (tstr )
1884
+ time_components , became_next_day = _parse_isoformat_time (tstr )
1875
1885
except ValueError :
1876
1886
raise ValueError (
1877
1887
f'Invalid isoformat string: { date_string !r} ' ) from None
1888
+ else :
1889
+ if became_next_day :
1890
+ year , month , day = date_components
1891
+
1892
+ # Only wrap day/month when it was previously valid
1893
+ if month <= 12 and day <= (days_in_month := _days_in_month (year , month )):
1894
+ # Calculate midnight of the next day
1895
+ day += 1
1896
+ if day > days_in_month :
1897
+ day = 1
1898
+ month += 1
1899
+ if month > 12 :
1900
+ month = 1
1901
+ year += 1
1902
+ date_components = [year , month , day ]
1878
1903
else :
1879
1904
time_components = [0 , 0 , 0 , 0 , None ]
1880
1905
0 commit comments