Работа с датой и временем в Python

Допустим, у нас лог-файл со строками вида 2026-05-20 14:30:42 ERROR ..., и нужно посчитать, сколько ошибок произошло за последние сутки. Для этого нужно: разобрать строку с датой в объект, посчитать разницу с «сейчас», и (если показывать пользователю) вернуть в его часовом поясе и привычном формате.

Эти три операции — парсинг строки, арифметика, форматирование — основа работы с датами в Python. Все они лежат в стандартном модуле datetime.

Три класса: datetime, date, time

В модуле datetime есть три основных класса, и важно понимать когда какой использовать:

  • datetime: конкретный момент времени с точностью до микросекунд. Используется в 90% случаев.
  • date: только дата, без времени. День рождения, дедлайн, дата заказа.
  • time: только время суток, без даты. Время встречи в календаре.
Python 3.13
from datetime import datetime, date, time

# Момент времени
now = datetime.now()
print(now)
2026-05-20 14:30:25.123456
# Только дата
birthday = date(1990, 5, 15)
print(birthday)
1990-05-15
# Только время
meeting = time(14, 30)
print(meeting)
14:30:00

У всех трёх классов есть отдельные атрибуты компонентов: .year, .month, .day, .hour, .minute, .second, .microsecond.

Python 3.13
from datetime import datetime

now = datetime(2026, 5, 20, 14, 30)
print(now.year, now.month, now.day)
2026 5 20
print(now.weekday())   # 0 = понедельник, 6 = воскресенье
2

Арифметика дат: timedelta

Прибавлять и вычитать даты можно напрямую: результат это timedelta (промежуток времени), либо новый datetime:

Python 3.13
from datetime import datetime, timedelta

now = datetime(2026, 5, 20, 14, 30)

# Прибавить интервал
week_later = now + timedelta(days=7)
print(week_later)
2026-05-27 14:30:00
# Разница между моментами это timedelta
deadline = datetime(2026, 6, 1)
delta = deadline - now
print(delta)
11 days, 9:30:00
print(delta.days, delta.total_seconds())
11 985800.0

timedelta принимает days, hours, minutes, seconds, weeks, но не months и years (потому что они переменной длины: в феврале 28 или 29 дней, в году 365 или 366). Для прибавления месяцев есть библиотека dateutil (внешняя).

strftime и strptime: между объектом и строкой

В реальном коде даты постоянно проходят через строки — API, логи, базы данных. Запомнить какой метод что делает помогает мнемоника:

  • strftimeformat: объект → строка
  • strptimeparse: строка → объект

Иллюстрация: слева строка 2026-05-20 14:30, стрелка strptime (parse) к datetime-объекту с year, month, day, hour, minute, далее стрелка strftime (format) к строке «20 мая 2026, 14:30»

Python 3.13
from datetime import datetime

# Объект → строка
now = datetime(2026, 5, 20, 14, 30)
print(now.strftime("%d.%m.%Y %H:%M"))
20.05.2026 14:30
print(now.strftime("%A, %d %B %Y"))
Wednesday, 20 May 2026
# Строка → объект
parsed = datetime.strptime("20.05.2026 14:30", "%d.%m.%Y %H:%M")
print(parsed)
2026-05-20 14:30:00

Формат описывается строкой с директивами вида %Y, %m, %d:

ДирективаОписаниеПример
%YГод, 4 цифры2026
%yГод, 2 цифры26
%mМесяц (01-12)05
%BПолное название месяцаMay
%bСокращённое название месяцаMay
%dДень месяца (01-31)20
%AПолное название дня неделиWed
%aСокращённое название дняWed
%HЧас (00-23)14
%IЧас (01-12)02
%MМинуты (00-59)30
%SСекунды (00-59)25

ISO 8601: стандарт обмена датами

Если дату нужно передать между системами (API, JSON, базы данных), используется ISO 8601: 2026-05-20T14:30:00. У datetime есть готовые методы для этого формата, и они быстрее и надёжнее, чем strftime/strptime:

Python 3.13
from datetime import datetime

now = datetime(2026, 5, 20, 14, 30)

# В ISO-строку
iso_string = now.isoformat()
print(iso_string)
2026-05-20T14:30:00
# Обратно
parsed = datetime.fromisoformat("2026-05-20T14:30:00")
print(parsed)
2026-05-20 14:30:00

Правило: внутри программы держите даты как datetime-объекты, при выводе наружу (в JSON, в базу) — .isoformat(), при чтении снаружи — fromisoformat(). Свой формат с strftime нужен только когда мы показываем дату пользователю.

Часовые пояса: naive vs aware

datetime.now() без аргумента возвращает «наивный» (naive) datetime — у него нет информации о часовом поясе. Это распространённая грабля: программа работает «дома», а на сервере в другой стране внезапно показывает время на 7 часов раньше.

Правильно работать с aware datetime, у которого часовой пояс есть. С Python 3.9 для этого есть модуль zoneinfo, который умеет в реальные часовые пояса с учётом перехода на летнее время:

Python 3.13
from datetime import datetime
from zoneinfo import ZoneInfo

# Aware datetime в UTC и Москве
utc_now = datetime(2026, 5, 20, 14, 30, tzinfo=ZoneInfo("UTC"))
moscow_now = utc_now.astimezone(ZoneInfo("Europe/Moscow"))

print(utc_now)
2026-05-20 14:30:00+00:00
print(moscow_now)
2026-05-20 17:30:00+03:00

Названия зон стандартизированы (IANA tz database): "Europe/Moscow", "America/New_York", "Asia/Tokyo". Когда вы храните datetime в базе данных, типичная практика: всегда хранить в UTC, конвертировать в локальную зону пользователя только при выводе.

Раньше для часовых поясов использовали внешнюю библиотеку pytz. С Python 3.9 встроенный zoneinfo это покрывает, и pytz больше не нужен.

Модуль time

Помимо datetime, есть низкоуровневый модуль time. Два его метода встречаются часто:

Python 3.13
import time

# Текущий момент как Unix timestamp (секунд с 1 января 1970)
print(time.time())
1779373825.123456
# Пауза на N секунд
print("Начало")
time.sleep(0.1)
print("Прошло 0.1 секунды")
Начало
Прошло 0.1 секунды

time.time() возвращает Unix timestamp — это формат, в котором часто хранят время в базах и логах: одно число, не зависит от часового пояса. Перевести в datetime можно через datetime.fromtimestamp(ts, tz=ZoneInfo("UTC")).

Проверка понимания

Какой класс из модуля datetime используется для представления промежутка времени?


В дальнейшем datetime будет встречаться часто: при работе с API (даты приходят строкой ISO 8601), с базами данных (хранение моментов событий), при логировании. Главное правило: внутри программы держите даты как объекты, превращайте в строки только на границе с внешним миром.