Logs, file names, reports and APIs all need dates as strings in a specific format.
ISO 8601 is the default machine format: dt.isoformat() gives 2025-03-01T14:30:00, with an offset like +00:00 for aware values. isoformat(sep=" ", timespec="minutes") adjusts it. Prefer ISO for APIs, logs and file names: it's unambiguous and sorts correctly as text.
For human-readable output, dt.strftime(format) uses % codes:
%Y4-digit year,%mmonth 01-12,%dday 01-31%Hhour 00-23,%Ihour 01-12 with%pAM/PM,%Mminutes,%Sseconds,%fmicroseconds%A/%aweekday name,%B/%bmonth name%zUTC offset like+0100,%Zzone name%jday of the year,%%a literal percent sign
The same codes work inside f-strings: f"{dt:%Y-%m-%d}".
Names such as %A and %B depend on the current locale, which is "C" (English) unless the program calls locale.setlocale. For user-facing localised dates, a library like Babel is more reliable than switching process-wide locales.
str(dt) equals dt.isoformat(sep=" "). When formatting file names, avoid : (invalid on Windows): use %Y%m%d-%H%M%S.
Example
from datetime import UTC, datetime
dt = datetime(2025, 3, 1, 14, 5, 9, 123456)
print(dt.isoformat()) # 2025-03-01T14:05:09.123456
print(dt.isoformat(sep=" ", timespec="minutes")) # 2025-03-01 14:05
print(dt.strftime("%d/%m/%Y %H:%M")) # 01/03/2025 14:05
print(dt.strftime("%A %d %B %Y, %I:%M %p")) # Saturday 01 March 2025, 02:05 PM
print(f"report-{dt:%Y%m%d-%H%M%S}.csv") # report-20250301-140509.csv
print(f"{dt:%j}") # 060
aware = dt.replace(tzinfo=UTC, microsecond=0)
print(aware.isoformat()) # 2025-03-01T14:05:09+00:00
print(aware.strftime("%H:%M %Z (%z)")) # 14:05 UTC (+0000)Remember
isoformat()is the unambiguous, sortable machine formatstrftimeand f-string specs use the same%codes- Month and day names depend on the locale
Watch out for
- Using
%M(minutes) where%m(month) was meant, or vice versa.