diff --git a/doc/source/whatsnew/v1.5.0.rst b/doc/source/whatsnew/v1.5.0.rst index 090fea57872c5..39f97500f7d60 100644 --- a/doc/source/whatsnew/v1.5.0.rst +++ b/doc/source/whatsnew/v1.5.0.rst @@ -957,6 +957,8 @@ I/O - Bug in :func:`read_sas` with RLE-compressed SAS7BDAT files that contain 0x00 control bytes (:issue:`47099`) - Bug in :func:`read_parquet` with ``use_nullable_dtypes=True`` where ``float64`` dtype was returned instead of nullable ``Float64`` dtype (:issue:`45694`) - Bug in :meth:`DataFrame.to_json` where ``PeriodDtype`` would not make the serialization roundtrip when read back with :meth:`read_json` (:issue:`44720`) +- Bug in :class:`ExcelWriter` not respecting ``date_format`` and ``datetime_format`` (:issue:`44284`) +- Period ^^^^^^ diff --git a/pandas/io/excel/_odswriter.py b/pandas/io/excel/_odswriter.py index a6e125f4b9f33..e77d643867a5d 100644 --- a/pandas/io/excel/_odswriter.py +++ b/pandas/io/excel/_odswriter.py @@ -33,7 +33,7 @@ def __init__( path: FilePath | WriteExcelBuffer | ExcelWriter, engine: str | None = None, date_format: str | None = None, - datetime_format=None, + datetime_format: str | None = None, mode: str = "w", storage_options: StorageOptions = None, if_sheet_exists: str | None = None, @@ -47,6 +47,8 @@ def __init__( super().__init__( path, + date_format=date_format, + datetime_format=datetime_format, mode=mode, storage_options=storage_options, if_sheet_exists=if_sheet_exists, diff --git a/pandas/io/excel/_openpyxl.py b/pandas/io/excel/_openpyxl.py index c3cd3fbe9e853..f676f59f285e6 100644 --- a/pandas/io/excel/_openpyxl.py +++ b/pandas/io/excel/_openpyxl.py @@ -59,6 +59,8 @@ def __init__( super().__init__( path, + date_format=date_format, + datetime_format=datetime_format, mode=mode, storage_options=storage_options, if_sheet_exists=if_sheet_exists, diff --git a/pandas/io/excel/_xlwt.py b/pandas/io/excel/_xlwt.py index 234d9e72de10d..777f3a3131ee2 100644 --- a/pandas/io/excel/_xlwt.py +++ b/pandas/io/excel/_xlwt.py @@ -51,6 +51,8 @@ def __init__( super().__init__( path, + date_format=date_format, + datetime_format=datetime_format, mode=mode, storage_options=storage_options, if_sheet_exists=if_sheet_exists, diff --git a/pandas/tests/io/excel/test_openpyxl.py b/pandas/tests/io/excel/test_openpyxl.py index 3b122c8572751..7a9bf91a3b66e 100644 --- a/pandas/tests/io/excel/test_openpyxl.py +++ b/pandas/tests/io/excel/test_openpyxl.py @@ -1,4 +1,8 @@ import contextlib +from datetime import ( + date, + datetime, +) from pathlib import Path import re @@ -10,6 +14,7 @@ import pandas._testing as tm from pandas.io.excel import ( + ExcelFile, ExcelWriter, _OpenpyxlWriter, ) @@ -410,3 +415,35 @@ def test_read_multiindex_header_no_index_names(datapath, ext): index=pd.MultiIndex.from_tuples([("A", "AA", "AAA"), ("A", "BB", "BBB")]), ) tm.assert_frame_equal(result, expected) + + +def test_write_date_datetime_format(ext): + # see gh-44284 + df = DataFrame( + [ + [date(2014, 1, 31), datetime(1998, 5, 26, 23, 33, 4)], + [date(1999, 9, 24), datetime(2014, 2, 28, 13, 5, 13)], + ], + index=["X", "Y"], + columns=["DATE", "DATETIME"], + ) + + with tm.ensure_clean(ext) as f: + with ExcelWriter( + f, + engine="xlsxwriter", + date_format="DD.MM.YYYY", + datetime_format="DD.MM.YYYY HH-MM-SS", + ) as writer: + df.to_excel(writer, "test1") + + with ExcelFile(f) as reader: + ws = reader.book["test1"] + + date_cells = (ws["B2"], ws["B3"]) + assert all(cell.number_format == "DD.MM.YYYY" for cell in date_cells) + + datetime_cells = (ws["C2"], ws["C3"]) + assert all( + cell.number_format == "DD.MM.YYYY HH-MM-SS" for cell in datetime_cells + ) diff --git a/pandas/tests/io/excel/test_xlsxwriter.py b/pandas/tests/io/excel/test_xlsxwriter.py index 82d47a13aefbc..40bba7451820c 100644 --- a/pandas/tests/io/excel/test_xlsxwriter.py +++ b/pandas/tests/io/excel/test_xlsxwriter.py @@ -1,4 +1,8 @@ import contextlib +from datetime import ( + date, + datetime, +) import re import warnings @@ -7,7 +11,10 @@ from pandas import DataFrame import pandas._testing as tm -from pandas.io.excel import ExcelWriter +from pandas.io.excel import ( + ExcelFile, + ExcelWriter, +) xlsxwriter = pytest.importorskip("xlsxwriter") @@ -92,3 +99,35 @@ def test_book_and_sheets_consistent(ext): assert writer.sheets == {} sheet = writer.book.add_worksheet("test_name") assert writer.sheets == {"test_name": sheet} + + +def test_write_date_datetime_format(ext): + # see gh-44284 + df = DataFrame( + [ + [date(2014, 1, 31), datetime(1998, 5, 26, 23, 33, 4)], + [date(1999, 9, 24), datetime(2014, 2, 28, 13, 5, 13)], + ], + index=["X", "Y"], + columns=["DATE", "DATETIME"], + ) + + with tm.ensure_clean(ext) as f: + with ExcelWriter( + f, + engine="xlsxwriter", + date_format="DD.MM.YYYY", + datetime_format="DD.MM.YYYY HH-MM-SS", + ) as writer: + df.to_excel(writer, "test1") + + with ExcelFile(f) as reader: + ws = reader.book["test1"] + + date_cells = (ws["B2"], ws["B3"]) + assert all(cell.number_format == "DD.MM.YYYY" for cell in date_cells) + + datetime_cells = (ws["C2"], ws["C3"]) + assert all( + cell.number_format == "DD.MM.YYYY HH-MM-SS" for cell in datetime_cells + ) diff --git a/pandas/tests/io/excel/test_xlwt.py b/pandas/tests/io/excel/test_xlwt.py index 3aa405eb1e275..39964266d1de7 100644 --- a/pandas/tests/io/excel/test_xlwt.py +++ b/pandas/tests/io/excel/test_xlwt.py @@ -1,3 +1,7 @@ +from datetime import ( + date, + datetime, +) import re import numpy as np @@ -144,3 +148,46 @@ def test_deprecated_attr(ext, attr): msg = f"{attr} is not part of the public API" with tm.assert_produces_warning(FutureWarning, match=msg): getattr(writer, attr) + + +def test_write_date_datetime_format(ext): + # see gh-44284 + xlrd = pytest.importorskip("xlrd") + + df = DataFrame( + [ + [date(2014, 1, 31), datetime(1998, 5, 26, 23, 33, 4)], + [date(1999, 9, 24), datetime(2014, 2, 28, 13, 5, 13)], + ], + index=["X", "Y"], + columns=["DATE", "DATETIME"], + ) + + with tm.ensure_clean(ext) as f: + with ExcelWriter( + f, + engine="xlwt", + date_format="DD.MM.YYYY", + datetime_format="DD.MM.YYYY HH-MM-SS", + ) as writer: + df.to_excel(writer, "test1") + + # formatting_info defaults to False + # so have to use xlrd.open_workbook() directly + with xlrd.open_workbook(f, formatting_info=True) as book: + sh = book["test1"] + xf_list = book.xf_list + format_map = book.format_map + + date_cells = (sh[1, 1], sh[2, 1]) + assert all( + format_map[xf_list[cell.xf_index].format_key].format_str == "DD.MM.YYYY" + for cell in date_cells + ) + + datetime_cells = (sh[1, 2], sh[2, 2]) + assert all( + format_map[xf_list[cell.xf_index].format_key].format_str + == "DD.MM.YYYY HH-MM-SS" + for cell in datetime_cells + )