diff --git a/pandas/_libs/tslib.pyx b/pandas/_libs/tslib.pyx index b1b38505b9476..9578fed2d1fd9 100644 --- a/pandas/_libs/tslib.pyx +++ b/pandas/_libs/tslib.pyx @@ -447,10 +447,10 @@ cpdef array_to_datetime( raise ValueError('Tz-aware datetime.datetime ' 'cannot be converted to ' 'datetime64 unless utc=True') + elif isinstance(val, _Timestamp): + iresult[i] = val.value else: iresult[i] = pydatetime_to_dt64(val, &dts) - if isinstance(val, _Timestamp): - iresult[i] += val.nanosecond check_dts_bounds(&dts) elif PyDate_Check(val): diff --git a/pandas/_libs/tslibs/conversion.pyx b/pandas/_libs/tslibs/conversion.pyx index 3b52b4d499694..676ff7deb950f 100644 --- a/pandas/_libs/tslibs/conversion.pyx +++ b/pandas/_libs/tslibs/conversion.pyx @@ -497,7 +497,7 @@ cdef _TSObject convert_datetime_to_tsobject(datetime ts, tzinfo tz, obj.value -= int(offset.total_seconds() * 1e9) if isinstance(ts, ABCTimestamp): - obj.value += ts.nanosecond + obj.value += ts.nanosecond obj.dts.ps = ts.nanosecond * 1000 if nanos: diff --git a/pandas/_libs/tslibs/src/datetime/np_datetime.c b/pandas/_libs/tslibs/src/datetime/np_datetime.c index 8eb995dee645b..9ad2ead5f919f 100644 --- a/pandas/_libs/tslibs/src/datetime/np_datetime.c +++ b/pandas/_libs/tslibs/src/datetime/np_datetime.c @@ -32,7 +32,7 @@ This file is derived from NumPy 1.7. See NUMPY_LICENSE.txt #endif // PyInt_AsLong const npy_datetimestruct _NS_MIN_DTS = { - 1677, 9, 21, 0, 12, 43, 145225, 0, 0}; + 1677, 9, 21, 0, 12, 43, 145224, 193000, 0}; const npy_datetimestruct _NS_MAX_DTS = { 2262, 4, 11, 23, 47, 16, 854775, 807000, 0}; diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index df4677a242758..bbf762158b89e 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -1533,11 +1533,7 @@ Timestamp.daysinmonth = Timestamp.days_in_month # Add the min and max fields at the class level cdef int64_t _NS_UPPER_BOUND = np.iinfo(np.int64).max -# the smallest value we could actually represent is -# INT64_MIN + 1 == -9223372036854775807 -# but to allow overflow free conversion with a microsecond resolution -# use the smallest value with a 0 nanosecond unit (0s in last 3 digits) -cdef int64_t _NS_LOWER_BOUND = -9_223_372_036_854_775_000 +cdef int64_t _NS_LOWER_BOUND = NPY_NAT + 1 # Resolution is in nanoseconds Timestamp.min = Timestamp(_NS_LOWER_BOUND) diff --git a/pandas/tests/arrays/test_datetimelike.py b/pandas/tests/arrays/test_datetimelike.py index 36a52e5802396..81d04fe7f3649 100644 --- a/pandas/tests/arrays/test_datetimelike.py +++ b/pandas/tests/arrays/test_datetimelike.py @@ -8,7 +8,7 @@ from pandas.compat.numpy import np_version_under1p18 import pandas as pd -from pandas import DatetimeIndex, Index, Period, PeriodIndex, TimedeltaIndex +from pandas import DatetimeIndex, Period, PeriodIndex, TimedeltaIndex import pandas._testing as tm from pandas.core.arrays import DatetimeArray, PandasArray, PeriodArray, TimedeltaArray @@ -330,6 +330,19 @@ def test_searchsorted_castable_strings(self, arr1d, box, request): ): arr.searchsorted([str(arr[1]), "baz"]) + def test_getitem_near_implementation_bounds(self): + # We only check tz-naive for DTA bc the bounds are slightly different + # for other tzs + i8vals = np.asarray([NaT.value + n for n in range(1, 5)], dtype="i8") + arr = self.array_cls(i8vals, freq="ns") + arr[0] # should not raise OutOfBoundsDatetime + + index = pd.Index(arr) + index[0] # should not raise OutOfBoundsDatetime + + ser = pd.Series(arr) + ser[0] # should not raise OutOfBoundsDatetime + def test_getitem_2d(self, arr1d): # 2d slicing on a 1D array expected = type(arr1d)(arr1d._data[:, np.newaxis], dtype=arr1d.dtype) @@ -403,7 +416,7 @@ def test_setitem(self): @pytest.mark.parametrize( "box", [ - Index, + pd.Index, pd.Series, np.array, list, diff --git a/pandas/tests/scalar/timestamp/test_constructors.py b/pandas/tests/scalar/timestamp/test_constructors.py index 583110cc4ba70..654c7d502610e 100644 --- a/pandas/tests/scalar/timestamp/test_constructors.py +++ b/pandas/tests/scalar/timestamp/test_constructors.py @@ -372,7 +372,7 @@ def test_out_of_bounds_value(self): # By definition we can't go out of bounds in [ns], so we # convert the datetime64s to [us] so we can go out of bounds - min_ts_us = np.datetime64(Timestamp.min).astype("M8[us]") + min_ts_us = np.datetime64(Timestamp.min).astype("M8[us]") + one_us max_ts_us = np.datetime64(Timestamp.max).astype("M8[us]") # No error for the min/max datetimes diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index 36d1b0911c909..579440a58abad 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -529,17 +529,20 @@ def test_to_datetime_bijective(self): # by going from nanoseconds to microseconds. exp_warning = None if Timestamp.max.nanosecond == 0 else UserWarning with tm.assert_produces_warning(exp_warning, check_stacklevel=False): - assert ( - Timestamp(Timestamp.max.to_pydatetime()).value / 1000 - == Timestamp.max.value / 1000 - ) + pydt_max = Timestamp.max.to_pydatetime() + + assert Timestamp(pydt_max).value / 1000 == Timestamp.max.value / 1000 exp_warning = None if Timestamp.min.nanosecond == 0 else UserWarning with tm.assert_produces_warning(exp_warning, check_stacklevel=False): - assert ( - Timestamp(Timestamp.min.to_pydatetime()).value / 1000 - == Timestamp.min.value / 1000 - ) + pydt_min = Timestamp.min.to_pydatetime() + + # The next assertion can be enabled once GH#39221 is merged + # assert pydt_min < Timestamp.min # this is bc nanos are dropped + tdus = timedelta(microseconds=1) + assert pydt_min + tdus > Timestamp.min + + assert Timestamp(pydt_min + tdus).value / 1000 == Timestamp.min.value / 1000 def test_to_period_tz_warning(self): # GH#21333 make sure a warning is issued when timezone