Skip to content

Commit e4d494b

Browse files
jbrockmendeljreback
authored andcommitted
catch OutOfBoundsDatetime in just one place (#24049)
1 parent 7a885ee commit e4d494b

File tree

1 file changed

+121
-146
lines changed

1 file changed

+121
-146
lines changed

pandas/_libs/tslib.pyx

+121-146
Original file line numberDiff line numberDiff line change
@@ -527,182 +527,157 @@ cpdef array_to_datetime(ndarray[object] values, str errors='raise',
527527
for i in range(n):
528528
val = values[i]
529529

530-
if checknull_with_nat(val):
531-
iresult[i] = NPY_NAT
530+
try:
531+
if checknull_with_nat(val):
532+
iresult[i] = NPY_NAT
532533

533-
elif PyDateTime_Check(val):
534-
seen_datetime = 1
535-
if val.tzinfo is not None:
536-
if utc_convert:
537-
try:
534+
elif PyDateTime_Check(val):
535+
seen_datetime = 1
536+
if val.tzinfo is not None:
537+
if utc_convert:
538538
_ts = convert_datetime_to_tsobject(val, None)
539539
iresult[i] = _ts.value
540-
except OutOfBoundsDatetime:
541-
if is_coerce:
542-
iresult[i] = NPY_NAT
543-
continue
544-
raise
540+
else:
541+
raise ValueError('Tz-aware datetime.datetime '
542+
'cannot be converted to '
543+
'datetime64 unless utc=True')
545544
else:
546-
raise ValueError('Tz-aware datetime.datetime cannot '
547-
'be converted to datetime64 unless '
548-
'utc=True')
549-
else:
550-
iresult[i] = pydatetime_to_dt64(val, &dts)
551-
if not PyDateTime_CheckExact(val):
552-
# i.e. a Timestamp object
553-
iresult[i] += val.nanosecond
554-
try:
545+
iresult[i] = pydatetime_to_dt64(val, &dts)
546+
if not PyDateTime_CheckExact(val):
547+
# i.e. a Timestamp object
548+
iresult[i] += val.nanosecond
555549
check_dts_bounds(&dts)
556-
except OutOfBoundsDatetime:
557-
if is_coerce:
558-
iresult[i] = NPY_NAT
559-
continue
560-
raise
561550

562-
elif PyDate_Check(val):
563-
seen_datetime = 1
564-
iresult[i] = pydate_to_dt64(val, &dts)
565-
try:
551+
elif PyDate_Check(val):
552+
seen_datetime = 1
553+
iresult[i] = pydate_to_dt64(val, &dts)
566554
check_dts_bounds(&dts)
567-
except OutOfBoundsDatetime:
568-
if is_coerce:
569-
iresult[i] = NPY_NAT
570-
continue
571-
raise
572555

573-
elif is_datetime64_object(val):
574-
seen_datetime = 1
575-
try:
556+
elif is_datetime64_object(val):
557+
seen_datetime = 1
576558
iresult[i] = get_datetime64_nanos(val)
577-
except OutOfBoundsDatetime:
578-
if is_coerce:
579-
iresult[i] = NPY_NAT
580-
continue
581-
raise
582559

583-
elif is_integer_object(val) or is_float_object(val):
584-
# these must be ns unit by-definition
585-
seen_integer = 1
560+
elif is_integer_object(val) or is_float_object(val):
561+
# these must be ns unit by-definition
562+
seen_integer = 1
586563

587-
if val != val or val == NPY_NAT:
588-
iresult[i] = NPY_NAT
589-
elif is_raise or is_ignore:
590-
iresult[i] = val
591-
else:
592-
# coerce
593-
# we now need to parse this as if unit='ns'
594-
# we can ONLY accept integers at this point
595-
# if we have previously (or in future accept
596-
# datetimes/strings, then we must coerce)
597-
try:
598-
iresult[i] = cast_from_unit(val, 'ns')
599-
except:
564+
if val != val or val == NPY_NAT:
600565
iresult[i] = NPY_NAT
566+
elif is_raise or is_ignore:
567+
iresult[i] = val
568+
else:
569+
# coerce
570+
# we now need to parse this as if unit='ns'
571+
# we can ONLY accept integers at this point
572+
# if we have previously (or in future accept
573+
# datetimes/strings, then we must coerce)
574+
try:
575+
iresult[i] = cast_from_unit(val, 'ns')
576+
except:
577+
iresult[i] = NPY_NAT
601578

602-
elif is_string_object(val):
603-
# string
604-
seen_string = 1
605-
606-
if len(val) == 0 or val in nat_strings:
607-
iresult[i] = NPY_NAT
608-
continue
609-
if isinstance(val, unicode) and PY2:
610-
val = val.encode('utf-8')
579+
elif is_string_object(val):
580+
# string
581+
seen_string = 1
611582

612-
try:
613-
_string_to_dts(val, &dts, &out_local, &out_tzoffset)
614-
except ValueError:
615-
# A ValueError at this point is a _parsing_ error
616-
# specifically _not_ OutOfBoundsDatetime
617-
if _parse_today_now(val, &iresult[i]):
583+
if len(val) == 0 or val in nat_strings:
584+
iresult[i] = NPY_NAT
618585
continue
619-
elif require_iso8601:
620-
# if requiring iso8601 strings, skip trying
621-
# other formats
622-
if is_coerce:
623-
iresult[i] = NPY_NAT
624-
continue
625-
elif is_raise:
626-
raise ValueError("time data {val} doesn't match "
627-
"format specified"
628-
.format(val=val))
629-
return values, tz_out
586+
if isinstance(val, unicode) and PY2:
587+
val = val.encode('utf-8')
630588

631589
try:
632-
py_dt = parse_datetime_string(val, dayfirst=dayfirst,
633-
yearfirst=yearfirst)
634-
except Exception:
635-
if is_coerce:
636-
iresult[i] = NPY_NAT
590+
_string_to_dts(val, &dts, &out_local, &out_tzoffset)
591+
except ValueError:
592+
# A ValueError at this point is a _parsing_ error
593+
# specifically _not_ OutOfBoundsDatetime
594+
if _parse_today_now(val, &iresult[i]):
637595
continue
638-
raise TypeError("invalid string coercion to datetime")
639-
640-
# If the dateutil parser returned tzinfo, capture it
641-
# to check if all arguments have the same tzinfo
642-
tz = py_dt.utcoffset()
643-
if tz is not None:
644-
seen_datetime_offset = 1
645-
# dateutil timezone objects cannot be hashed, so store
646-
# the UTC offsets in seconds instead
647-
out_tzoffset_vals.add(tz.total_seconds())
648-
else:
649-
# Add a marker for naive string, to track if we are
650-
# parsing mixed naive and aware strings
651-
out_tzoffset_vals.add('naive')
652-
try:
596+
elif require_iso8601:
597+
# if requiring iso8601 strings, skip trying
598+
# other formats
599+
if is_coerce:
600+
iresult[i] = NPY_NAT
601+
continue
602+
elif is_raise:
603+
raise ValueError("time data {val} doesn't "
604+
"match format specified"
605+
.format(val=val))
606+
return values, tz_out
607+
608+
try:
609+
py_dt = parse_datetime_string(val,
610+
dayfirst=dayfirst,
611+
yearfirst=yearfirst)
612+
except Exception:
613+
if is_coerce:
614+
iresult[i] = NPY_NAT
615+
continue
616+
raise TypeError("invalid string coercion to "
617+
"datetime")
618+
619+
# If the dateutil parser returned tzinfo, capture it
620+
# to check if all arguments have the same tzinfo
621+
tz = py_dt.utcoffset()
622+
if tz is not None:
623+
seen_datetime_offset = 1
624+
# dateutil timezone objects cannot be hashed, so
625+
# store the UTC offsets in seconds instead
626+
out_tzoffset_vals.add(tz.total_seconds())
627+
else:
628+
# Add a marker for naive string, to track if we are
629+
# parsing mixed naive and aware strings
630+
out_tzoffset_vals.add('naive')
631+
653632
_ts = convert_datetime_to_tsobject(py_dt, None)
654633
iresult[i] = _ts.value
655-
except OutOfBoundsDatetime:
634+
except:
635+
# TODO: What exception are we concerned with here?
656636
if is_coerce:
657637
iresult[i] = NPY_NAT
658638
continue
659639
raise
660-
except:
661-
# TODO: What exception are we concerned with here?
640+
else:
641+
# No error raised by string_to_dts, pick back up
642+
# where we left off
643+
value = dtstruct_to_dt64(&dts)
644+
if out_local == 1:
645+
seen_datetime_offset = 1
646+
# Store the out_tzoffset in seconds
647+
# since we store the total_seconds of
648+
# dateutil.tz.tzoffset objects
649+
out_tzoffset_vals.add(out_tzoffset * 60.)
650+
tz = pytz.FixedOffset(out_tzoffset)
651+
value = tz_convert_single(value, tz, UTC)
652+
else:
653+
# Add a marker for naive string, to track if we are
654+
# parsing mixed naive and aware strings
655+
out_tzoffset_vals.add('naive')
656+
iresult[i] = value
657+
check_dts_bounds(&dts)
658+
659+
else:
662660
if is_coerce:
663661
iresult[i] = NPY_NAT
664-
continue
665-
raise
666-
else:
667-
# No error raised by string_to_dts, pick back up
668-
# where we left off
669-
value = dtstruct_to_dt64(&dts)
670-
if out_local == 1:
671-
seen_datetime_offset = 1
672-
# Store the out_tzoffset in seconds
673-
# since we store the total_seconds of
674-
# dateutil.tz.tzoffset objects
675-
out_tzoffset_vals.add(out_tzoffset * 60.)
676-
tz = pytz.FixedOffset(out_tzoffset)
677-
value = tz_convert_single(value, tz, UTC)
678662
else:
679-
# Add a marker for naive string, to track if we are
680-
# parsing mixed naive and aware strings
681-
out_tzoffset_vals.add('naive')
682-
iresult[i] = value
683-
try:
684-
check_dts_bounds(&dts)
685-
except OutOfBoundsDatetime:
686-
# GH#19382 for just-barely-OutOfBounds falling back to
687-
# dateutil parser will return incorrect result because
688-
# it will ignore nanoseconds
689-
if is_coerce:
690-
iresult[i] = NPY_NAT
691-
continue
692-
elif require_iso8601:
693-
if is_raise:
694-
raise ValueError("time data {val} doesn't "
695-
"match format specified"
696-
.format(val=val))
697-
return values, tz_out
698-
raise
663+
raise TypeError("{typ} is not convertible to datetime"
664+
.format(typ=type(val)))
699665

700-
else:
666+
except OutOfBoundsDatetime:
701667
if is_coerce:
702668
iresult[i] = NPY_NAT
703-
else:
704-
raise TypeError("{typ} is not convertible to datetime"
705-
.format(typ=type(val)))
669+
continue
670+
elif require_iso8601 and is_string_object(val):
671+
# GH#19382 for just-barely-OutOfBounds falling back to
672+
# dateutil parser will return incorrect result because
673+
# it will ignore nanoseconds
674+
if is_raise:
675+
raise ValueError("time data {val} doesn't "
676+
"match format specified"
677+
.format(val=val))
678+
assert is_ignore
679+
return values, tz_out
680+
raise
706681

707682
except OutOfBoundsDatetime:
708683
if is_raise:

0 commit comments

Comments
 (0)