Очень часто бывает, что практически все знают, как надо делать правильно, но при этом всё равно постоянно делают неправильно. Один из таких случаев — сохрание времени в базе данных. Понятно, что на персональном блоге вполне можно обойтись наивных подходом, не учитывающим перевод времени. Но для круглосуточно работающих приложений строгой системы отчётности вроде биллинга это неприемлемо.
Я не буду здесь рассматривать все возможные варианты корректной работы со временем. Покажу лишь насколько просто можно реализовать самый распространённый вариант — хранение в базе в UTC — на примере SQLAlchemy. Для этого достаточно определить новый тип колонки:
from sqlalchemy import types
from dateutil.tz import tzutc
from datetime import datetime
class UTCDateTime(types.TypeDecorator):
impl = types.DateTime
def process_bind_param(self, value, engine):
if value is not None:
return value.astimezone(tzutc()).replace(tzinfo=None)
def process_result_value(self, value, engine):
if value is not None:
return datetime(value.year, value.month, value.day,
value.hour, value.minute, value.second,
value.microsecond, tzinfo=tzutc())
Теперь вы можете сохранять время с произвольной зоной, все преобразования будут сделаны автоматически. Но сохранить время без зоны не получится — метод astimezone()
выбросит исключение ValueError
, что позволит избежать случайных ошибок.