← Назад к вопросам
Какие знаешь инструменты для подсчета даты и времени через несколько часов?
2.0 Middle🔥 131 комментариев
#Python Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Инструменты для работы с датой и временем в Python
Работать с датами и временем нужно аккуратно. Правильный выбор инструмента избавляет от множества ошибок.
1. datetime (встроенный модуль)
datetime — основной модуль стандартной библиотеки для работы с датами и временем.
from datetime import datetime, timedelta, timezone
# Текущее время (осторожно — naive datetime!)
now_naive = datetime.now() # ❌ Без временной зоны
print(f"Naive now: {now_naive}")
# Правильно — с временной зоной
now_aware = datetime.now(timezone.utc) # ✅ С UTC
print(f"Aware now: {now_aware}")
# Добавить часов
future = now_aware + timedelta(hours=5)
print(f"Through 5 hours: {future}")
# Также можно:
future = now_aware + timedelta(
days=1,
hours=3,
minutes=30,
seconds=45,
milliseconds=100,
microseconds=50
)
# Разница между датами
difference = future - now_aware
print(f"Difference: {difference}")
print(f"Days: {difference.days}")
print(f"Seconds: {difference.total_seconds()}")
Проблемы с datetime:
- Naive datetimes (без timezone) — источник ошибок
- Нет встроенной работы с временными зонами
- Сложная парсинг форматов
2. pytz (работа с временными зонами)
import pytz
from datetime import datetime
# Список доступных зон
print(pytz.all_timezones) # Сотни зон
# Создание datetime в конкретной зоне
from pytz import timezone
moscow_tz = timezone('Europe/Moscow')
now_moscow = datetime.now(moscow_tz)
print(f"Moscow time: {now_moscow}")
# Конвертация между зонами
us_tz = timezone('America/New_York')
now_us = now_moscow.astimezone(us_tz)
print(f"New York time: {now_us}")
# Добавить часов в конкретной зоне
from datetime import timedelta
future = now_moscow + timedelta(hours=5)
print(f"Moscow + 5 hours: {future}")
# Пример: ночной перерыв в компании
class BusinessHours:
def __init__(self, tz_name: str):
self.tz = timezone(tz_name)
def add_business_hours(self, dt: datetime, hours: int) -> datetime:
"""Добавить рабочие часы (9:00-17:00), пропускающие выходные"""
current = dt.replace(tzinfo=self.tz)
while hours > 0:
current += timedelta(hours=1)
# Если ночь (раньше 9:00) — пропускаем
if current.hour < 9:
current = current.replace(hour=9, minute=0, second=0)
# Если выходной или после 17:00 — следующий день
if current.hour >= 17 or current.weekday() >= 5:
current = current.replace(hour=9, minute=0, second=0) + timedelta(days=1)
hours -= 1
return current
bh = BusinessHours('Europe/Moscow')
current = datetime.now(timezone('Europe/Moscow'))
future = bh.add_business_hours(current, 5)
print(f"After 5 business hours: {future}")
3. dateutil (расширения datetime)
from dateutil import parser, rrule, relativedelta
from dateutil.tz import tzlocal, gettz
from datetime import datetime
# Парсинг разных форматов (очень гибко)
dates = [
"2024-03-22",
"22/03/2024",
"March 22, 2024",
"22-Mar-2024 15:30:00",
"2024-03-22T15:30:00+03:00",
]
for date_str in dates:
dt = parser.parse(date_str)
print(f"{date_str} -> {dt}")
# relativedelta — более удобно для смещений
from dateutil.relativedelta import relativedelta
now = datetime.now()
# Через 5 часов
through_hours = now + relativedelta(hours=5)
# Через 1 месяц
through_month = now + relativedelta(months=1)
# Следующий день в 9:00
next_day_9am = (now + relativedelta(days=1)).replace(hour=9, minute=0, second=0)
# Повторяющиеся события (напоминания)
for i, occurrence in enumerate(rrule.rrule(rrule.DAILY, count=5, dtstart=now)):
print(f"Occurrence {i+1}: {occurrence}")
# Каждый вторник
for i, occurrence in enumerate(rrule.rrule(
rrule.WEEKLY,
byweekday=rrule.TU, # Tuesday
count=5,
dtstart=now
)):
print(f"Tuesday {i+1}: {occurrence}")
# Каждый месяц в 10 число
for i, occurrence in enumerate(rrule.rrule(
rrule.MONTHLY,
bymonthday=10,
count=5,
dtstart=now
)):
print(f"10th of month {i+1}: {occurrence}")
4. Arrow (красивый API для дат)
import arrow
# Текущее время (удобнее, чем datetime)
now = arrow.now('Europe/Moscow')
print(now) # 2024-03-22T15:30:45.123456+03:00
# Через 5 часов
future = now.shift(hours=5)
print(future)
# Множество способов:
future = now.shift(
years=1,
months=2,
days=3,
hours=4,
minutes=5,
seconds=6
)
# Красивое форматирование
print(now.format('YYYY-MM-DD HH:mm:ss')) # 2024-03-22 15:30:45
print(now.format('dddd, MMMM Do YYYY')) # Friday, March 22nd 2024
# Парсинг
arrow_dt = arrow.get('2024-03-22T15:30:00+03:00')
print(arrow_dt)
# Гуманизированные временные интервалы
from arrow import now as arrow_now
past = arrow.now().shift(days=-3)
print(past.humanize()) # 3 days ago
future = arrow.now().shift(hours=2)
print(future.humanize()) # in 2 hours
# Конвертация между зонами
moscow = arrow.now('Europe/Moscow')
newyork = moscow.to('America/New_York')
print(f"Moscow: {moscow}")
print(f"New York: {newyork}")
5. pendulum (ещё более удобный Arrow)
import pendulum
# Самый эргономичный API
now = pendulum.now('Europe/Moscow')
print(now) # 2024-03-22T15:30:45.123456+03:00
# Через 5 часов (очень красиво)
future = now.add(hours=5)
print(future)
# Или так
future = now.add(days=1, hours=3, minutes=30)
# Гуманизированные интервалы (с русским языком!)
from pendulum import now as pnow
past = pnow().subtract(days=3)
print(past.diff_for_humans(locale='ru')) # "3 дня назад"
future = pnow().add(hours=2)
print(future.diff_for_humans(locale='ru')) # "в 2 часах"
# Красивое форматирование
print(now.format('dddd, MMMM D, YYYY HH:mm:ss')) # Friday, March 22, 2024 15:30:45
# Period (интервалы времени)
start = pendulum.parse('2024-01-01')
end = pendulum.parse('2024-03-22')
period = start.period_for_humans(end)
print(period) # 80 days
# Timezone aware by default
moscow_time = pendulum.now('Europe/Moscow')
newyork_time = moscow_time.in_timezone('America/New_York')
6. cron (планирование по расписанию)
from croniter import croniter
from datetime import datetime
# Парсинг cron выражений
cron_expr = "0 9 * * 1-5" # Каждый рабочий день в 9:00
cron = croniter(cron_expr)
# Следующие 5 запусков
for i in range(5):
next_run = cron.get_next(datetime)
print(f"Next run {i+1}: {next_run}")
# Проверить, когда будет следующий запуск
now = datetime.now()
cron = croniter('0 */3 * * *', now) # Каждые 3 часа
next_run = cron.get_next(datetime)
print(f"Next run in: {(next_run - now).total_seconds() / 3600:.1f} hours")
# Обратное: сколько будет следующий запуск
cron = croniter('0 9 * * *', now) # Каждый день в 9:00
sleep_time = (cron.get_next(datetime) - now).total_seconds()
print(f"Sleep {sleep_time} seconds until next job")
# APScheduler — для фоновых задач
from apscheduler.schedulers.background import BackgroundScheduler
scheduler = BackgroundScheduler()
def my_task():
print("Task executed!")
# Запускать каждый день в 9:00
scheduler.add_job(my_task, 'cron', hour=9)
# Запускать каждые 3 часа
scheduler.add_job(my_task, 'interval', hours=3)
# Запускать один раз через 1 час
scheduler.add_job(my_task, 'date', run_date=datetime.now() + timedelta(hours=1))
scheduler.start()
Практический пример: система напоминаний
import pendulum
from typing import Optional
from enum import Enum
class NotificationPriority(Enum):
LOW = 1
NORMAL = 2
HIGH = 3
URGENT = 4
class ReminderSystem:
def __init__(self, user_timezone: str = 'Europe/Moscow'):
self.tz = user_timezone
def schedule_reminder(self, message: str, delay_hours: int,
priority: NotificationPriority) -> dict:
"""Расписать напоминание через N часов"""
now = pendulum.now(self.tz)
scheduled_time = now.add(hours=delay_hours)
# Если ночь (раньше 8:00), отложить до 9:00
if scheduled_time.hour < 8:
scheduled_time = scheduled_time.start_of_day().add(hours=9)
# Если выходной — отложить на понедельник
if scheduled_time.weekday() == 6: # Воскресенье
scheduled_time = scheduled_time.next(pendulum.MONDAY).start_of_day().add(hours=9)
return {
"message": message,
"scheduled_at": scheduled_time.isoformat(),
"time_until": scheduled_time.diff_for_humans(now),
"priority": priority.name,
}
def get_reminders_for_today(self) -> list:
"""Получить все напоминания на сегодня"""
now = pendulum.now(self.tz)
return self.reminders.filter(
lambda r: pendulum.parse(r['scheduled_at']).date() == now.date()
)
# Использование
reminder_sys = ReminderSystem('Europe/Moscow')
result = reminder_sys.schedule_reminder(
"Отправить отчёт",
delay_hours=5,
priority=NotificationPriority.URGENT
)
print(result)
Сравнение инструментов
┌──────────────┬─────────────┬──────────────┬─────────────┬─────────┐
│ Библиотека │ Встроенная │ Удобство API │ Временные з │ Размер │
├──────────────┼─────────────┼──────────────┼─────────────┼─────────┤
│ datetime │ ✅ │ ⭐⭐ │ Плохо │ 0KB │
│ pytz │ ✗ │ ⭐⭐ │ ⭐⭐⭐⭐⭐ │ ~2KB │
│ dateutil │ ✗ │ ⭐⭐⭐⭐ │ ⭐⭐⭐ │ ~50KB │
│ arrow │ ✗ │ ⭐⭐⭐⭐⭐ │ ⭐⭐⭐⭐⭐ │ ~100KB │
│ pendulum │ ✗ │ ⭐⭐⭐⭐⭐ │ ⭐⭐⭐⭐⭐ │ ~50KB │
│ croniter │ ✗ │ ⭐⭐⭐⭐ │ ⭐⭐ │ ~30KB │
└──────────────┴─────────────┴──────────────┴─────────────┴─────────┘
Рекомендации
- Простые операции: datetime + timedelta
- Работа с зонами: pytz или pendulum
- Парсинг: dateutil.parser
- Красивый API: arrow или pendulum
- Планирование: croniter или APScheduler
- Production: Используй UTC внутри, конвертируй при отправке пользователю
Золотое правило: Всегда сохраняй UTC в БД, конвертируй в локальную зону только при отображении.