Convert a String to datetime in Python (strptime and ISO)

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
Command Prompt showing several Python strptime format strings parsing different date formats
Seven formats, seven correct results.
CodeMeansExample
%YFour-digit year2026
%yTwo-digit year26
%mMonth as a number09
%b / %BMonth name, short / fullSep / September
%dDay of the month27
%H / %IHour, 24 / 12 hour14 / 02
%pAM or PMPM
%zUTC 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
Command Prompt showing the Python ValueError that time data does not match format
The message quotes your string and your format, side by side.

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
Command Prompt showing a Python string parsed into a timezone aware datetime and converted to UTC
+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.
Command Prompt showing a TypeError when comparing an offset naive and an offset aware Python datetime
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
Command Prompt showing that the dash d strftime code raises an Invalid format string error on Windows
%-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

SymptomCauseFix
time data does not match formatFormat differs from the textCompare the two quoted values
Day and month swapped%d/%m vs %m/%dCheck which convention the data uses
tzinfo is NoneNo %z in the formatAdd %z or replace(tzinfo=)
can't compare offset-naiveMixing naive and awareMake everything aware
Z raises on 3.10Old fromisoformatReplace Z with +00:00
Invalid format string%-d on WindowsUse %#d or strip in Python
Parsing is slowstrptime in a big loopUse fromisoformat

Other Python conversion and formatting guides:

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.