hour, minute, second, microsecond = time_comps
became_next_day = False
error_from_components = False
+ error_from_tz = None
if (hour == 24):
if all(time_comp == 0 for time_comp in time_comps[1:]):
hour = 0
else:
tzsign = -1 if tstr[tz_pos - 1] == '-' else 1
- td = timedelta(hours=tz_comps[0], minutes=tz_comps[1],
- seconds=tz_comps[2], microseconds=tz_comps[3])
-
- tzi = timezone(tzsign * td)
+ try:
+ # This function is intended to validate datetimes, but because
+ # we restrict time zones to ±24h, it serves here as well.
+ _check_time_fields(hour=tz_comps[0], minute=tz_comps[1],
+ second=tz_comps[2], microsecond=tz_comps[3],
+ fold=0)
+ except ValueError as e:
+ error_from_tz = e
+ else:
+ td = timedelta(hours=tz_comps[0], minutes=tz_comps[1],
+ seconds=tz_comps[2], microseconds=tz_comps[3])
+ tzi = timezone(tzsign * td)
time_comps.append(tzi)
- return time_comps, became_next_day, error_from_components
+ return time_comps, became_next_day, error_from_components, error_from_tz
# tuple[int, int, int] -> tuple[int, int, int] version of date.fromisocalendar
def _isoweek_to_gregorian(year, week, day):
time_string = time_string.removeprefix('T')
try:
- return cls(*_parse_isoformat_time(time_string)[0])
- except Exception:
- raise ValueError(f'Invalid isoformat string: {time_string!r}')
+ time_components, _, error_from_components, error_from_tz = (
+ _parse_isoformat_time(time_string)
+ )
+ except ValueError:
+ raise ValueError(
+ f'Invalid isoformat string: {time_string!r}') from None
+ else:
+ if error_from_tz:
+ raise error_from_tz
+ if error_from_components:
+ raise ValueError(
+ "Minute, second, and microsecond must be 0 when hour is 24"
+ )
+
+ return cls(*time_components)
def strftime(self, format):
"""Format using strftime(). The date part of the timestamp passed
if tstr:
try:
- time_components, became_next_day, error_from_components = _parse_isoformat_time(tstr)
+ (time_components,
+ became_next_day,
+ error_from_components,
+ error_from_tz) = _parse_isoformat_time(tstr)
except ValueError:
raise ValueError(
f'Invalid isoformat string: {date_string!r}') from None
else:
+ if error_from_tz:
+ raise error_from_tz
if error_from_components:
raise ValueError("minute, second, and microsecond must be 0 when hour is 24")
'2009-04-19T12:30:45.400 +02:30', # Space between ms and timezone (gh-130959)
'2009-04-19T12:30:45.400 ', # Trailing space (gh-130959)
'2009-04-19T12:30:45. 400', # Space before fraction (gh-130959)
+ '2009-04-19T12:30:45+00:90:00', # Time zone field out from range
+ '2009-04-19T12:30:45+00:00:90', # Time zone field out from range
+ '2009-04-19T12:30:45-00:90:00', # Time zone field out from range
+ '2009-04-19T12:30:45-00:00:90', # Time zone field out from range
]
for bad_str in bad_strs:
'12:30:45.400 +02:30', # Space between ms and timezone (gh-130959)
'12:30:45.400 ', # Trailing space (gh-130959)
'12:30:45. 400', # Space before fraction (gh-130959)
+ '24:00:00.000001', # Has non-zero microseconds on 24:00
+ '24:00:01.000000', # Has non-zero seconds on 24:00
+ '24:01:00.000000', # Has non-zero minutes on 24:00
+ '12:30:45+00:90:00', # Time zone field out from range
+ '12:30:45+00:00:90', # Time zone field out from range
]
for bad_str in bad_strs:
// -3: Failed to parse time component
// -4: Failed to parse time separator
// -5: Malformed timezone string
+ // -6: Timezone fields are not in range
const char *p = dtstr;
const char *p_end = dtstr + dtlen;
rv = parse_hh_mm_ss_ff(tzinfo_pos, p_end, &tzhour, &tzminute, &tzsecond,
tzmicrosecond);
+ // Check if timezone fields are in range
+ if (check_time_args(tzhour, tzminute, tzsecond, *tzmicrosecond, 0) < 0) {
+ return -6;
+ }
+
*tzoffset = tzsign * ((tzhour * 3600) + (tzminute * 60) + tzsecond);
*tzmicrosecond *= tzsign;
&tzoffset, &tzimicrosecond);
if (rv < 0) {
+ if (rv == -6) {
+ goto error;
+ }
goto invalid_string_error;
}
invalid_string_error:
PyErr_Format(PyExc_ValueError, "Invalid isoformat string: %R", tstr);
return NULL;
+
+error:
+ return NULL;
}
len -= (p - dt_ptr);
rv = parse_isoformat_time(p, len, &hour, &minute, &second,
µsecond, &tzoffset, &tzusec);
+ if (rv == -6) {
+ goto error;
+ }
}
if (rv < 0) {
goto invalid_string_error;