Fix fill_between TypeError when passed datetime or datetime64 coordinates
AL
3d ago+511+511+012345678901234567890123456789-52-52-01234567890123456789
Summary
- Problem: Calling
ax.fill_between(x, y1, y2)with temporal coordinates (datetime.datetime,datetime.date, ordatetime64) failed with:
TypeError: float() argument must be a string or a real number, not 'datetime.datetime'
This happened because fill_between passed raw inputs directly to _masked_float(x) without going through unit_converted_values(x) like other coordinate paths in Axes.
- Solution: Wrapped
xinunit_converted_values(x)before calling_masked_float(x)inAxes.fill_between:
xv, upper, lower = np.broadcast_arrays(
_masked_float(unit_converted_values(x)),
_masked_float(y1),
_masked_float(y2),
)
No modification to _masked_float itself was needed; reusing the existing coordinate conversion handles temporal normalization consistently across the module.
- Validation:
- Added regression tests in
tests/pyplot/test_axes_charts.pyverifying that Pythondatetimelists and NumPydatetime64arrays convert to expected epoch millisecond coordinates and that masked/NaT inputs break polygon segments properly. - Verified local verification gates:
make check,make check-pyplot,make check-errors, andmake news-check.
Minimal Reproducible Example
Code
from datetime import datetime, timedelta
import xy.pyplot as plt
# Generate temporal coordinates
base_time = datetime(2026, 1, 1, 0, 0)
dates = [base_time + timedelta(days=i) for i in range(5)]
y1 = [10.0, 11.5, 12.0, 14.0, 13.5]
y2 = [8.0, 9.5, 10.0, 11.5, 11.0]
fig, ax = plt.subplots()
# Previously failed with TypeError; now succeeds
ax.fill_between(dates, y1, y2, alpha=0.3, label="Band")
ax.plot(dates, y1, label="Upper")
ax.legend()
Before
Traceback (most recent call last):
File "main.py", line 13, in <module>
ax.fill_between(dates, y1, y2, alpha=0.3, label="Band")
~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "xy/pyplot/_axes.py", line 3033, in fill_between
_masked_float(x),
~~~~~~~~~~~~~^^^
File "xy/pyplot/_axes.py", line 9511, in _masked_float
return np.ma.asarray(value, dtype=np.float64).filled(np.nan)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
File "numpy/ma/core.py", line 8691, in asarray
return masked_array(a, dtype=dtype, copy=False, keep_mask=True,
subok=False, order=order)
File "numpy/ma/core.py", line 2903, in __new__
_data = np.array(data, dtype=dtype, copy=copy,
order=order, subok=True, ndmin=ndmin)
TypeError: float() argument must be a string or a real number, not 'datetime.datetime'
After
Executes cleanly and produces the correct area mark with canonical epoch millisecond coordinates.
Summary by CodeRabbit
- Bug Fixes
- Fixed
fill_betweenerrors with Pythondatetime, NumPydatetime64, and masked date sequences. - Improved datetime-axis handling for regular and masked inputs, including
NaTvalues. - Filled regions now break around masked or invalid dates instead of connecting across them.
- Fixed
- Documentation
- Clarified that temporal coordinates are converted to epoch milliseconds while datetime-axis behavior is retained.