Рано или поздно в любой настоящей программе появляются даты: когда пользователь зарегистрировался, до какого числа действует подписка, сколько дней осталось до дедлайна. Самый простой способ, который приходит в голову — хранить дату обычной строкой, например "2024-01-15". Но со строкой почти ничего нельзя сделать по-настоящему: нельзя корректно узнать, какая из двух дат раньше, нельзя прибавить к ней 7 дней, нельзя посчитать, сколько дней прошло между двумя датами — придётся вручную резать строку и складывать числа, рискуя ошибиться с количеством дней в месяце или февралём в високосном году.
Python решает эту проблему модулем datetime: он встроен в язык, ничего дополнительно устанавливать не нужно. Модуль даёт готовые объекты, которые умеют сравнивать себя с другими такими же объектами, форматироваться в любую строку и складываться с промежутками времени, как обычные числа с помощью + и -.
Три главных класса модуля
Представьте три разных бланка: один только для даты, второй только для времени и третий — сразу для того и другого. В модуле datetime устроено так же: под разные задачи есть разные объекты, и вы выбираете подходящий, не таская лишние данные там, где они не нужны.
Официально это три отдельных класса внутри модуля datetime: date хранит только дату (год, месяц, день), time хранит только время (час, минута, секунда), а datetime объединяет их и хранит дату и время сразу — это и есть главный, самый ходовой класс, с которым вы будете работать чаще всего. Чтобы использовать любой из них, его нужно подключить командой import, с которой вы уже знакомы:
from datetime import date, time, datetime
Эта строка забирает из модуля datetime сразу три класса: date, time и datetime. После неё к каждому из них можно обращаться напрямую по имени, без точки впереди, и создавать через них объекты — так же, как вы создаёте список через [].
У модуля и у одного из его классов совпадает название — оба называются datetime. Путаницы это не создаёт: вы почти всегда пишете from datetime import datetime и дальше работаете с классом.
date: когда время не важно
Представьте день рождения или дату сдачи отчёта — в обоих случаях важен именно день, а не конкретный час и минута. Хранить время в такой ситуации незачем — лишние данные, которые никто не будет использовать.
Для этого в модуле есть класс date. Он хранит ровно три числа: год, месяц и день. Создать объект можно двумя способами — указать конкретную дату вручную или спросить у Python сегодняшнюю.
from datetime import date
birthday = date(1995, 6, 23)
print(birthday)
today = date.today()
print(today.year)
print(today.month)
print(today.day)
Разберём построчно:
birthday = date(1995, 6, 23)— создаём объект вручную: в скобках через запятую передаём год, месяц и день, именно в таком порядке. Месяцы и дни нумеруются как у людей — январь это1, а не0.print(birthday)— выведет1995-06-23: классdateсам превращает себя в строку год-месяц-день через дефис.today = date.today()—todayздесь не переменная, а метод самого классаdate, который спрашивает у операционной системы сегодняшнюю дату и собирает из неё готовый объект. Вызывается без аргументов.print(today.year),.month,.day— у любого объектаdateесть три таких атрибута для отдельного года, месяца и дня.
time: когда важно только время
Теперь наоборот — представьте расписание звонков в школе: "урок начинается в 09:00". Дата тут не нужна, важен только момент внутри суток, который повторяется каждый день.
Для этого есть класс time. Он хранит час, минуту и секунду, но ничего не знает про год, месяц или день. Используется реже, чем date и datetime.
from datetime import time
lunch = time(12, 30, 0)
alarm = time(7)
print(lunch)
print(alarm)
Разберём построчно:
lunch = time(12, 30, 0)— создаём время: час, минута, секунда через запятую, в таком порядке.alarm = time(7)— уtimeобязателен только час, минуты и секунды необязательны: если их не указать, Python сам подставит0.print(lunch)выведет12:30:00, аprint(alarm)выведет07:00:00— класс сам форматирует себя в вид часы:минуты:секунды. Как и уdate, у объектаtimeесть атрибуты.hour,.minute,.secondдля отдельных частей.
datetime: дата и время вместе
А теперь пример, где нужны оба сразу: момент регистрации пользователя — "15 января в 12:30", а не просто "15 января".
Именно для этого существует класс datetime — он объединяет всё, что умеют date и time, в одном объекте. Это самый используемый класс модуля, и дальше речь в основном пойдёт о нём.
from datetime import datetime
dt = datetime(2024, 1, 15, 12, 30, 0)
print(dt)
dt2 = datetime(2024, 6, 1)
print(dt2)
now = datetime.now()
print(now.year, now.hour, now.minute)
Разберём построчно:
dt = datetime(2024, 1, 15, 12, 30, 0)— создаём объект вручную: сначала год, месяц, день (как уdate), затем час, минуту, секунду (как уtime) — всё через запятую в одном вызове.print(dt)выведет2024-01-15 12:30:00.dt2 = datetime(2024, 6, 1)— если после дня ничего не передать, Python сам подставит время00:00:00, так же, какtime(7)подставил нулевые минуты. Выведется2024-06-01 00:00:00.now = datetime.now()— ещё один метод самого класса, какdate.today(), только сразу забирает у компьютера и текущую дату, и текущее время.print(now.year, now.hour, now.minute)— уdatetimeдоступны все атрибутыdateиtimeсразу:year,month,day,hour,minute,second. Выведутся текущие год, час и минута.
Совет: если важна только дата — берите date, если дата и время вместе — datetime. Отдельный time нужен редко, обычно для расписания без привязки к дню.
strftime: превращаем дату в красивую строку
Когда вы печатаете объект datetime напрямую, он всегда выглядит одинаково: 2024-03-08 09:05:00. Но людям чаще привычен другой вид, например "08.03.2024". Формат вывода нужно задавать самостоятельно.
Для этого у date, time и datetime есть метод strftime() (от string format time — "отформатировать время в строку"). Ему передают строку-шаблон со специальными кодами со знаком процента, а метод возвращает готовую строку по этому шаблону.
from datetime import datetime
now = datetime(2024, 3, 8, 9, 5, 0)
print(now.strftime("%d.%m.%Y"))
print(now.strftime("%Y-%m-%d"))
print(now.strftime("%H:%M"))
Разберём построчно:
now = datetime(2024, 3, 8, 9, 5, 0)— уже знакомое создание объекта: 8 марта 2024 года, 9 часов 5 минут.now.strftime("%d.%m.%Y")— вызываем методstrftimeу объектаnowи передаём шаблон:%dозначает "день, две цифры",%m— "месяц, две цифры",%Y— "год, четыре цифры". Точки между кодами — обычные символы, они попадут в результат как есть. Выведется08.03.2024.now.strftime("%Y-%m-%d")— те же коды, но в другом порядке и с дефисами. Выведется2024-03-08.now.strftime("%H:%M")—%Hозначает час в 24-часовом формате,%M— минуты. Выведется09:05.
Основных кодов немного, вот самые ходовые:
| КОД | ЧТО ОЗНАЧАЕТ | ПРИМЕР |
|---|---|---|
%Y | Год, 4 цифры | 2024 |
%m | Месяц, число (01–12) | 03 |
%B | Месяц, полное название | March |
%d | День месяца (01–31) | 08 |
%A | День недели, полное название | Friday |
%H | Час, 24-часовой (00–23) | 09 |
%M | Минуты (00–59) | 05 |
Мнемоника: большая буква — полная версия (%B = March), маленькая — сокращённая. У года наоборот: %Y — четыре цифры, %y — две.
strptime: превращаем строку в дату
Часто бывает наоборот: дата приходит строкой — например, пользователь ввёл её в форме — а работать с ней нужно как с настоящим объектом datetime, чтобы сравнивать её или прибавлять дни.
Для этого есть метод strptime() (от string parse time — "разобрать строку как время"). В отличие от strftime, это метод самого класса datetime, а не объекта: ему передают строку с датой и шаблон, по которому её читать, а он возвращает готовый объект.
from datetime import datetime
s = "15.01.2024"
dt = datetime.strptime(s, "%d.%m.%Y")
print(dt)
print(dt.year)
Разберём построчно:
s = "15.01.2024"— обычная строка с датой, как будто её только что ввёл пользователь.dt = datetime.strptime(s, "%d.%m.%Y")— вызываемstrptimeу самого классаdatetime, передаём два аргумента: саму строку и шаблон, описывающий её формат — день, точка, месяц, точка, год. Шаблон должен в точности совпадать со строкой: если бы шаблон остался"%d.%m.%Y", а строка была"2024-01-15", Python выбросил быValueError.print(dt)выведет2024-01-15 00:00:00— получился полноценныйdatetime, а время подставилось нулевым, раз в строке его не было.print(dt.year)— у результатаstrptimeдоступны все обычные атрибутыdatetime. Выведется2024.
Главная ловушка strptime: шаблон должен совпадать со строкой символ в символ — те же разделители, тот же порядок частей, то же количество цифр. Если они не совпадают хотя бы в одном месте, вы получите ValueError, а не тихо неправильный результат.
timedelta: складываем и сравниваем даты
Раз объекты datetime ведут себя как числа, к ним напрашивается арифметика: "через 7 дней", "сколько дней осталось до дедлайна". Но прибавить к дате просто число нельзя — непонятно, число чего это: дней, часов, секунд?
Поэтому для промежутков времени есть класс timedelta ("разница во времени"). Вы явно указываете, из чего состоит промежуток — дни, часы, минуты — и получившийся объект можно прибавлять к datetime или вычитать из него, тем же оператором + и -, каким складывают числа.
from datetime import datetime, timedelta
now = datetime(2024, 3, 1, 12, 0, 0)
in_7_days = now + timedelta(days=7)
print(in_7_days)
yesterday = now - timedelta(days=1)
print(yesterday)
Разберём построчно:
now = datetime(2024, 3, 1, 12, 0, 0)— отправная точка: 1 марта 2024 года, 12:00.timedelta(days=7)— создаём промежуток в 7 дней. Аргумент передан по имени (days=), поэтому Python точно понимает, что это дни, а не часы или минуты — уtimedeltaесть такие же именованные аргументыhours,minutes,seconds, их можно комбинировать в одном вызове.in_7_days = now + timedelta(days=7)— складываемdatetimeиtimedeltaобычным оператором+. Результат сноваdatetime, только на 7 дней позже:2024-03-08 12:00:00.yesterday = now - timedelta(days=1)— то же вычитанием: дата на день раньше. Выведется2024-02-29 12:00:00— Python сам учёл, что в феврале 2024 года 29 дней, потому что год високосный.
А что если вычесть не промежуток, а одну дату из другой? Получится объект timedelta — так удобно узнавать разницу между двумя датами, а даты дополнительно можно сравнивать обычными операторами.
start = datetime(2024, 1, 1)
end = datetime(2024, 3, 14)
diff = end - start
print(diff.days)
print(start < end)
Разберём построчно:
diff = end - start— вычитаем одинdatetimeиз другого. Результат — не число, а объектtimedelta, представляющий промежуток между ними.print(diff.days)— уtimedeltaесть атрибут.daysс количеством полных дней в промежутке. Выведется73.print(start < end)— объектыdatetimeсравниваются теми же операторами, что и числа:<,>,==,!=,<=,>=. Более ранняя дата считается "меньше". ВыведетсяTrue, потому что январь раньше марта.
Частые ошибки
Шаблон strptime не совпадает со строкой
datetime.strptime("15.01.2024", "%Y-%m-%d") вызовет ValueError: строка написана через точки и день впереди, а шаблон ждёт дефисы и год впереди. Формат в шаблоне и в строке должен совпадать буквально.
Прибавление обычного числа вместо timedelta
now + 7 вызовет TypeError: datetime не умеет складываться с обычным числом, только с timedelta. Нужно писать now + timedelta(days=7), явно указывая, что это дни.
Перепутанный порядок аргументов в date() и datetime()
date(23, 6, 1995) вместо date(1995, 6, 23) создаст дату с годом 23, а не ошибку — Python не проверяет, похоже ли число на настоящий год. Порядок всегда один: год, месяц, день.
.days у разницы дат округляет вниз, а не до целого
У результата вычитания двух datetime атрибут .days отбрасывает остаток в часах: разница в 1 день 23 часа даст .days равным 1, а не 2. Это нормальное поведение, а не ошибка.
Что важно запомнить
- Модуль datetime встроен в Python, устанавливать ничего не нужно.
dateхранит только дату,time— только время,datetime— и то и другое вместе.datetimeиспользуется чаще всего.date.today()иdatetime.now()— методы самих классов, возвращают текущий момент без аргументов.- strftime() превращает объект в строку по шаблону. strptime() делает наоборот: строку в объект. Шаблон обязан совпадать с форматом строки.
- timedelta — промежуток времени. Его прибавляют к дате или вычитают из неё оператором
+и-, а разница двух дат сама возвращаетtimedeltaс атрибутом.days. - Даты сравниваются обычными операторами
<,>,==— более ранняя дата считается "меньше".
Проверьте себя
7 вопросов
Дней до следующего дня рождения
Напишите функцию days_until(target_day, target_month, from_date), которая принимает день и месяц целевой даты и опорную дату from_date (объект date), и возвращает количество дней от from_date до ближайшей даты с этим числом и месяцем.
Если такая дата в текущем году уже прошла (раньше from_date), считайте её в следующем году. Если дата совпадает с from_date — верните 0.
Например, days_until(5, 3, date(2024, 3, 1)) должна вернуть 4: от 1 марта до 5 марта ровно 4 дня.