Skip to content

BUG: Fix date_format and datetime_format arguments to ExcelWriter being ignored #47315

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/source/whatsnew/v1.5.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,8 @@ I/O
- Bug in :func:`read_sas` returned ``None`` rather than an empty DataFrame for SAS7BDAT files with zero rows (:issue:`18198`)
- Bug in :class:`StataWriter` where value labels were always written with default encoding (:issue:`46750`)
- Bug in :class:`StataWriterUTF8` where some valid characters were removed from variable names (:issue:`47276`)
- Bug in :class:`ExcelWriter` not respecting ``date_format`` and ``datetime_format`` (:issue:`44284`)
-

Period
^^^^^^
Expand Down
4 changes: 3 additions & 1 deletion pandas/io/excel/_odswriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions pandas/io/excel/_openpyxl.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,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,
Expand Down
2 changes: 2 additions & 0 deletions pandas/io/excel/_xlwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
40 changes: 40 additions & 0 deletions pandas/tests/io/excel/test_openpyxl.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import contextlib
from datetime import (
date,
datetime,
)
from pathlib import Path
import re

Expand All @@ -10,6 +14,7 @@
import pandas._testing as tm

from pandas.io.excel import (
ExcelFile,
ExcelWriter,
_OpenpyxlWriter,
)
Expand Down Expand Up @@ -396,3 +401,38 @@ def test_ints_spelled_with_decimals(datapath, ext):
result = pd.read_excel(path)
expected = DataFrame(range(2, 12), columns=[1])
tm.assert_frame_equal(result, expected)


def test_write_date_datetime_format(ext):
# see gh-44284
#
# Test that custom date/datetime formats are respected
# by inspecting formatting info in written file.
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
)
22 changes: 12 additions & 10 deletions pandas/tests/io/excel/test_writers.py
Original file line number Diff line number Diff line change
Expand Up @@ -652,25 +652,27 @@ def test_excel_roundtrip_datetime(self, merge_cells, tsframe, path):

tm.assert_frame_equal(tsframe, recons)

def test_excel_date_datetime_format(self, ext, path):
def test_excel_roundtrip_date_datetime_format(self, ext, path):
# see gh-4133
#
# Excel output format strings
# Test that df written with custom date/datetime format strings
# is read back the same as if written using the default formats,
# and that the values are still recognised as dates/datetimes.
df = DataFrame(
[
[date(2014, 1, 31), date(1999, 9, 24)],
[datetime(1998, 5, 26, 23, 33, 4), datetime(2014, 2, 28, 13, 5, 13)],
[date(2014, 1, 31), datetime(1998, 5, 26, 23, 33, 4)],
[date(1999, 9, 24), datetime(2014, 2, 28, 13, 5, 13)],
],
index=["DATE", "DATETIME"],
columns=["X", "Y"],
index=["X", "Y"],
columns=["DATE", "DATETIME"],
)
df_expected = DataFrame(
[
[datetime(2014, 1, 31), datetime(1999, 9, 24)],
[datetime(1998, 5, 26, 23, 33, 4), datetime(2014, 2, 28, 13, 5, 13)],
[datetime(2014, 1, 31), datetime(1998, 5, 26, 23, 33, 4)],
[datetime(1999, 9, 24), datetime(2014, 2, 28, 13, 5, 13)],
],
index=["DATE", "DATETIME"],
columns=["X", "Y"],
index=["X", "Y"],
columns=["DATE", "DATETIME"],
)

with tm.ensure_clean(ext) as filename2:
Expand Down
44 changes: 43 additions & 1 deletion pandas/tests/io/excel/test_xlsxwriter.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import contextlib
from datetime import (
date,
datetime,
)
import re
import warnings

Expand All @@ -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")

Expand Down Expand Up @@ -92,3 +99,38 @@ 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
#
# Test that custom date/datetime formats are respected
# by inspecting formatting info in written file.
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
)
50 changes: 50 additions & 0 deletions pandas/tests/io/excel/test_xlwt.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from datetime import (
date,
datetime,
)
import re

import numpy as np
Expand Down Expand Up @@ -144,3 +148,49 @@ 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
#
# Test that custom date/datetime formats are respected
# by inspecting formatting info in written file.
xlrd = pytest.importorskip("xlrd")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be needed. It's already called near the top of the file


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
)