Перейти к содержанию

412 уроков, 25 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 5 из 20 Начальный 20 мин 110 XP

strptime: строка превращается в дату

Лог, форма и таблица присылают даты строками — для вычислений нужен объект. Учим strptime читать строку по шаблону и отличать настоящий формат от почти настоящего.

Редакция Питоники

strftime переводил объект в строку — а в реальном коде чаще нужно наоборот: лог прислал «2026-10-21 09:15», форма — «21.10.2026», отчёт — «21 октября 2026». Пока дата — строка, из неё нельзя вычесть неделю или сравнить с сегодняшним днём. Обратный перевод делает метод strptime — «string parse time»: строка разбирается в объект.

Разбор по шаблону

У strptime два аргумента: строка и шаблон. Важно: метод принадлежит классу datetime, а не объекту, — вызывается через datetime.strptime(...). Коды шаблона у strptime те же, что у strftime, — меняется только направление: из полей объекта в строку или из строки в объект. Если в строке нет времени, часы, минуты и секунды станут нулями.

два разбора
from datetime import datetime

day = datetime.strptime('21.10.2026', '%d.%m.%Y')
print(day)

stamp = datetime.strptime('2026-10-21 14:30', '%Y-%m-%d %H:%M')
print(stamp)
Вывод
2026-10-21 00:00:00
2026-10-21 14:30:00

Оба результата — полноценные datetime: у них работают year, month, hour, поверх них — вся арифметика и сравнения из следующих уроков. Шаблон при этом должен совпадать со строкой символ в символ: коды — под числа, разделители — под разделители. Перепутанный порядок ловится по-разному: шаблон %m.%d.%Y для строки «21.10.2026» упадёт сразу — месяца 21 не бывает, а вот для «04.10.2026» пройдёт молча и даст 10 апреля вместо 4 октября. Python читает ровно по шаблону и никогда не угадывает; на этой ловушке стоит питфолл-блок ниже.

строки из лога
from datetime import datetime

start = datetime.strptime('2026-10-21 09:15', '%Y-%m-%d %H:%M')
end = datetime.strptime('2026-10-21 18:40', '%Y-%m-%d %H:%M')

print(start.date())
print(end - start)
Вывод
2026-10-21
9:25:00

Две строки из лога превратились в объекты — и из них уже получилась длительность рабочего дня: 9:25:00. Разница дат — это timedelta, отдельный объект со своей арифметикой; урок 6 целиком про него. Пока важно другое: разбор строки — это дверь, через которую внешние данные попадают в мир вычислений с датами.

Когда формат не совпал: ValueError

настоящий текст ошибки
from datetime import datetime

try:
    datetime.strptime('21/10/2026', '%d.%m.%Y')
except ValueError as err:
    print('Ошибка:', err)

try:
    datetime.strptime('21.10.2026 лишнее', '%d.%m.%Y')
except ValueError as err:
    print('Ошибка:', err)
Вывод
Ошибка: time data '21/10/2026' does not match format '%d.%m.%Y'
Ошибка: unconverted data remains:  лишнее

Два типовых сообщения. Первое, «time data ... does not match format ...», — строка не подходит под шаблон: в логе дефисы, в шаблоне точки, или порядок чисел другой. Второе, «unconverted data remains ...», — шаблон разобрал начало строки, а на хвосте остались символы, которых шаблон не описывает: после двоеточия в сообщении стоит лишний кусок с пробелом. Обе ошибки — ValueError, и обе лечатся одинаково: шаблон приводится в точное соответствие строке.

Пара strftime и strptime закрывает обе дороги между объектами и строками: наружу — для людей, внутрь — для вычислений. Дальше по курсу: урок 6 — timedelta и разница между моментами, тот самый 9:25:00 из лога; урок 10 — isoformat: стандартный формат, который парсится одной строкой и без шаблона вовсе.

Разбор пачки строк: лог, в котором одна строка битая

В одиночном разборе всё выглядит просто: дал строку — получил объект. Жизнь начинается, когда строк много и среди них попадаются неправильные. Лог приходит из чужой системы, и в нём встречается всё: обрезанная строка, другой разделитель, пустая строка в середине файла. Если разбирать каждую строку без try/except, первый же битый элемент уронит весь разбор — а вместе с ним весь отчёт, ради которого вы всё это затевали.

parse_list.py — разбор с защитой
from datetime import datetime

lines = ['2026-10-21 09:00', '2026-10-22 11:30', 'битая строка']

for line in lines:
    try:
        moment = datetime.strptime(line, '%Y-%m-%d %H:%M')
    except ValueError:
        print(line, '->', 'не разобралась')
    else:
        print(line, '->', moment)
Вывод
2026-10-21 09:00 -> 2026-10-21 09:00:00
2026-10-22 11:30 -> 2026-10-22 11:30:00
битая строка -> не разобралась

Ветка else существует не для красоты. Она отделяет успешный разбор от неуспешного и даёт вам выбор: залогировать битую строку и посчитать их количество, или молча пропустить. В рабочих проектах мы всегда считаем: если из десяти тысяч строк не разобрались три, это повод разбираться, а не повод молчать. Молчаливый пропуск превращает пропуск данных в неверный отчёт, который заметят уже наверху.

Пробелы, которые никто не видит

Отдельная боль — невидимые символы. CSV-экспорт часто оставляет в начале строки пробел или неразрывный пробел, лог добавляет отступы при выравнивании. Для глаза строка выглядит нормальной, а strptime честно падает с ValueError: разбор идёт посимвольно, и лишний пробел не совпал с ожидаемым. Лечится это либо strip() перед разбором, либо пробелом в шаблоне — %Y-%m-%d %H:%M с конкретным пробелом в ожидаемом месте.

spaces.py — пробел ломает разбор
from datetime import datetime

raw = ' 2026-10-21'

try:
    print(datetime.strptime(raw, '%Y-%m-%d'))
except ValueError:
    print('лишний пробел')

print(datetime.strptime(raw.strip(), '%Y-%m-%d'))
Вывод
лишний пробел
2026-10-21 00:00:00

Strip перед разбором — привычка, которая стоит завести один раз и забыть. Мы вызываем его на каждой строке из внешнего источника, потому что цена одного вызова — микросекунды, а цена пропущенной строки — неверный отчёт. Исключение одно: если в данных гарантированно нет мусора и формат жёсткий, strip() можно опустить — мы так делаем в hot-loop на миллионе строк, где лишний проход по строке уже заметен.

swap.py — порядок решает всё
from datetime import datetime

october = datetime.strptime('05.10.2026', '%d.%m.%Y')
print(october)
print(october.month)
print(october.day)
Вывод
2026-10-05 00:00:00
10
5

Кстати, тот же приём сработает и наоборот: если вы знаете, что в логе приходит именно «05.10.2026», а разбор даёт пятое мая — дело в шаблоне, а не в данных. Отладка здесь занимает минуту: напечатайте месяц и день отдельно, и перепутать их невозможно.

Разбор даты, которая не в том формате: как понять, что именно сломалось

Самый частый сценарий: текст пришёл из внешнего источника, формат в нём другой, и strptime бросает ValueError. Сообщение об ошибке всегда содержит обе стороны — и строку, и шаблон: «time data '21/10/2026' does not match format '%d.%m.%Y'». Это уже половина разбора: сразу видно, что в строке слэши, а в шаблоне точки, и остаётся только решить, какую сторону менять.

Дальше полезно сузить область поиска. Чаще всего разбор спотыкается об один символ: лишний пробел в начале строки, неразрывный пробел вместо обычного, дефис вместо точки, или число, записанное с буквой. Печатать исходную строку с невидимыми символами — плохая идея, они не покажутся; вместо этого удобно напечатать repr(line), и невидимые символы станут видны как \u00a0 или \t. Ещё один приём — сравнить длины строки и результата разбора: если результат короче, потерялось что-то в конце, и это уже второй случай, unconverted data remains.

И третий приём, когда формат заранее неизвестен: собрать кандидатов и попробовать каждый, а не гадать. Список шаблонов в порядке убывания вероятности, обход по нему с try/except даёт работающий парсер там, где угадывание не работает никогда. Порядок в списке важен: более строгий шаблон должен стоять раньше, иначе строка разберётся не тем шаблоном и потеряет часть данных.

parse_any.py — перебор шаблонов
from datetime import datetime

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


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


for line in ['21.10.2026 14:30', '2026-10-21', '21.10.2026', 'не дата']:
    print(line, '->', parse_any(line))
Вывод
21.10.2026 14:30 -> 2026-10-21 14:30:00
2026-10-21 -> 2026-10-21 00:00:00
21.10.2026 -> 2026-10-21 00:00:00
не дата -> None

Функция возвращает None на неудачу, и это осознанный выбор: вызывающий код сам решает, что делать со строкой, которую разобрать не вышло, — посчитать их, залогировать или пропустить. Бросать исключение из такой функции — значит заставлять каждого вызывающего писать свой try/except, а забытый обработчик уронит разбор целиком, как мы разбирали выше.

Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

from datetime import datetime

d = datetime.strptime('15.01.2027', '%d.%m.%Y')
print(d.year, d.month)
from datetime import datetime

stamp = datetime.strptime('2026-10-21 14:30', '%Y-%m-%d %H:%M')
print(stamp)
from datetime import datetime

start = datetime.strptime('2026-10-21 09:00', '%Y-%m-%d %H:%M')
end = datetime.strptime('2026-10-21 11:30', '%Y-%m-%d %H:%M')
print(end - start)
from datetime import datetime

lines = ['2026-10-21 09:00', 'битая строка']

for line in lines:
    try:
        moment = datetime.strptime(line, '%Y-%m-%d %H:%M')
    except ValueError:
        print(line, '->', 'не разобралась')
    else:
        print(line, '->', moment)
Проверь себя
0 / 8

1. Что вернёт datetime.strptime('21.10.2026', '%d.%m.%Y')?

2. Чем шаблон strptime отличается от шаблона strftime?

3. Какая ошибка будет при datetime.strptime('21/10/2026', '%d.%m.%Y')?

4. Строка '04.10.2026' разобрана шаблоном '%m.%d.%Y'. Какая это дата?

5. Что означает сообщение «unconverted data remains: лишнее»?

6. Что произойдёт при datetime.strptime(' 2026-10-21', '%Y-%m-%d')?

7. Зачем при разборе списка строк оборачивать strptime в try/except?

8. Что даст разбор строки '05.10.2026' шаблоном '%m.%d.%Y'?

Карточки терминов
Запомнено: 0 / 8
Практика

Разбери две строки из журнала: '15.01.2027 08:00' (начало) и '15.01.2027 16:30' (конец). Выведи оба объекта datetime, а затем дату начала отдельно методом .date().

practice.py
Вопросы и ответы по уроку

Как преобразовать строку в дату в Python?

Методом datetime.strptime(строка, шаблон): datetime.strptime('21.10.2026', '%d.%m.%Y') вернёт объект datetime. Шаблон собирается из кодов %d, %m, %Y и других и должен совпадать со строкой символ в символ.

Урок 5: strptime

Почему strptime выдаёт ValueError?

Самая частая причина — строка не совпадает с шаблоном: другие разделители, другой порядок чисел или лишний хвост. Сообщения различаются: «time data ... does not match format ...» — полное несовпадение, «unconverted data remains ...» — шаблон разобрал начало, а конец строки остался неописанным.

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

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

Урок 4: strftime

Как разобрать дату из лог-файла?

Вырезать из строки часть с датой и прогнать через strptime с шаблоном, повторяющим формат лога: для «2026-10-21 09:15» это '%Y-%m-%d %H:%M'. Из двух разобранных штампов уже можно получить длительность события вычитанием.

Урок 19: даты в логах

Почему strptime падает на строке с пробелом в начале?

Разбор идёт посимвольно: пробел не совпадает ни с одним кодом шаблона, и strptime честно сообщает об этом через ValueError. Перед разбором вызывайте strip() — тогда лишние пробелы и неразрывные пробелы не попадут в результат.

Как разобрать список дат, в котором часть строк может быть битой?

Оберните strptime в try/except ValueError прямо в цикле: успешные строки собирайте в список, неудачные считайте и логируйте. Так разбор не прервётся на первой проблеме, а вы увидите масштаб проблемы. Подробный разбор логов — в уроке 19.

Урок 19: даты в логах

Понравился урок? Сошлитесь на него

«Коды шаблона у strptime те же, что у strftime, — меняется только направление: из полей объекта в строку или из строки в объект.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Что дальше

Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.