← Назад к вопросам

Как работать с timezone в Python?

1.8 Middle🔥 161 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Работа с временными зонами в Python

Время и часовые пояса — одна из самых сложных задач в программировании. Python предоставляет несколько инструментов для работы с ними правильно.

Основные понятия

Naive и Aware datetime

from datetime import datetime
import pytz

# Naive datetime — без информации о временной зоне
naive_dt = datetime(2024, 3, 22, 14, 30, 0)
print(naive_dt.tzinfo)  # None
print(naive_dt.isoformat())  # '2024-03-22T14:30:00'

# Aware datetime — с информацией о временной зоне
aware_dt = datetime(2024, 3, 22, 14, 30, 0, tzinfo=pytz.UTC)
print(aware_dt.tzinfo)  # UTC
print(aware_dt.isoformat())  # '2024-03-22T14:30:00+00:00'

Получение текущего времени

✓ Правильный способ — UTC

from datetime import datetime, timezone

# Получить текущее UTC время (Aware)
current_utc = datetime.now(timezone.utc)
print(current_utc)  # 2024-03-22 12:45:30.123456+00:00

# Это предпочтительный способ — не зависит от системного времени

✗ Неправильный способ

# Не используй это!
naive_now = datetime.now()  # Naive datetime, ненадёжно
utc_now = datetime.utcnow()  # DEPRECATED в Python 3.12+

Работа с временными зонами (pytz)

import pytz
from datetime import datetime

# Получить список всех временных зон
print(pytz.all_timezones)  # выведет все зоны

# Создать datetime с конкретной зоной
moscow_tz = pytz.timezone('Europe/Moscow')
dt_moscow = moscow_tz.localize(datetime(2024, 3, 22, 14, 30, 0))
print(dt_moscow)  # 2024-03-22 14:30:00+03:00

# Или через replace (менее надёжный способ)
dt_moscow = datetime(2024, 3, 22, 14, 30, 0, tzinfo=moscow_tz)

Преобразование между зонами

from datetime import datetime, timezone
import pytz

# Создать время в UTC
utc_time = datetime(2024, 3, 22, 12, 0, 0, tzinfo=pytz.UTC)
print(f"UTC: {utc_time}")  # 2024-03-22 12:00:00+00:00

# Преобразовать в другую зону
moscow_tz = pytz.timezone('Europe/Moscow')
moscow_time = utc_time.astimezone(moscow_tz)
print(f"Moscow: {moscow_time}")  # 2024-03-22 15:00:00+03:00

# Преобразовать в токийское время
tokyo_tz = pytz.timezone('Asia/Tokyo')
tokyo_time = utc_time.astimezone(tokyo_tz)
print(f"Tokyo: {tokyo_time}")  # 2024-03-22 21:00:00+09:00

Работа с zoneinfo (Python 3.9+)

from datetime import datetime, timezone
from zoneinfo import ZoneInfo

# Новый способ (рекомендуется для Python 3.9+)
moscow_tz = ZoneInfo("Europe/Moscow")
dt_moscow = datetime(2024, 3, 22, 14, 30, 0, tzinfo=moscow_tz)
print(dt_moscow)  # 2024-03-22 14:30:00+03:00

# Преобразование
utc_time = datetime(2024, 3, 22, 12, 0, 0, tzinfo=ZoneInfo("UTC"))
tokyo_time = utc_time.astimezone(ZoneInfo("Asia/Tokyo"))
print(tokyo_time)  # 2024-03-22 21:00:00+09:00

Парсинг строк с временем

from datetime import datetime
from zoneinfo import ZoneInfo
import dateutil.parser

# Стандартный способ
dt_str = "2024-03-22T14:30:00+03:00"
dt = datetime.fromisoformat(dt_str)
print(dt)  # 2024-03-22 14:30:00+03:00

# Более гибкий парсинг с dateutil
dt = dateutil.parser.isoparse("2024-03-22T14:30:00+03:00")
dt = dateutil.parser.parse("22 March 2024 at 2:30 PM")  # Гибкий парсинг
print(dt)  # 2024-03-22 14:30:00

Работа с текущим временем в разных зонах

from datetime import datetime, timezone
from zoneinfo import ZoneInfo

# Текущее время в UTC
now_utc = datetime.now(timezone.utc)
print(f"UTC: {now_utc}")

# Текущее время в конкретной зоне
now_moscow = datetime.now(ZoneInfo("Europe/Moscow"))
print(f"Moscow: {now_moscow}")

now_tokyo = datetime.now(ZoneInfo("Asia/Tokyo"))
print(f"Tokyo: {now_tokyo}")

Сравнение datetime с разными зонами

from datetime import datetime, timezone
from zoneinfo import ZoneInfo

# Создать два времени в разных зонах
dt1 = datetime(2024, 3, 22, 12, 0, 0, tzinfo=timezone.utc)
dt2 = datetime(2024, 3, 22, 15, 0, 0, tzinfo=ZoneInfo("Europe/Moscow"))

# Сравнение автоматически учитывает временные зоны
print(dt1 == dt2)  # True! Оба представляют одно время
print(dt1 < dt2)   # False

Работа с базой данных

PostgreSQL (рекомендуется)

from datetime import datetime, timezone
from sqlalchemy import Column, DateTime
from sqlalchemy.orm import declarative_base

Base = declarative_base()

class Event(Base):
    __tablename__ = "events"
    
    # TIMESTAMPTZ — хранит время с информацией о зоне
    created_at = Column(DateTime(timezone=True), default=datetime.now(timezone.utc))

# При работе с БД — всегда используй UTC
event = Event(created_at=datetime.now(timezone.utc))
session.add(event)
session.commit()

# При получении из БД — конвертируй в нужную зону
local_time = event.created_at.astimezone(ZoneInfo("Europe/Moscow"))

Сериализация для API (JSON)

from datetime import datetime, timezone
from zoneinfo import ZoneInfo
import json

# ✓ Правильно — сохранять ISO 8601 с UTC
dt = datetime.now(timezone.utc)
iso_string = dt.isoformat()  # '2024-03-22T12:45:30.123456+00:00'
json_data = json.dumps({"timestamp": iso_string})

# ✓ При получении — парсить и конвертировать
data = json.loads(json_data)
dt_received = datetime.fromisoformat(data["timestamp"])
local_time = dt_received.astimezone(ZoneInfo("Europe/Moscow"))

# ✗ Не делай это — отправлять naive datetime
# naive_dt = datetime.now()  # ПЛОХО!

Django ORM

# settings.py
USE_TZ = True  # ВСЕГДА True для работы с временными зонами
TIME_ZONE = 'Europe/Moscow'  # Зона для отображения в админке

# models.py
from django.db import models
from django.utils import timezone

class Article(models.Model):
    # DateTimeField автоматически создаёт TIMESTAMPTZ
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

# views.py
from django.utils import timezone

# Получить текущее время
now = timezone.now()  # Всегда UTC, aware

# Сравнивать с timezone-aware datetime
articles = Article.objects.filter(created_at__gt=timezone.now())

FastAPI

from datetime import datetime, timezone
from pydantic import BaseModel, Field
from fastapi import FastAPI

app = FastAPI()

class EventSchema(BaseModel):
    name: str
    created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))

@app.post("/events")
async def create_event(event: EventSchema):
    # Pydantic автоматически парсит ISO 8601
    return event

# Клиент отправляет: {"name": "test", "created_at": "2024-03-22T12:00:00+00:00"}

Лучшие практики

# ✓ ПРАВИЛЬНО
from datetime import datetime, timezone
from zoneinfo import ZoneInfo

# Используй UTC везде в коде
now = datetime.now(timezone.utc)

# Конвертируй в локальное время только на границе (API, шаблон)
local_time = now.astimezone(ZoneInfo("Europe/Moscow"))

# Сохраняй в БД как UTC
db.save(created_at=now)

# Отправляй в API как ISO 8601
response = {"timestamp": now.isoformat()}

# ✗ НЕПРАВИЛЬНО
# datetime.now()  # Naive datetime
# datetime.utcnow()  # Deprecated
# now.replace(tzinfo=timezone.utc)  # Неправильное преобразование!
# Хранить время как строку или число

Итого

Работа с временем в Python:

  • Используй UTC везде в коде приложения
  • datetime.now(timezone.utc) — текущее время
  • zoneinfo.ZoneInfo (Python 3.9+) или pytz для работы с зонами
  • Конвертируй в локальное время только на границах (API, UI)
  • Сохраняй в БД как UTC с типом TIMESTAMPTZ
  • Отправляй в API как ISO 8601 строки
  • Никогда не используй naive datetime в production