timedelta: разница между моментами
«Срок подписки истекает через 7 дней», «событие было 3 часа назад» — за этим стоит timedelta. Вычитаем моменты, читаем дни и секунды, прибавляем разницу к датам.
Редакция Питоники
До сих пор моменты из первых уроков стояли рядом и не взаимодействовали. Настоящая работа с датами начинается, когда их вычитают: сколько дней до дедлайна, сколько часов длился простой, какая дата будет через неделю. Python отвечает на все эти вопросы одним объектом — timedelta, разницей между моментами.
Разница — это объект timedelta
Из более позднего момента вычитаем более ранний — получаем timedelta. Создаётся он и руками: timedelta(days=7), timedelta(hours=5, minutes=30) — аргументы складываются в одну длительность. Импортируется из того же модуля: from datetime import datetime, timedelta.
from datetime import datetime, timedelta
start = datetime(2026, 10, 21, 12, 0)
end = datetime(2026, 10, 22, 15, 30)
delta = end - start
print(delta)
print(type(delta))
print(timedelta(hours=5))
1 day, 3:30:00 <class 'datetime.timedelta'> 5:00:00
Печать timedelta читается слева направо: сначала дни, запятая, потом часы:минуты:секунды. 1 day, 3:30:00 — сутки и ещё три с половиной часа. Внутри объекта три главных поля: delta.days — количество полных суток; delta.seconds — это остаток внутри последних суток, а не всего секунд: для полного числа есть total_seconds(). И наконец delta.total_seconds() — вся длительность одним числом в секундах.
from datetime import datetime
start = datetime(2026, 10, 21, 12, 0)
end = datetime(2026, 10, 22, 15, 30)
delta = end - start
print(delta.days)
print(delta.seconds)
print(delta.total_seconds())
1 12600 99000.0
Дата плюс timedelta — момент в будущем
from datetime import datetime, timedelta
moment = datetime(2026, 10, 21, 12, 0)
print(moment + timedelta(days=7))
print(moment + timedelta(hours=5, minutes=30))
print(moment - timedelta(days=1))
2026-10-28 12:00:00 2026-10-21 17:30:00 2026-10-20 12:00:00
Сложение сдвигает момент вперёд, вычитание — назад, и время внутри момента пересчитывается само: к полудню прибавили полсуток с получасом — получили 17:30 того же дня. Работает и с чистыми датами: date(2026, 10, 21) + timedelta(days=7) даст дату 2026-10-28 без времени — timedelta дружит со всеми классами из урока 2. Минус к разнице тоже применим: минус timedelta(days=1) — то же самое, что timedelta(days=-1), длительность бывает и отрицательной. Теперь соберём приём целиком: «сейчас» — подставная дата из урока 3, релиз — момент в будущем, а поле days разницы — готовый ответ на вопрос «сколько полных дней осталось».
from datetime import datetime, timedelta
now = datetime(2026, 10, 23) # подставное «сейчас»
release = datetime(2026, 11, 1, 12, 0)
left = release - now
print(left)
print(left.days)
print(now + timedelta(days=left.days))
9 days, 12:00:00 9 2026-11-01 00:00:00
Обрати внимание на последнюю строку: к «сегодня» прибавили найденное число дней и получили полночь дня релиза. Так работают счётчики «осталось N дней» в интерфейсах: разница, поле days, при необходимости — обратное смещение для проверок. Возраст, стаж и рабочие дни строятся на том же приёме — им посвящены урок 16 и урок 17.
Отрицательная разница — это тоже разница
Пока разница была положительной, всё выглядело безобидно. Но дедлайны случаются и в прошлом: просроченный счёт, отменённый заказ, событие, которое началось раньше, чем закончилось. Вычитание в обратную сторону даёт timedelta с минусом, и вот тут начинается разница между «просто посмотреть» и «посчитать правильно». Поле days становится отрицательным, а вот total_seconds() честно отдаёт минус секунд — со знаком.
from datetime import datetime
late = datetime(2026, 10, 24)
early = datetime(2026, 10, 21)
delta = early - late
print(delta)
print(delta.days)
print(delta.total_seconds())
-3 days, 0:00:00 -3 -259200.0
Работать с таким знаком надо аккуратно, потому что timedelta хранит длительность в нормализованном виде: дни отдельно, остаток секунд отдельно, и остаток всегда неотрицательный. Для минус трёх дней это выглядит как days = -3, а не «-3 дня и минус 259200 секунд». Поэтому проверка «просрочено или нет» делается сравнением самих дат, а не знаком разницы: early < late читается прямо и не требует разбираться в нормализации.
36 часов — это 1 день и 12 часов
Ещё одно место, где datetime удивляет. Длительность нормализуется: сколько бы часов вы ни задали, timedelta хранит их как целые сутки плюс остаток. timedelta(hours=36) и timedelta(days=1.5) — это один и тот же объект, и оба печатаются как 1 day, 12:00:00. Дробные дни разрешены и равны часам ровно, потому что в сутках ровно 24 часа, а не 23 и не 25.
from datetime import timedelta
print(timedelta(hours=36))
print(timedelta(days=1.5))
print(timedelta(hours=2) * 3)
1 day, 12:00:00 1 day, 12:00:00 6:00:00
Умножение и деление работают как с числами, и это иногда не то, что ожидаешь. Делить timedelta на timedelta можно, но получится обычное число — отношение двух длительностей, а не длительность. Деление timedelta на число даёт timedelta: шесть часов делённое на два — это три часа.
from datetime import timedelta
work = timedelta(hours=6)
total = timedelta(hours=10)
print(round(work / total * 100))
print(total - work)
print(round(total.total_seconds() / 3600, 1))
60 4:00:00 10.0
Полные сутки против календарных
Поле days отвечает на вопрос «сколько полных суток прошло», и это не то же самое, что «сколько суток от дедлайна». Между ними может быть полсуток. Если до дедлайна осталось 30 часов, days покажет 1, а по календарю это полтора дня. Для счётчика «осталось N дней» в интерфейсе обычно нужен второй вариант — округление вверх: осталось 2 дня, потому что 30 часов это больше суток. Считать надо явно, а не полагаться на то, что поле days даст «правильное» число.
from datetime import timedelta
delta = timedelta(hours=30)
print(delta)
print(delta.days)
print(round(delta.total_seconds() / 86400, 2))
1 day, 6:00:00 1 1.25
Кстати, делить на 86400 — число секунд в сутках — приходится постоянно, и это нормально. Отдельного метода «сколько суток дробью» у timedelta нет, а total_seconds() даёт секунды, из которых сутки приходится вычислять. Мы держим в проектах константу SECONDS_PER_DAY = 86400, чтобы магическое число не встречалось в коде вперемешку с календарными датами.
timedelta в счёте: дни, минуты и граница месяца
timedelta умеет всё, что связано с интервалами: складываться, вычитаться, умножаться на число, делиться на другое timedelta. Но у него есть одно свойство, о котором стоит знать заранее: он хранит дни, секунды и микросекунды, а месяцев и годов в нём нет.
Поэтому timedelta(days=30) — это 30 дней, а не месяц. Для примера «дедлайн через месяц» это даст другую дату, чем «через 30 дней»: 15 марта плюс месяц — это 15 апреля, а плюс 30 дней — 14 апреля, потому что в марте 31 день. Разница в один-три дня, и именно на таких расчётах забывают про платежи.
Практическое правило: если в задаче фигурирует календарный период (месяц, квартал, год), считайте через границы периода — первый день месяца и первый день следующего, — а не через timedelta с числом дней. Отдельный модуль calendar подсказывает, сколько дней в конкретном месяце, и про него отдельный урок 15. Если же речь о точной длительности (смена длится 12 часов, дедлайн через 3 дня) — timedelta идеален.
И ещё полезная деталь, на которой мы спотыкались: делить timedelta на timedelta можно, но обычное деление даёт дробь, а целочисленное — число полных единиц. delta / timedelta(minutes=1) вернёт 330.0, а delta // timedelta(minutes=1) — 330. Второе и нужно для отчёта «сколько полных минут заняла обработка»: первое округлит 330.5 минут до 330.5 и потеряет половину минуты.
from datetime import timedelta
delta = timedelta(hours=5, minutes=31)
print(delta // timedelta(minutes=1))
print(delta / timedelta(minutes=1))
print(delta // timedelta(hours=1))
331 331.0 5
Сначала предскажи ответ в голове — это главный навык программиста.
from datetime import datetime
start = datetime(2026, 10, 21, 12, 0)
end = datetime(2026, 10, 22, 15, 30)
print((end - start).days)
from datetime import datetime, timedelta
moment = datetime(2026, 10, 21, 12, 0)
print(moment + timedelta(days=7))
from datetime import datetime
now = datetime(2026, 10, 23)
release = datetime(2026, 11, 1, 12, 0)
left = release - now
print(left)
from datetime import datetime, timedelta
now = datetime(2026, 10, 21)
release = datetime(2026, 11, 1, 12, 0)
left = release - now
print(left.days)
print(round(left.total_seconds() / 86400, 2))
1. Что вернёт выражение datetime(2026, 11, 1) - datetime(2026, 10, 21)?
2. Что означает поле delta.seconds у timedelta(days=2, hours=3)?
3. Как получить всю длительность timedelta одним числом?
4. Что получится, если к datetime прибавить timedelta(days=7)?
5. Какая длительность у timedelta(hours=5, minutes=30)?
6. Что вернёт timedelta(hours=36)?
7. Что вернёт выражение timedelta(hours=6) / timedelta(hours=10)?
8. У timedelta(hours=30) какое значение поля days?
Тренировка началась в 21 октября 2026 года в 19:00 и закончилась в тот же день в 21:30. Выведи длительность, общее число секунд длительности и момент следующей тренировки — ровно через 3 дня после первой.
Как посчитать разницу между двумя датами в Python?
Просто вычесть: d2 - d1 вернёт объект timedelta. Поле .days даст полные сутки разницы, total_seconds() — всю длительность в секундах. Для дат без времени разница всегда целодневная: date(2026, 11, 1) - date(2026, 10, 21) — это 11 days, 0:00:00.
Урок 6: timedeltaПочему delta.seconds показывает не все секунды?
Потому что timedelta хранится как комбинация дней и остатка: days — полные сутки, seconds — остаток от 0 до 86399 внутри последних суток. Вся длительность одним числом — только total_seconds(). Для timedelta(days=2, hours=3) поле seconds — 10800, а total_seconds() — 183600.0.
Как прибавить к дате несколько дней?
Создать timedelta и сложить: moment + timedelta(days=7) — момент на неделю позже. Работает с датами, временем, днями, часами, минутами и секундами одновременно: timedelta(days=1, hours=6, minutes=30).
Может ли timedelta быть отрицательным?
Да: вычитание более позднего момента из более раннего даст отрицательную длительность, например timedelta(days=-1). Поле days при этом отрицательное, а seconds остаётся неотрицательным хвостом; для «просрочено или нет» проще сравнивать сами даты — об этом урок 7.
Урок 7: сравнение датКак посчитать процент выполненной работы через timedelta?
Разделить одну длительность на другую и умножить на 100: round(work / total * 100). Деление timedelta на timedelta даёт обычное число, а не длительность. Round нужен обязательно: без него получится 59.99999999999999 из-за двоичной точности.
Почему timedelta(hours=36) печатается как 1 day, 12:00:00?
Длительность всегда нормализуется: целые сутки плюс остаток. Поэтому timedelta(hours=36) и timedelta(days=1.5) — один и тот же объект, и любые timedelta с тем же количеством секунд сравниваются между собой как равные.
Что означает отрицательный timedelta?
Что разница посчитана в обратную сторону: более ранний момент минус более поздний. Поле days при этом отрицательное, а остаток секунд — неотрицательный, потому что timedelta хранит длительность в нормализованном виде. Чтобы проверить «просрочено ли», сравнивайте сами даты: это читается прямо.
Понравился урок? Сошлитесь на него
«delta.seconds — это остаток внутри последних суток, а не всего секунд: для полного числа есть total_seconds().»
Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.
Что дальше
Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.
Что читать дальше
Datetime · Урок 3
now и today: текущий момент
Фиксированные даты хороши для примеров, а для логов и дедлайнов нужен настоящий сейчас. Учимся получать текущий момент — и понимать, почему вывод перестаёт быть предсказуемым.
Datetime · Урок 5
strptime: строка превращается в дату
Лог, форма и таблица присылают даты строками — для вычислений нужен объект. Учим strptime читать строку по шаблону и отличать настоящий формат от почти настоящего.
Datetime · Урок 7
Сравнение и сортировка дат
Даты сравниваются как числа — и потому сортируются, выбираются min и max и становятся ключами словаря. Разбираем операторы и честную сортировку без строковых сюрпризов.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
openpyxl · Урок 9
Даты в Excel через openpyxl: запись, формат и арифметика
Пишем в ячейку настоящий datetime, показываем его по-русски маской DD.MM.YYYY и считаем дни между датами — а заодно узнаём, почему дату из файла нельзя сравнивать со строкой.
openpyxl датыopenpyxl дата в ячейке
NumPy · Урок 3
Индексация и срезы массивов NumPy: как достать любой элемент
Индексы с нуля и с конца, срезы с шагом, строки и столбцы двумерных массивов — и главный подводный камень NumPy: срез возвращает view, а не копию. С примерами, разбором ошибок и шпаргалкой.
разница между copy и view в numpy
Datetime · Урок 16
Возраст и разница в днях
Полный возраст из даты рождения с учётом того, был ли день рождения в этом году, дни до события и недели из timedelta — три расчёта, которые нужны в любом проекте с датами.
разница дат pythonpython разница между датами в днях