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

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

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

combine и replace: datetime из частей

date знает день, time знает часы — combine склеивает их в datetime, а replace меняет отдельные поля и возвращает новый объект: datetime неизменяем.

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

Из строки datetime собирает fromisoformat, а как собрать его из частей? Объект date знает календарный день, но в нём всегда полночь; объект time знает часы, но не знает, к какому дню они относятся. Для склейки двух половинок в модуле есть метод combine.

combine: date плюс time

дата встречает время
from datetime import date, datetime, time

d = datetime.combine(date(2026, 10, 21), time(9, 0))
print(d)
print(repr(d))
Вывод
2026-10-21 09:00:00
datetime.datetime(2026, 10, 21, 9, 0)

datetime.combine берёт календарный день у первого аргумента и время у второго. repr показывает это буквально: datetime.datetime(2026, 10, 21, 9, 0) — дата от date(2026, 10, 21), часы с минутами от time(9, 0). print печатает 09:00:00: ведущий нуль перед девяткой и секунды появляются сами — строковый вывод datetime всегда в одном формате.

replace: точечная правка полей

меняем только нужное
from datetime import datetime

d = datetime(2026, 10, 21, 14, 30)
evening = d.replace(hour=18, minute=0)
print(d)
print(evening)
print(d == evening)
Вывод
2026-10-21 14:30:00
2026-10-21 18:00:00
False

replace принимает только те поля, которые меняем: час стал 18, минута 0, а день, месяц, год и секунды остались как были. Важнее другое: исходный d не тронут и по-прежнему показывает 14:30. datetime неизменяем: replace не правит поля, а возвращает новый объект. Изменяемость тут была бы опасна: одна правка поля испортила бы все переменные, указывающие на объект. Неизменяемость делает даты безопасными для передачи в функции.

combine и replace в связке
from datetime import date, datetime, time

d = datetime.combine(date(2026, 10, 21), time(9, 0))
reminder = d.replace(hour=18, minute=30)
print(d)
print(reminder)
print(reminder.strftime('%H:%M'))
Вывод
2026-10-21 09:00:00
2026-10-21 18:30:00
18:30

Типичный конвейер: combine собрал встречу на утро, replace перенёс её на вечер — встреча-основа осталась нетронутой, рядом вырос новый объект. У date тоже есть replace — с полями year, month и day. Перенести дату на другой год можно без арифметики timedelta:

replace у date
from datetime import date

d = date(2026, 10, 21)
next_year = d.replace(year=2027)
print(d)
print(next_year)
Вывод
2026-10-21
2027-10-21

Итого по сборке: fromisoformat читает строку, combine склеивает date и time, replace правит поля по одному. Из готового datetime теперь можно доставать день недели — этим займётся следующий урок.

Расписание дня: combine в цикле

Типичное применение combine — расписание. У вас есть один рабочий день и список времени слотов: 9:00, 11:30, 15:00. Слоты без даты бесполезны: встреча в 9:00 в понедельник и в 9:00 в среду — разные события. Combine превращает каждую пару день-плюс-время в полноценный момент, и дальше с ними можно работать обычными средствами datetime: сортировать, сравнивать, форматировать.

schedule.py — слоты на день
from datetime import date, datetime, time

day = date(2026, 10, 21)
slots = [time(9, 0), time(11, 30), time(15, 0)]

for slot in slots:
    print(datetime.combine(day, slot))
Вывод
2026-10-21 09:00:00
2026-10-21 11:30:00
2026-10-21 15:00:00

Обратите внимание на порядок аргументов: сначала дата, потом время. Перепутать их невозможно — combine проверит типы и выбросит TypeError, если вы подали datetime вместо date, как мы видели в уроке 2. Это тот случай, когда строгая проверка типов спасает: ошибка приходит в момент сборки, а не через месяц в отчёте.

replace проверяет календарь, а не только типы

Казалось бы, replace — простая операция: «возьми тот же момент, но с другим месяцем». Но календарь не даёт менять месяц как заблагорассудится. Тридцать первого января нельзя перенести в февраль: там тридцать дней, а то и двадцать восемь в високосном году. Python это проверяет и падает с ValueError.

replace_month.py — 31 января в феврале
from datetime import datetime

try:
    print(datetime(2026, 1, 31).replace(month=2))
except ValueError:
    print('в феврале нет 31-го')
Вывод
в феврале нет 31-го

Обратите внимание на год: проверка зависит от високосности, и 2027 год в феврале имеет 28 дней, а 2028 — 29. Та же самая строка кода в один год сработает, а в другой упадёт. Если такая замена нужна в цикле по месяцам, разумнее не полагаться на replace, а взять последний день месяца явно — через calendar.monthrange, о котором пойдёт речь в уроке 15.

replace или timedelta: два разных сдвига

Два способа сдвинуть дату выглядят похоже, но означают разное. replace меняет календарное поле: месяц сменился на апрель, день остался тем же. timedelta сдвигает момент на промежуток: «плюс 30 дней» — это ровно 30 суток вперёд, независимо от того, сколько дней в месяцах. Разница проявляется на конкретных датах, и она часто оказывается ровно тем, что нужно.

shift.py — месяц против тридцати дней
from datetime import datetime, timedelta

d = datetime(2026, 3, 15)

print(d.replace(month=4))
print(d + timedelta(days=30))
Вывод
2026-04-15 00:00:00
2026-04-14 00:00:00

Пятнадцатое марта плюс месяц — это пятнадцатое апреля, а пятнадцатое марта плюс 30 дней — четырнадцатое апреля, потому что в марте 31 день. Выбор за вами, но полезно понимать, что спрашивает заказчик: «следующий месяц» и «через 30 дней» — это разные вопросы, и на квартальном отчёте разница в один день обычно видна глазом.

midnight.py — сброс времени в полночь
from datetime import datetime

d = datetime(2026, 10, 21, 14, 30, 45)
start = d.replace(hour=0, minute=0, second=0, microsecond=0)

print(d)
print(start)
Вывод
2026-10-21 14:30:45
2026-10-21 00:00:00

Кстати, цепочка replace — обычный приём для обнуления времени. Чтобы получить начало суток, убирают все четыре поля разом: hour, minute, second, microsecond. А чтобы получить конец суток, заменяют на 23, 59, 59 и 999999. Такие «якорные» даты удобны для запросов в базе вида «всё, что попало в этот день».

combine отвечает за сборку, replace — за правку: одно собирает момент из частей, другое меняет поле в готовом, и обе операции возвращают новый объект, а не правят старый.

combine и replace: собрать дату из частей и заменить часть

Два метода класса datetime закрывают задачи, которые иначе требуют ручной сборки. Разница между ними укладывается в одно предложение: combine ничего не меняет, а собирает; replace ничего не собирает, а меняет.

combine() берёт календарный день у первого аргумента и время у второго: datetime.combine(date(2026, 10, 30), time(18, 45)) даёт полный datetime с той же датой и нужным временем. Метод зовётся у класса datetime, а не у date: у самого date такого метода нет, и попытка вызвать его на date закончится AttributeError. Это удобно, когда дата пришла из документа, а время вы задаёте сами.

replace() меняет поля: moment.replace(year=2027, month=1) даёт тот же момент с другим годом и месяцем. Ключевое, что стоит помнить, — объект datetime неизменяем, поэтому replace ничего не правит на месте, а возвращает новый объект; исходный остаётся нетронутым, и это важно, если он где-то уже используется.

Из двух методов получается типовой сценарий «последний день месяца», и тут есть подвох, на котором мы уже споткнулись в продакшене. Очевидное решение — заменить месяц на следующий и отнять от результата день. Оно не работает: у 31 января нет 31 февраля, и replace выбросит ValueError. Рабочий рецепт другой: сначала свести дату к первому дню месяца, потом отступить назад на один день.

end_of_month.py — последний день месяца
from datetime import date, timedelta

d = date(2026, 1, 31)

first_of_next = d.replace(day=1) + timedelta(days=32)
last_day = first_of_next.replace(day=1) - timedelta(days=1)

print(last_day)
Вывод
2026-01-31

Рецепт выглядит неуклюже, но он единственный, который не ломается на високосном году: 32 дня вперёд от первого числа всегда переносят в следующий месяц, а шаг назад приземляет ровно на последнее число. Мы в проектах держим его отдельной функцией — end_of_month(d) — потому что писать эти четыре строки в пяти местах означает получить пять копий, одна из которых рано или поздно будет отличаться.

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

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

from datetime import datetime

d = datetime(2026, 10, 21, 14, 30)
print(d.replace(hour=9))
from datetime import date, datetime, time

print(datetime.combine(date(2026, 10, 21), time(0, 15)))
from datetime import date

d = date(2026, 10, 21)
print(d.replace(day=1))
from datetime import date, datetime, time

day = date(2026, 10, 21)
slots = [time(9, 0), time(15, 0)]

for slot in slots:
    print(datetime.combine(day, slot))
Проверь себя
0 / 8

1. Что вернёт datetime.combine(date(2026, 10, 21), time(9, 0))?

2. Что делает d.replace(hour=18)?

3. Что будет в d после строк d = datetime(2026, 10, 21, 14, 30); d.replace(hour=9)?

4. Какие поля умеет менять replace у объекта date?

5. Чем combine отличается от replace?

6. Что произойдёт при datetime(2026, 1, 31).replace(month=2)?

7. Чем d.replace(month=4) отличается от d + timedelta(days=30) для даты 15 марта?

8. Как получить начало суток из момента?

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

Соберите встречу meeting из date(2026, 10, 21) и time(9, 0), выведите её. Затем через replace перенесите встречу на 18:30, сохраните в переменную moved и выведите её.

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

Как собрать datetime из даты и времени по отдельности?

Методом datetime.combine: он принимает объект date и объект time и возвращает datetime. Например, datetime.combine(date(2026, 10, 21), time(9, 0)) — 21 октября 2026 года, 09:00 утра. Обратная операция — атрибуты .date() и .time() у готового datetime.

Как изменить час или минуту в datetime?

Методом replace: d.replace(hour=18, minute=30) вернёт новый объект с нужным временем. Меняются только перечисленные поля. Результат обязательно сохраняйте в переменную — datetime неизменяем, сам себя он не перепишет.

Почему datetime нельзя изменить на месте?

Объекты datetime спроектированы неизменяемыми, как строки и кортежи: это защищает от случайных правок общего объекта и позволяет использовать даты как ключи словарей. Любое изменение — replace, арифметика с timedelta — создаёт новый объект, исходный остаётся нетронутым.

Урок 12: weekday и isoweekday

Как заменить год у даты в Python?

Тем же replace: date(2026, 10, 21).replace(year=2027) вернёт 2027-10-21. У date доступны поля year, month и day, у datetime — плюс часы, минуты, секунды и микросекунды. Для сдвига на несколько дней или месяцев удобнее арифметика с timedelta.

Что будет, если заменить месяц у даты, в которой нет такого дня?

ValueError: day is out of range for month. datetime(2026, 1, 31).replace(month=2) так и упадёт — 31 февраля не бывает ни в одном году. Если нужна последняя дата месяца, берите её явно через calendar.monthrange.

Урок 15: модуль calendar

Как собрать datetime из объектов date и time?

Методом datetime.combine: datetime.combine(date(2026, 10, 21), time(9, 0)). Он же проверит типы аргументов и выбросит TypeError, если вы подали datetime вместо date. Удобно для расписаний, где есть список слотов времени и один день.

Чем replace отличается от timedelta?

replace меняет календарное поле и сохраняет остальные: 15 марта с месяцем апрель даёт 15 апреля. timedelta сдвигает момент на длительность: те же 15 марта плюс 30 дней дают 14 апреля, потому что в марте 31 день. «Следующий месяц» и «через 30 дней» — разные вопросы.

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

«datetime неизменяем: replace не правит поля, а возвращает новый объект.»

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

TelegramVK

Что дальше

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

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

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