From 396737f8d7bc63b002f3552dde702e35f474500e Mon Sep 17 00:00:00 2001 From: Yi Li Date: Wed, 10 Dec 2025 02:29:32 -0500 Subject: [PATCH 1/2] BUG: Fix inconsistent datetime types between calamine and openpyxl readers (#59186) The calamine Excel reader was returning pd.Timestamp and pd.Timedelta for datetime values in mixed-type columns, while openpyxl returns standard library datetime.datetime and datetime.timedelta objects. This change modifies the calamine reader to return standard library datetime types, ensuring consistent behavior across Excel reader engines. - Return datetime.datetime as-is instead of converting to pd.Timestamp - Convert date objects to datetime to match openpyxl behavior - Return datetime.timedelta as-is instead of converting to pd.Timedelta - Remove unused pandas import - Update test helper get_exp_unit() to reflect new consistent behavior --- pandas/io/excel/_calamine.py | 11 ++++++++--- pandas/tests/io/excel/test_readers.py | 7 +------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pandas/io/excel/_calamine.py b/pandas/io/excel/_calamine.py index 0bdd2b42aad51..f4bb8fa3b11b6 100644 --- a/pandas/io/excel/_calamine.py +++ b/pandas/io/excel/_calamine.py @@ -15,7 +15,6 @@ from pandas.compat._optional import import_optional_dependency from pandas.util._decorators import doc -import pandas as pd from pandas.core.shared_docs import _shared_docs from pandas.io.excel._base import BaseExcelReader @@ -107,10 +106,16 @@ def _convert_cell(value: _CellValue) -> Scalar | NaTType | time: return val else: return value + elif isinstance(value, datetime): + # Return datetime.datetime as-is to match openpyxl behavior (GH#59186) + return value elif isinstance(value, date): - return pd.Timestamp(value) + # Convert date to datetime to match openpyxl behavior (GH#59186) + return datetime(value.year, value.month, value.day) elif isinstance(value, timedelta): - return pd.Timedelta(value) + # Return datetime.timedelta as-is to match openpyxl behavior (GH#59186) + # (previously returned pd.Timedelta) + return value elif isinstance(value, time): return value diff --git a/pandas/tests/io/excel/test_readers.py b/pandas/tests/io/excel/test_readers.py index a50b1f4b5c306..6516ffc348361 100644 --- a/pandas/tests/io/excel/test_readers.py +++ b/pandas/tests/io/excel/test_readers.py @@ -135,12 +135,7 @@ def df_ref(datapath): def get_exp_unit(read_ext: str, engine: str | None) -> str: - unit = "us" - if read_ext == ".ods" and engine == "odf": - pass - elif (read_ext == ".ods") ^ (engine == "calamine"): - unit = "s" - return unit + return "us" def adjust_expected(expected: DataFrame, read_ext: str, engine: str | None) -> None: From d59460ca2cc6e605cea6808d541879a3b8f76212 Mon Sep 17 00:00:00 2001 From: Yi Li Date: Wed, 10 Dec 2025 17:03:02 -0500 Subject: [PATCH 2/2] CLN: Combine isinstance checks per review --- pandas/io/excel/_calamine.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pandas/io/excel/_calamine.py b/pandas/io/excel/_calamine.py index f4bb8fa3b11b6..525e19e601680 100644 --- a/pandas/io/excel/_calamine.py +++ b/pandas/io/excel/_calamine.py @@ -106,16 +106,12 @@ def _convert_cell(value: _CellValue) -> Scalar | NaTType | time: return val else: return value - elif isinstance(value, datetime): - # Return datetime.datetime as-is to match openpyxl behavior (GH#59186) + elif isinstance(value, (datetime, timedelta)): + # Return as-is to match openpyxl behavior (GH#59186) return value elif isinstance(value, date): # Convert date to datetime to match openpyxl behavior (GH#59186) return datetime(value.year, value.month, value.day) - elif isinstance(value, timedelta): - # Return datetime.timedelta as-is to match openpyxl behavior (GH#59186) - # (previously returned pd.Timedelta) - return value elif isinstance(value, time): return value