diff --git a/pandas/core/frame.py b/pandas/core/frame.py index afcd4d014316e..95e9aa42a1e35 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -205,6 +205,8 @@ ) import pandas.plotting +from pandas.core.tools.datetimes import to_datetime + if TYPE_CHECKING: import datetime @@ -11632,6 +11634,27 @@ def all( result = result.__finalize__(self, method="all") return result + def convert_date_na_columns(self): + """ + Convert all columns containing only dates and nulls in a DataFrame to datetime objects. + """ + for col in self.columns: + if self.is_date_or_null_column(self[col]): + try: + self[col] = to_datetime(self[col]) + except (ValueError, TypeError): + continue + else: + continue + + def is_date_or_null_column(self, column: Series) -> bool: + """Check if a pandas Series contains only datetime.date objects or nulls.""" + for value in column: + from datetime import date + if not (isna(value) or isinstance(value, date)): + return False + return True + @doc(make_doc("min", ndim=2)) def min( self, @@ -11640,6 +11663,7 @@ def min( numeric_only: bool = False, **kwargs, ): + self.convert_date_na_columns() result = super().min(axis, skipna, numeric_only, **kwargs) if isinstance(result, Series): result = result.__finalize__(self, method="min") @@ -11653,6 +11677,7 @@ def max( numeric_only: bool = False, **kwargs, ): + self.convert_date_na_columns() result = super().max(axis, skipna, numeric_only, **kwargs) if isinstance(result, Series): result = result.__finalize__(self, method="max") diff --git a/pandas/tests/frame/methods/test_minmax.py b/pandas/tests/frame/methods/test_minmax.py new file mode 100644 index 0000000000000..706cdc6f6287a --- /dev/null +++ b/pandas/tests/frame/methods/test_minmax.py @@ -0,0 +1,64 @@ +import pytest +import pandas as pd +import numpy as np +import datetime + + +def test_min_with_mixed_nan_and_dates(): + """Test min() with mix of NaN and datetime.date objects""" + data = { + "dates": [ + np.nan, + np.nan, + datetime.date(2025, 1, 3), + datetime.date(2025, 1, 4), + ], + } + df = pd.DataFrame(data) + result = df.min() + expected = pd.Series({"dates": datetime.date(2025, 1, 3)}, dtype="datetime64[ns]") + pd.testing.assert_series_equal(result, expected) + + +def test_min_with_all_nan(): + """Test min() with all NaN values (should return NaN)""" + data = { + "dates": [ + np.nan, + np.nan, + np.nan, + ], + } + df = pd.DataFrame(data) + result = df.min() + expected = pd.Series({"dates": np.nan}, dtype="datetime64[ns]") + pd.testing.assert_series_equal(result, expected) + + +def test_min_with_only_dates(): + """Test min() with only datetime.date objects (no NaN)""" + data = { + "dates": [ + datetime.date(2025, 1, 3), + datetime.date(2025, 1, 4), + datetime.date(2025, 1, 1), + ], + } + df = pd.DataFrame(data) + result = df.min() + expected = pd.Series({"dates": datetime.date(2025, 1, 1)}, dtype="datetime64[ns]") + pd.testing.assert_series_equal(result, expected) + + +def test_min_with_mixed_types_error(): + """Test min() with incompatible types (should raise TypeError)""" + data = { + "mixed": [ + np.nan, + "string", + datetime.date(2025, 1, 3), + ], + } + df = pd.DataFrame(data) + with pytest.raises(TypeError): + df.max()