Python: уроки, справочник и задачи

strptime в Python: разбор строки в дату и время

Раздел: Модули и библиотеки Python · Обновлено 31.10.2026

Метод datetime.strptime() в Python превращает строку с датой, например "12.06.2024 18:40", в объект datetime по шаблону, который вы задаете кодами вроде %d, %m и %Y. Ниже разобрано, как составить шаблон под конкретную строку, какие коды работают при разборе, откуда берется ValueError, как читать смещение часового пояса и когда вместо strptime удобнее fromisoformat() или dateutil.

Как устроена работа strptime?
Содержание статьи
  1. Синтаксис и первый пример
  2. Как подобрать шаблон под строку
  3. Коды формата при разборе строки
    1. Числа без ведущего нуля
    2. Двузначный год и 12-часовое время
    3. Значения по умолчанию
  4. Ошибка ValueError и как ее обработать
  5. Часовые пояса: %z и %Z
  6. Русские названия месяцев
  7. Альтернативы: fromisoformat, time.strptime, dateutil, pandas
  8. Что делать с датой после разбора
  9. Частые вопросы
    1. Чем strptime отличается от strftime?
    2. Как получить из строки date, а не datetime?
    3. Почему возникает ошибка unconverted data remains?
    4. Можно ли разобрать строку с миллисекундами?
    5. Учитывает ли strptime часовой пояс?

Синтаксис и первый пример

У метода два обязательных аргумента: строка и шаблон формата. Это метод класса, поэтому вызывается он у самого datetime, а не у готового объекта. Результат всегда объект datetime, даже если в строке нет времени: тогда часы, минуты и секунды будут нулевыми.

from datetime import datetime

raw = '12.06.2024 18:40'
dt = datetime.strptime(raw, '%d.%m.%Y %H:%M')

print(dt)          # 2024-06-12 18:40:00
print(type(dt))    # <class 'datetime.datetime'>
print(dt.year, dt.month, dt.day)   # 2024 6 12

Шаблон читается слева направо и должен описывать строку полностью. Обычные символы в шаблоне (точки, дефисы, двоеточия, пробелы, буква T) должны совпасть с символами строки, а каждый код с процентом забирает свой фрагмент: %d день, %m месяц, %Y четырехзначный год. Если нужна только дата без времени, возьмите dt.date(), если только время, то dt.time().

Название легко запомнить по последней букве: p означает parse, то есть разбор. Обратная операция, вывод даты в строку, делается методом strftime и его кодами форматирования. Коды у двух методов общие, но при разборе у некоторых из них свои особенности, о которых ниже.

Как подобрать шаблон под строку

Удобный прием: выпишите строку и замените в ней каждую часть даты на код, оставив разделители как есть. Из "2024-06-12" получится %Y-%m-%d, из "12/06/24 7:05 PM" получится %d/%m/%y %I:%M %p. Несколько типичных вариантов собрано в таблице.

СтрокаШаблонКомментарий
12.06.2024%d.%m.%Yпривычная запись в России
2024-06-12 18:40:05%Y-%m-%d %H:%M:%Sформат баз данных и логов
06/12/2024%m/%d/%Yамериканский порядок, месяц первым
12 June 2024%d %B %Yполное английское название месяца
Wed, 12 Jun 2024%a, %d %b %Yсокращенные день недели и месяц
20240612%Y%m%dдата без разделителей
2024-06-12T18:40:05.250+03:00%Y-%m-%dT%H:%M:%S.%f%zISO 8601 с миллисекундами и смещением

Одна и та же строка может читаться по-разному. "03/04/2024" по шаблону %d/%m/%Y дает 3 апреля, а по шаблону %m/%d/%Y дает 4 марта, и ошибки не будет ни в одном случае. Поэтому формат надо брать из источника данных (документации API, настроек выгрузки), а не угадывать по одной строке.

from datetime import datetime

s = '03/04/2024'
print(datetime.strptime(s, '%d/%m/%Y').date())   # 2024-04-03
print(datetime.strptime(s, '%m/%d/%Y').date())   # 2024-03-04

Коды формата при разборе строки

Список директив тот же, что у strftime, но при чтении важно, сколько символов код готов принять и какие значения считаются допустимыми. В таблице только то, что чаще всего встречается в реальных строках.

КодЧто ожидает в строкеПример
%dдень месяца, одна или две цифры5, 05, 31
%mмесяц числом, одна или две цифры6, 06, 12
%Yгод четырьмя цифрами2024
%yгод двумя цифрами24
%Hчас в 24-часовом формате0, 09, 23
%I и %pчас в 12-часовом формате и AM/PM07 PM
%M, %Sминуты и секунды05, 59
%fдробная часть секунды, от 1 до 6 цифр250, 123456
%b, %Bсокращенное и полное название месяца по локалиJun, June
%a, %Aдень недели по локалиWed, Wednesday
%jномер дня в году001, 164
%zсмещение от UTC+0300, +03:00, Z
%Zимя пояса, только UTC, GMT и имена локального поясаUTC
%%сам символ процента%

Полный перечень с примечаниями есть в разделе strftime() and strptime() Behavior официальной документации. Ниже особенности, на которых чаще всего спотыкаются.

Числа без ведущего нуля

Коды %d, %m, %H при разборе принимают и "05", и "5", поэтому строка "1.2.2024" читается обычным шаблоном %d.%m.%Y. Варианты с дефисом вроде %-d, которые встречаются в статьях про вывод даты на Linux, strptime не понимает и падает с ошибкой '-' is a bad directive.

from datetime import datetime

print(datetime.strptime('1.2.2024', '%d.%m.%Y'))   # 2024-02-01 00:00:00
# datetime.strptime('1.2.2024', '%-d.%m.%Y')  -> ValueError

Двузначный год и 12-часовое время

Для %y действует правило POSIX: значения 69-99 превращаются в 1969-1999, а 00-68 в 2000-2068. Строка "05.03.69" станет 1969 годом, а "05.03.68" уже 2068. Если в данных встречаются даты рождения или архивные записи, лучше требовать четырехзначный год.

Код %p влияет на результат только в паре с %I. С %H он молча игнорируется, и "07:15 PM" превратится в 7 утра вместо 19 часов.

from datetime import datetime

print(datetime.strptime('07:15 PM', '%I:%M %p').time())  # 19:15:00
print(datetime.strptime('07:15 PM', '%H:%M %p').time())  # 07:15:00

Значения по умолчанию

Все, чего нет в строке, заполняется значениями по умолчанию: год 1900, месяц 1, день 1, время 00:00:00. Разбор одного времени "10:30" по шаблону %H:%M вернет 1900-01-01 10:30:00. Чтобы получить время без фиктивной даты, вызовите .time() у результата. Начиная с Python 3.13 разбор дня месяца без года (например, шаблон %d.%m) выдает DeprecationWarning: без года нельзя проверить, существует ли 29 февраля.

Ошибка ValueError и как ее обработать

Если строка не подходит под шаблон, strptime выбрасывает ValueError, и текст ошибки обычно прямо подсказывает причину. Чаще всего встречаются три сообщения.

from datetime import datetime

for s, fmt in [('12/06/2024', '%d.%m.%Y'),
               ('2024-06-12 10:30', '%Y-%m-%d'),
               ('29.02.2023', '%d.%m.%Y')]:
    try:
        datetime.strptime(s, fmt)
    except ValueError as e:
        print(e)
# time data '12/06/2024' does not match format '%d.%m.%Y'
# unconverted data remains:  10:30
# day is out of range for month

Данные из форм, файлов и таблиц редко бывают однородными. Практичное решение: перед разбором убрать пробелы по краям через strip() и перебрать несколько допустимых шаблонов по очереди, возвращая None, если ни один не подошел.

from datetime import datetime

FORMATS = ('%d.%m.%Y', '%Y-%m-%d', '%d/%m/%Y', '%d.%m.%Y %H:%M')

def parse_date(text):
    text = text.strip()
    for fmt in FORMATS:
        try:
            return datetime.strptime(text, fmt)
        except ValueError:
            continue
    return None

print(parse_date(' 2024-06-12 '))   # 2024-06-12 00:00:00
print(parse_date('12 июня'))        # None

Порядок шаблонов в кортеже имеет значение: если строка подходит под два из них (как в примере с 03/04/2024), сработает первый.

Часовые пояса: %z и %Z

Без кода пояса strptime возвращает "наивный" datetime, у которого tzinfo равен None. Код %z читает смещение в видах +0300, +03:00 и букву Z для UTC, после чего объект становится "осведомленным": его можно сравнивать с другими осведомленными датами и переводить в нужный пояс.

from datetime import datetime, timezone

dt = datetime.strptime('2024-06-12 18:40:00+0300', '%Y-%m-%d %H:%M:%S%z')
print(dt.tzinfo)                     # UTC+03:00
print(dt.astimezone(timezone.utc))   # 2024-06-12 15:40:00+00:00

z = datetime.strptime('2024-06-12T15:40:00Z', '%Y-%m-%dT%H:%M:%S%z')
print(z == dt)                       # True

С %Z все скромнее: он принимает только "UTC", "GMT" и названия локального пояса системы, а результат остается наивным, поле tzinfo не заполняется. Строки вида "MSK" или "Europe/Moscow" так не разобрать. Если пояс известен заранее, разберите время без него и прикрепите пояс вручную через replace(tzinfo=...) с объектом ZoneInfo('Europe/Moscow') из модуля zoneinfo.

from datetime import datetime
from zoneinfo import ZoneInfo

naive = datetime.strptime('12.06.2024 18:40', '%d.%m.%Y %H:%M')
msk = naive.replace(tzinfo=ZoneInfo('Europe/Moscow'))
print(msk.isoformat())   # 2024-06-12T18:40:00+03:00

Сравнивать наивный и осведомленный объекты нельзя: проверка на больше-меньше и вычитание дают TypeError. Поэтому в одном наборе данных все даты должны быть либо с поясом, либо без него.

Русские названия месяцев

Коды %B и %b сверяются с названиями месяцев текущей локали. По умолчанию это английские имена, и строка "12 июня 2024" не разберется. Переключение через locale.setlocale(locale.LC_TIME, 'ru_RU.UTF-8') зависит от операционной системы: на одних машинах такой локали нет, на других названия хранятся в именительном падеже ("июнь"), а в тексте стоит родительный ("июня"). К тому же локаль меняется для всего процесса.

Надежнее заменить название месяца на номер перед разбором. Словарь по первым трем буквам покрывает и "июня", и "июнь", и "Июн".

from datetime import datetime

MONTHS = {'янв': 1, 'фев': 2, 'мар': 3, 'апр': 4, 'мая': 5, 'май': 5,
          'июн': 6, 'июл': 7, 'авг': 8, 'сен': 9, 'окт': 10, 'ноя': 11, 'дек': 12}

def parse_ru(text):
    day, month, year = text.lower().split()
    num = MONTHS[month[:3]]
    return datetime.strptime(f'{day}.{num}.{year}', '%d.%m.%Y')

print(parse_ru('12 июня 2024').date())   # 2024-06-12
print(parse_ru('1 Мая 2025').date())     # 2025-05-01

Альтернативы: fromisoformat, time.strptime, dateutil, pandas

strptime универсален, но не всегда лучший выбор. Если строка в стандарте ISO 8601, быстрее и проще вызвать fromisoformat(): шаблон не нужен, а с Python 3.11 метод понимает почти любые варианты стандарта, включая букву Z и дробные секунды. Для чистой даты есть date.fromisoformat().

from datetime import date, datetime

print(date.fromisoformat('2024-06-12'))                    # 2024-06-12
print(datetime.fromisoformat('2024-06-12T18:40:05+03:00'))
# 2024-06-12 18:40:05+03:00

Функция time.strptime() из модуля time принимает те же коды, но возвращает кортеж struct_time, а не datetime. Она пригодится в старом коде, построенном на этом модуле, в остальных случаях удобнее работать с datetime. В Python 3.14 у классов date и time из модуля datetime появились собственные методы strptime(), а в более ранних версиях дату или время получают через datetime.strptime(...).date() и .time().

Сторонняя библиотека python-dateutil (pip install python-dateutil) разбирает строку без шаблона и сама определяет формат. Параметр dayfirst=True подсказывает, что в неоднозначных строках день идет первым, как принято в России.

from dateutil import parser

print(parser.parse('12.06.2024 18:40', dayfirst=True))  # 2024-06-12 18:40:00
print(parser.parse('June 12, 2024 6:40 PM'))            # 2024-06-12 18:40:00

Угадывание формата удобно для разовых задач, но в рабочем коде оно может тихо перепутать день и месяц, поэтому для известных форматов надежнее явный шаблон strptime. Когда дат тысячи и они лежат в таблице, используйте pd.to_datetime(df['col'], format='%d.%m.%Y') из библиотеки Pandas: она применяет шаблон ко всему столбцу сразу и работает заметно быстрее цикла.

СпособНужен шаблонКогда выбирать
datetime.strptime()даизвестный нестандартный формат
datetime.fromisoformat()нетстроки ISO 8601 из API и баз данных
time.strptime()дакод на модуле time, нужен struct_time
dateutil.parser.parse()нетразнородные строки, формат заранее не известен
pandas.to_datetime()по желаниюцелые столбцы таблиц

Что делать с датой после разбора

Ради этого строку и превращают в объект: со строками "09.06.2024" и "12.05.2024" сравнение идет посимвольно и дает неверный порядок, а объекты datetime сравниваются по времени. Разность двух объектов дает интервал, подробно работа с ним описана в статье про timedelta и арифметику дат.

from datetime import datetime

dates = ['09.06.2024', '12.05.2024', '01.01.2025']
parsed = sorted(datetime.strptime(d, '%d.%m.%Y') for d in dates)
print([d.strftime('%d.%m.%Y') for d in parsed])
# ['12.05.2024', '09.06.2024', '01.01.2025']

delta = parsed[-1] - parsed[0]
print(delta.days)   # 234

Обзор остальных возможностей модуля, от текущего момента до замены отдельных полей, есть в материале про модуль datetime.

Частые вопросы

Короткие ответы на вопросы, которые чаще всего возникают при разборе дат из строк.

Чем strptime отличается от strftime?

Направлением работы. strptime получает строку и шаблон и возвращает объект datetime, strftime вызывается у готового объекта и возвращает строку. Коды формата у них общие.

Как получить из строки date, а не datetime?

Разберите строку через datetime.strptime() и вызовите у результата .date(). Для строки вида 2024-06-12 проще сразу использовать date.fromisoformat().

Почему возникает ошибка unconverted data remains?

В конце строки остались символы, которые шаблон не описывает: время, миллисекунды, пояс или пробел. Дополните шаблон нужными кодами или обрежьте строку через strip() перед разбором.

Можно ли разобрать строку с миллисекундами?

Да, код %f принимает от одной до шести цифр после точки: "18:40:05.250" читается шаблоном %H:%M:%S.%f, а в объекте окажется 250000 микросекунд.

Учитывает ли strptime часовой пояс?

Только если в шаблоне есть %z: тогда объект получит смещение от UTC. Без него, а также с %Z, результат будет наивным, и пояс придется задать вручную через replace(tzinfo=...).