Skip to content

Commit 34565ef

Browse files
authored
BUG: Period incorrectly allowing np.timedelta64('NaT') (#44507)
1 parent 52c9181 commit 34565ef

File tree

4 files changed

+48
-5
lines changed

4 files changed

+48
-5
lines changed

doc/source/whatsnew/v1.4.0.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,7 @@ Period
653653
^^^^^^
654654
- Bug in adding a :class:`Period` object to a ``np.timedelta64`` object incorrectly raising ``TypeError`` (:issue:`44182`)
655655
- Bug in :meth:`PeriodIndex.to_timestamp` when the index has ``freq="B"`` inferring ``freq="D"`` for its result instead of ``freq="B"`` (:issue:`44105`)
656+
- Bug in :class:`Period` constructor incorrectly allowing ``np.timedelta64("NaT")`` (:issue:`44507`)
656657
-
657658

658659
Plotting

pandas/_libs/tslibs/period.pyx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ from pandas._libs.tslibs.nattype cimport (
104104
_nat_scalar_rules,
105105
c_NaT as NaT,
106106
c_nat_strings as nat_strings,
107-
is_null_datetimelike,
107+
checknull_with_nat,
108108
)
109109
from pandas._libs.tslibs.offsets cimport (
110110
BaseOffset,
@@ -1459,10 +1459,13 @@ def extract_ordinals(ndarray[object] values, freq) -> np.ndarray:
14591459
for i in range(n):
14601460
p = values[i]
14611461

1462-
if is_null_datetimelike(p):
1462+
if checknull_with_nat(p):
14631463
ordinals[i] = NPY_NAT
14641464
elif util.is_integer_object(p):
1465-
raise TypeError(p)
1465+
if p == NPY_NAT:
1466+
ordinals[i] = NPY_NAT
1467+
else:
1468+
raise TypeError(p)
14661469
else:
14671470
try:
14681471
ordinals[i] = p.ordinal
@@ -2473,14 +2476,17 @@ class Period(_Period):
24732476
converted = other.asfreq(freq)
24742477
ordinal = converted.ordinal
24752478

2476-
elif is_null_datetimelike(value) or (isinstance(value, str) and
2477-
value in nat_strings):
2479+
elif checknull_with_nat(value) or (isinstance(value, str) and
2480+
value in nat_strings):
24782481
# explicit str check is necessary to avoid raising incorrectly
24792482
# if we have a non-hashable value.
24802483
ordinal = NPY_NAT
24812484

24822485
elif isinstance(value, str) or util.is_integer_object(value):
24832486
if util.is_integer_object(value):
2487+
if value == NPY_NAT:
2488+
value = "NaT"
2489+
24842490
value = str(value)
24852491
value = value.upper()
24862492
dt, reso = parse_time_string(value, freq)

pandas/tests/arrays/period/test_constructors.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,28 @@ def test_from_sequence_disallows_i8():
9696

9797
with pytest.raises(TypeError, match=msg):
9898
PeriodArray._from_sequence(list(arr.asi8), dtype=arr.dtype)
99+
100+
101+
def test_from_td64nat_sequence_raises():
102+
# GH#44507
103+
td = pd.NaT.to_numpy("m8[ns]")
104+
105+
dtype = pd.period_range("2005-01-01", periods=3, freq="D").dtype
106+
107+
arr = np.array([None], dtype=object)
108+
arr[0] = td
109+
110+
msg = "Value must be Period, string, integer, or datetime"
111+
with pytest.raises(ValueError, match=msg):
112+
PeriodArray._from_sequence(arr, dtype=dtype)
113+
114+
with pytest.raises(ValueError, match=msg):
115+
pd.PeriodIndex(arr, dtype=dtype)
116+
with pytest.raises(ValueError, match=msg):
117+
pd.Index(arr, dtype=dtype)
118+
with pytest.raises(ValueError, match=msg):
119+
pd.array(arr, dtype=dtype)
120+
with pytest.raises(ValueError, match=msg):
121+
pd.Series(arr, dtype=dtype)
122+
with pytest.raises(ValueError, match=msg):
123+
pd.DataFrame(arr, dtype=dtype)

pandas/tests/scalar/period/test_period.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@
4040

4141

4242
class TestPeriodConstruction:
43+
def test_from_td64nat_raises(self):
44+
# GH#44507
45+
td = NaT.to_numpy("m8[ns]")
46+
47+
msg = "Value must be Period, string, integer, or datetime"
48+
with pytest.raises(ValueError, match=msg):
49+
Period(td)
50+
51+
with pytest.raises(ValueError, match=msg):
52+
Period(td, freq="D")
53+
4354
def test_construction(self):
4455
i1 = Period("1/1/2005", freq="M")
4556
i2 = Period("Jan 2005")

0 commit comments

Comments
 (0)