75
75
from pandas .core .construction import extract_array
76
76
from pandas .core .indexes .base import Index
77
77
from pandas .core .indexes .datetimes import DatetimeIndex
78
+
78
79
if TYPE_CHECKING :
79
80
from collections .abc import (
80
81
Callable ,
@@ -478,72 +479,78 @@ def _array_strptime_with_fallback(
478
479
return Index (result , dtype = result .dtype , name = name )
479
480
480
481
481
-
482
-
483
-
484
482
def _to_datetime_with_unit (arg , unit , name , utc : bool , errors : str ) -> DatetimeIndex :
485
483
"""
486
484
to_datetime specialized to the case where a 'unit' is passed.
487
- Fixes a bug where scalar out-of-bounds values were not raising
488
- an error consistently.
489
485
"""
490
- import pdb ; pdb .set_trace ()
491
-
492
- # Ensure we handle both array-likes and scalars the same way.
493
- # extract_array can return a scalar if 'arg' is scalar-like;
494
- # so we force everything into at least 1D shape.
495
486
arg = extract_array (arg , extract_numpy = True )
487
+ # Fix GH#60677
488
+ # Ensure scalar and array-like both become arrays
489
+ # (so both paths use the same code).
496
490
arg = np .atleast_1d (arg )
497
491
498
492
# GH#30050 pass an ndarray to tslib.array_to_datetime
499
493
# because it expects an ndarray argument
500
494
if isinstance (arg , IntegerArray ):
501
- # For IntegerArray, we can directly convert
502
495
arr = arg .astype (f"datetime64[{ unit } ]" )
503
496
tz_parsed = None
504
-
505
497
else :
506
- # Now we have a guaranteed ndarray
507
498
arg = np .asarray (arg )
508
499
509
500
if arg .dtype .kind in "iu" :
510
501
# Note we can't do "f" here because that could induce unwanted
511
- # rounding GH#14156, GH#20445
502
+ # rounding GH#14156, GH#20445
503
+ # Fix GH#60677
504
+ # ------------------------------------------------
505
+ # A) **Check for uint64 values above int64 max**
506
+ # so we don't accidentally wrap around to -1, etc.
507
+ # ------------------------------------------------
508
+ if arg .dtype .kind == "u" : # unsigned
509
+ above_max = arg > np .iinfo (np .int64 ).max
510
+ if above_max .any ():
511
+ if errors == "raise" :
512
+ raise OutOfBoundsDatetime (
513
+ "Cannot convert uint64 values above"
514
+ f"{ np .iinfo (np .int64 ).max } "
515
+ "to a 64-bit signed datetime64[ns]."
516
+ )
517
+ else :
518
+ # For errors != "raise" (e.g. "coerce" or "ignore"),
519
+ # we can replace out-of-range entries with NaN (-> NaT),
520
+ # then switch to the fallback object path:
521
+ arg = arg .astype (object )
522
+ arg [above_max ] = np .nan
523
+ return _to_datetime_with_unit (arg , unit , name , utc , errors )
524
+
525
+ # ------------------------------------------------
526
+ # B) Proceed with normal numeric -> datetime logic
527
+ # ------------------------------------------------
512
528
arr = arg .astype (f"datetime64[{ unit } ]" , copy = False )
513
529
try :
514
530
arr = astype_overflowsafe (arr , np .dtype ("M8[ns]" ), copy = False )
515
531
except OutOfBoundsDatetime :
516
532
if errors == "raise" :
517
533
raise
518
- # errors != "raise" => coerce to object and retry
519
534
arg = arg .astype (object )
520
535
return _to_datetime_with_unit (arg , unit , name , utc , errors )
521
536
tz_parsed = None
522
537
523
538
elif arg .dtype .kind == "f" :
524
- # Floating dtypes
525
539
with np .errstate (over = "raise" ):
526
540
try :
527
541
arr = cast_from_unit_vectorized (arg , unit = unit )
528
542
except OutOfBoundsDatetime as err :
529
543
if errors != "raise" :
530
- # coerce to object and retry
531
544
return _to_datetime_with_unit (
532
- arg .astype (object ),
533
- unit ,
534
- name ,
535
- utc ,
536
- errors ,
545
+ arg .astype (object ), unit , name , utc , errors
537
546
)
538
547
raise OutOfBoundsDatetime (
539
548
f"cannot convert input with unit '{ unit } '"
540
549
) from err
541
550
542
551
arr = arr .view ("M8[ns]" )
543
552
tz_parsed = None
544
-
545
553
else :
546
- # Fallback: treat as object dtype
547
554
arg = arg .astype (object , copy = False )
548
555
arr , tz_parsed = tslib .array_to_datetime (
549
556
arg ,
@@ -553,22 +560,21 @@ def _to_datetime_with_unit(arg, unit, name, utc: bool, errors: str) -> DatetimeI
553
560
creso = NpyDatetimeUnit .NPY_FR_ns .value ,
554
561
)
555
562
556
- # Construct a DatetimeIndex from the array
557
563
result = DatetimeIndex (arr , name = name )
558
564
559
- # May need to localize result to parsed tz or convert to UTC if requested
565
+ # GH#23758: We may still need to localize the result with tz
566
+ # GH#25546: Apply tz_parsed first (from arg), then tz (from caller)
567
+ # result will be naive but in UTC
560
568
result = result .tz_localize ("UTC" ).tz_convert (tz_parsed )
561
569
562
570
if utc :
563
571
if result .tz is None :
564
572
result = result .tz_localize ("utc" )
565
573
else :
566
574
result = result .tz_convert ("utc" )
567
-
568
575
return result
569
576
570
577
571
-
572
578
def _adjust_to_origin (arg , origin , unit ):
573
579
"""
574
580
Helper function for to_datetime.
0 commit comments