datetime.strptime converts a string to a datetime in Python, using a format that describes your text exactly:
from datetime import datetime
datetime.strptime("2026-09-27 14:30:00", "%Y-%m-%d %H:%M:%S")
datetime.fromisoformat("2026-09-27T14:30:00") # ISO text, no format needed
Reach for fromisoformat whenever the text is already ISO 8601. It needs no format string and it is dramatically faster.
All output below is from real runs on Python 3.12.5 on Windows.
Converting a string to datetime with strptime
Two arguments: the text, and a format describing it:
from datetime import datetime
text = "2026-09-27 14:30:00"
moment = datetime.strptime(text, "%Y-%m-%d %H:%M:%S")
print("parsed :", moment)
print("type :", type(moment).__name__)
print("year :", moment.year, " month:", moment.month, " day:", moment.day)
print("tzinfo :", moment.tzinfo, " <- naive, no timezone attached")
Output:
parsed : 2026-09-27 14:30:00
type : datetime
year : 2026 month: 9 day: 27
tzinfo : None <- naive, no timezone attached
The result is a datetime object with .year, .month, .day and the rest available as attributes.
Note tzinfo is None. strptime produces a naive datetime unless your format includes a timezone code, which matters more than it sounds.
Python strptime format codes
The format string is a literal picture of your text. Every separator has to match:
from datetime import datetime
samples = [
("2026-09-27", "%Y-%m-%d"),
("27/09/2026", "%d/%m/%Y"),
("09/27/2026", "%m/%d/%Y"),
("27 Sep 2026", "%d %b %Y"),
("27 September 2026", "%d %B %Y"),
("2026-09-27 02:30 PM", "%Y-%m-%d %I:%M %p"),
("20260927143000", "%Y%m%d%H%M%S"),
]
for text, fmt in samples:
print(f"{text:<22} {fmt:<22} -> {datetime.strptime(text, fmt)}")
Output:
2026-09-27 %Y-%m-%d -> 2026-09-27 00:00:00
27/09/2026 %d/%m/%Y -> 2026-09-27 00:00:00
09/27/2026 %m/%d/%Y -> 2026-09-27 00:00:00
27 Sep 2026 %d %b %Y -> 2026-09-27 00:00:00
27 September 2026 %d %B %Y -> 2026-09-27 00:00:00
2026-09-27 02:30 PM %Y-%m-%d %I:%M %p -> 2026-09-27 14:30:00
20260927143000 %Y%m%d%H%M%S -> 2026-09-27 14:30:00
| Code | Means | Example |
|---|---|---|
%Y | Four-digit year | 2026 |
%y | Two-digit year | 26 |
%m | Month as a number | 09 |
%b / %B | Month name, short / full | Sep / September |
%d | Day of the month | 27 |
%H / %I | Hour, 24 / 12 hour | 14 / 02 |
%p | AM or PM | PM |
%z | UTC offset | +0530 |
%d/%m/%Y and %m/%d/%Y look almost identical and mean different things. On an ambiguous date like 05/06/2026 both parse happily and only one is right.
Fixing the strptime time data does not match format error
This is the error everyone hits, and the message is more helpful than it first appears:
from datetime import datetime
# the format has to match the string EXACTLY
try:
datetime.strptime("27/09/2026", "%Y-%m-%d")
except ValueError as err:
print("ValueError:", err)
print()
try:
datetime.strptime("2026-09-27 14:30", "%Y-%m-%d %H:%M:%S")
except ValueError as err:
print("ValueError:", err)
print()
print("the message names your string AND the format, so compare them character by character")
print("the second one failed only because the seconds were missing")
Output:
ValueError: time data '27/09/2026' does not match format '%Y-%m-%d'
ValueError: time data '2026-09-27 14:30' does not match format '%Y-%m-%d %H:%M:%S'
the message names your string AND the format, so compare them character by character
the second one failed only because the seconds were missing
Put the two quoted values next to each other and read across. The mismatch is usually a separator, a missing seconds field, or a zero-padding difference.
%d expects a zero-padded day, so 7 fails where 07 works. The same goes for %m and %H.
If the text can vary in shape, catch the ValueError and try a second format, or use dateutil. That’s the same defensive habit described in handling Python errors.
Using fromisoformat for ISO strings
ISO 8601 text needs no format string at all:
from datetime import datetime
# fromisoformat needs no format string at all
for text in ("2026-09-27",
"2026-09-27T14:30:00",
"2026-09-27T14:30:00+05:30",
"2026-09-27T14:30:00Z"):
parsed = datetime.fromisoformat(text)
print(f"{text:<28} -> {parsed} tz={parsed.tzinfo}")
print()
print("the trailing Z works from Python 3.11 onward; before that it raised")
Output:
2026-09-27 -> 2026-09-27 00:00:00 tz=None
2026-09-27T14:30:00 -> 2026-09-27 14:30:00 tz=None
2026-09-27T14:30:00+05:30 -> 2026-09-27 14:30:00+05:30 tz=UTC+05:30
2026-09-27T14:30:00Z -> 2026-09-27 14:30:00+00:00 tz=UTC
the trailing Z works from Python 3.11 onward; before that it raised
Dates, datetimes and offsets all work. From Python 3.11 a trailing Z is accepted too, which covers most JSON and API responses.
On Python 3.10 and earlier, Z raises. The usual workaround is text.replace("Z", "+00:00") before parsing.
Why is fromisoformat faster than strptime?
If you are parsing thousands of timestamps, the difference is worth knowing:
import timeit
from datetime import datetime
text = "2026-09-27T14:30:00"
strp = timeit.timeit(lambda: datetime.strptime(text, "%Y-%m-%dT%H:%M:%S"), number=100_000)
iso = timeit.timeit(lambda: datetime.fromisoformat(text), number=100_000)
print(f"strptime {strp * 1000:>7.0f} ms")
print(f"fromisoformat {iso * 1000:>7.0f} ms")
print(f"\nfromisoformat is about {strp / iso:.0f}x faster for 100,000 parses")
print("strptime recompiles the format string on every single call")
Output:
strptime 480 ms
fromisoformat 11 ms
fromisoformat is about 45x faster for 100,000 parses
strptime recompiles the format string on every single call
strptime interprets the format string on every call. fromisoformat has one fixed layout to check, so it runs in C with nothing to parse.
For a handful of dates it makes no difference. For a log file with a million lines, it is the difference between half a second and twenty.
Converting a string to datetime with a timezone
Add %z to the format and the offset is parsed into the object:
from datetime import datetime, timezone
# %z parses an offset like +0530
text = "2026-09-27 14:30:00 +0530"
aware = datetime.strptime(text, "%Y-%m-%d %H:%M:%S %z")
print("parsed :", aware)
print("tzinfo :", aware.tzinfo)
print("utcoffset :", aware.utcoffset())
print("as UTC :", aware.astimezone(timezone.utc))
print()
# attaching a timezone to a naive datetime you know the zone of
naive = datetime.strptime("2026-09-27 14:30:00", "%Y-%m-%d %H:%M:%S")
print("naive :", naive, " tz:", naive.tzinfo)
print("as UTC :", naive.replace(tzinfo=timezone.utc))
Output:
parsed : 2026-09-27 14:30:00+05:30
tzinfo : UTC+05:30
utcoffset : 5:30:00
as UTC : 2026-09-27 09:00:00+00:00
naive : 2026-09-27 14:30:00 tz: None
as UTC : 2026-09-27 14:30:00+00:00
+0530 becomes a real tzinfo, and astimezone converts it.%z accepts +0530, +05:30 and Z in current Python versions.
If you know the zone but the text does not carry it, use replace(tzinfo=...) to attach one. Use zoneinfo.ZoneInfo("Asia/Kolkata") rather than a fixed offset when daylight saving applies.
Naive and aware datetimes do not mix
This is the bug that surfaces in production rather than in testing:
from datetime import datetime, timezone
naive = datetime.strptime("2026-09-27", "%Y-%m-%d")
aware = naive.replace(tzinfo=timezone.utc)
print("naive:", naive, " tzinfo:", naive.tzinfo)
print("aware:", aware, " tzinfo:", aware.tzinfo)
print()
try:
naive < aware
except TypeError as err:
print("comparing them ->", type(err).__name__ + ":", err)
print()
print("pick one and stay with it. Storing everything as UTC-aware is the usual answer.")
Output:
naive: 2026-09-27 00:00:00 tzinfo: None
aware: 2026-09-27 00:00:00+00:00 tzinfo: UTC
comparing them -> TypeError: can't compare offset-naive and offset-aware datetimes
pick one and stay with it. Storing everything as UTC-aware is the usual answer.
can't compare offset-naive and offset-aware datetimes.Subtraction fails the same way, so any arithmetic mixing the two raises. It usually appears when one date comes from a database and the other from strptime.
Choose a convention early. Storing and comparing everything as UTC-aware, then converting to local time only for display, avoids the whole class of problem.
Why does the strftime %-d code fail on Windows?
Most tutorials give a format code that does not work on Windows at all:
import platform
from datetime import datetime
d = datetime(2026, 9, 27)
print("running on:", platform.system())
# stripping the leading zero from a day number is spelled differently per platform
for code in ("%-d", "%#d"):
try:
print(f" strftime('{code}') -> {d.strftime(code)}")
except ValueError as err:
print(f" strftime('{code}') -> ValueError: {err}")
print()
print("%-d works on Linux and macOS, %#d on Windows")
print("for portable code, format normally and strip the zero yourself:")
print(" ", d.strftime("%d").lstrip("0"))
Output:
running on: Windows
strftime('%-d') -> ValueError: Invalid format string
strftime('%#d') -> 27
%-d works on Linux and macOS, %#d on Windows
for portable code, format normally and strip the zero yourself:
27
%-d raises. %#d is the Windows spelling.Stripping a leading zero is %-d on Linux and macOS, and %#d on Windows. Neither is portable.
Format with the normal code and strip the zero in Python instead. It is one extra call and it runs everywhere.
Common string to datetime problems
| Symptom | Cause | Fix |
|---|---|---|
time data does not match format | Format differs from the text | Compare the two quoted values |
| Day and month swapped | %d/%m vs %m/%d | Check which convention the data uses |
tzinfo is None | No %z in the format | Add %z or replace(tzinfo=) |
can't compare offset-naive | Mixing naive and aware | Make everything aware |
Z raises on 3.10 | Old fromisoformat | Replace Z with +00:00 |
Invalid format string | %-d on Windows | Use %#d or strip in Python |
| Parsing is slow | strptime in a big loop | Use fromisoformat |
Other Python conversion and formatting guides:
- Convert a string to an integer
- Convert a string to a boolean
- Check if a string is an integer or float
- Convert integers to datetime in pandas
- Get the first n characters of a string
- Compare strings in Python
Frequently asked questions
How do I convert a string to a datetime in Python?
datetime.strptime(text, format), where the format describes your text exactly. The format codes are listed in the Python strftime and strptime format codes reference.
What does ‘time data does not match format’ mean?
Your format string does not describe the text you gave it. The error quotes both, so compare them character by character, watching separators and zero padding.
What is the difference between strptime and fromisoformat?
strptime takes any format you describe. fromisoformat only reads ISO 8601, needs no format string, and is around 40 times faster.
How do I parse a string with a timezone?
Include %z in the format, as in "%Y-%m-%d %H:%M:%S %z". That produces a timezone-aware datetime.
Why can’t I compare two datetimes?
One is naive and the other is timezone-aware. Python refuses to compare them because the naive one has no defined point in time.
Why does %-d give an Invalid format string error?
%-d is a Linux and macOS extension. On Windows the equivalent is %#d. Neither is portable.
How do I convert a string to a date rather than a datetime?
Parse it with strptime and call .date() on the result, or use date.fromisoformat() for ISO text.
Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn