Skip to content

Commit 22b0ac0

Browse files
authored
Merge pull request #5415 from plotly/fix-np-date-handling-issue
Fix bug where numpy datetime contained in Python list gets converted to integer
2 parents 11d50b0 + aa30be5 commit 22b0ac0

File tree

2 files changed

+32
-2
lines changed

2 files changed

+32
-2
lines changed

_plotly_utils/basevalidators.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,20 @@ def fullmatch(regex, string, flags=0):
2222
return re.match("(?:" + regex_string + r")\Z", string, flags=flags)
2323

2424

25+
def to_non_numpy_type(np, v):
26+
"""
27+
Convert a numpy scalar value to a native Python type.
28+
Calling .item() on a datetime64[ns] value returns an integer, since
29+
Python datetimes only support microsecond precision. So we cast
30+
datetime64[ns] to datetime64[us] to ensure it remains a datetime.
31+
32+
Should only be used in contexts where we already know `np` is defined.
33+
"""
34+
if hasattr(v, "dtype") and v.dtype == np.dtype("datetime64[ns]"):
35+
return v.astype("datetime64[us]").item()
36+
return v.item()
37+
38+
2539
# Utility functions
2640
# -----------------
2741
def to_scalar_or_list(v):
@@ -35,12 +49,12 @@ def to_scalar_or_list(v):
3549
np = get_module("numpy", should_load=False)
3650
pd = get_module("pandas", should_load=False)
3751
if np and np.isscalar(v) and hasattr(v, "item"):
38-
return v.item()
52+
return to_non_numpy_type(np, v)
3953
if isinstance(v, (list, tuple)):
4054
return [to_scalar_or_list(e) for e in v]
4155
elif np and isinstance(v, np.ndarray):
4256
if v.ndim == 0:
43-
return v.item()
57+
return to_non_numpy_type(np, v)
4458
return [to_scalar_or_list(e) for e in v]
4559
elif pd and isinstance(v, (pd.Series, pd.Index)):
4660
return [to_scalar_or_list(e) for e in v]
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from datetime import datetime
2+
3+
import numpy as np
4+
5+
import plotly.graph_objs as go
6+
7+
8+
def test_np_ns_datetime():
9+
x = [np.datetime64("2025-09-26").astype("datetime64[ns]")]
10+
y = [1.23]
11+
scatter = go.Scatter(x=x, y=y, mode="markers")
12+
13+
# x value should be converted to native datetime
14+
assert isinstance(scatter.x[0], datetime)
15+
# x value should match original numpy value at microsecond precision
16+
assert x[0].astype("datetime64[us]").item() == scatter.x[0]

0 commit comments

Comments
 (0)