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 не правит поля, а возвращает новый объект. Изменяемость тут была бы опасна: одна правка поля испортила бы все переменные, указывающие на объект. Неизменяемость делает даты безопасными для передачи в функции.
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:
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: сортировать, сравнивать, форматировать.
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.
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 суток вперёд, независимо от того, сколько дней в месяцах. Разница проявляется на конкретных датах, и она часто оказывается ровно тем, что нужно.
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 дней» — это разные вопросы, и на квартальном отчёте разница в один день обычно видна глазом.
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. Рабочий рецепт другой: сначала свести дату к первому дню месяца, потом отступить назад на один день.
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))
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. Как получить начало суток из момента?
Соберите встречу meeting из date(2026, 10, 21) и time(9, 0), выведите её. Затем через replace перенесите встречу на 18:30, сохраните в переменную moved и выведите её.
Как собрать 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-канал или свой блог — так о проекте узнают новые читатели.
Что дальше
Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.
Что читать дальше
Datetime · Урок 10
isoformat и fromisoformat: ISO-даты
Один вызов — и datetime превращается в строку 2026-10-21T14:30:45. ISO-формат сортируется как текст, а fromisoformat читает его обратно без единого кода %.
Datetime · Урок 12
weekday и isoweekday: дни недели
weekday() считает с нуля: 0 — понедельник. isoweekday() — с единицы, по стандарту ISO-8601. Достаём день недели из даты, переводим в «среду» и находим ближайший понедельник.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
Pandas · Урок 9
Даты и временные ряды в Pandas: resample и rolling
Строки «01.07.2024» превращаются в настоящие даты, выручка агрегируется по неделям и месяцам через resample, а шум сглаживает rolling — строим тренд продаж магазина.
даты и время в pandas to_datetime примерыset_index datetime pandas
openpyxl · Урок 9
Даты в Excel через openpyxl: запись, формат и арифметика
Пишем в ячейку настоящий datetime, показываем его по-русски маской DD.MM.YYYY и считаем дни между датами — а заодно узнаём, почему дату из файла нельзя сравнивать со строкой.
openpyxl датыopenpyxl datetime
Datetime · Урок 2
date, time и datetime: три класса
День рождения без часов, будильник без календаря, лог-запись со всем сразу. Три класса модуля datetime — и правило, какой выбрать под задачу.
date time datetime pythonразница date и datetime