A time-series chart can have accurate points and still be difficult to read when its x-axis uses dense or ambiguous dates. Matplotlib separates tick placement from tick text, so the chart can keep real date values while displaying labels suited to the reporting interval.
The object-oriented API attaches both choices to ax.xaxis. A MonthLocator selects the months that receive ticks, while a DateFormatter turns those tick values into abbreviated month and four-digit year labels.
The sample covers one calendar year with a fixed two-month tick interval. For date ranges that vary at runtime, AutoDateLocator with ConciseDateFormatter can adapt the spacing, but an explicit monthly locator keeps a recurring annual report predictable.
from datetime import datetime from pathlib import Path import matplotlib.dates as mdates import matplotlib.pyplot as plt months = [datetime(2026, month, 1) for month in range(1, 13)] tickets_closed = [42, 45, 47, 51, 55, 58, 63, 61, 66, 70, 74, 79]
fig, ax = plt.subplots(figsize=(7, 4), layout="constrained") ax.plot(months, tickets_closed, marker="o", linewidth=2) ax.set_title("Support tickets closed by month") ax.set_xlabel("Month closed") ax.set_ylabel("Tickets closed") ax.grid(True, axis="y", alpha=0.3) ax.set_xlim(months[0], months[-1])
locator = mdates.MonthLocator(bymonth=range(1, 13, 2)) ax.xaxis.set_major_locator(locator) ax.xaxis.set_major_formatter(mdates.DateFormatter("%b %Y")) ax.tick_params(axis="x", rotation=30)
%b produces the abbreviated month name, and %Y produces the four-digit year.
fig.canvas.draw() tick_labels = [label.get_text() for label in ax.get_xticklabels() if label.get_text()] output = Path("support-tickets-date-axis.png") fig.savefig(output, dpi=160) print(f"formatted tick labels: {', '.join(tick_labels)}") print(f"saved chart: {output}") plt.close(fig)
from datetime import datetime from pathlib import Path import matplotlib.dates as mdates import matplotlib.pyplot as plt months = [datetime(2026, month, 1) for month in range(1, 13)] tickets_closed = [42, 45, 47, 51, 55, 58, 63, 61, 66, 70, 74, 79] fig, ax = plt.subplots(figsize=(7, 4), layout="constrained") ax.plot(months, tickets_closed, marker="o", linewidth=2) ax.set_title("Support tickets closed by month") ax.set_xlabel("Month closed") ax.set_ylabel("Tickets closed") ax.grid(True, axis="y", alpha=0.3) ax.set_xlim(months[0], months[-1]) locator = mdates.MonthLocator(bymonth=range(1, 13, 2)) ax.xaxis.set_major_locator(locator) ax.xaxis.set_major_formatter(mdates.DateFormatter("%b %Y")) ax.tick_params(axis="x", rotation=30) fig.canvas.draw() tick_labels = [label.get_text() for label in ax.get_xticklabels() if label.get_text()] output = Path("support-tickets-date-axis.png") fig.savefig(output, dpi=160) print(f"formatted tick labels: {', '.join(tick_labels)}") print(f"saved chart: {output}") plt.close(fig)
$ python3 date_axis_format.py formatted tick labels: Jan 2026, Mar 2026, May 2026, Jul 2026, Sep 2026, Nov 2026 saved chart: support-tickets-date-axis.png
The plotted x-values remain datetime objects; the locator changes tick positions, and the formatter changes only their displayed text.