SQLAlchemy DateTime часовой пояс
Тип SQLAlchemy DateTime
позволяет аргументу timezone=True
сохранять в базе данных объект, не являющийся наивным datetime, и возвращать его как таковой. Есть ли способ изменить часовой пояс tzinfo
, который передает SQLAlchemy, так что это может быть, например, UTC? Я понимаю, что могу просто использовать default=datetime.datetime.utcnow
; тем не менее, это наивное время, которое с радостью согласится с тем, что кто-то перешел в наивную локальную дату и время, даже если я использовал timezone=True
с ним, потому что это делает время локального или UTC не наивным без базового часового пояса, чтобы нормализовать его с помощью, Я попытался (используя pytz), чтобы сделать объект datetime не наивным, но когда я сохраняю его в БД, он возвращается наивным.
Обратите внимание, как datetime.datetime.utcnow не работает с timezone=True
так хорошо:
import sqlalchemy as sa
from sqlalchemy.sql import select
import datetime
metadata = sa.MetaData('postgres://user:[email protected]/db')
data_table = sa.Table('data', metadata,
sa.Column('id', sa.types.Integer, primary_key=True),
sa.Column('date', sa.types.DateTime(timezone=True), default=datetime.datetime.utcnow)
)
metadata.create_all()
engine = metadata.bind
conn = engine.connect()
result = conn.execute(data_table.insert().values(id=1))
s = select([data_table])
result = conn.execute(s)
row = result.fetchone()
(1, datetime.datetime(2009, 1, 6, 0, 9, 36, 891887))
row[1].utcoffset()
datetime.timedelta(-1, 64800) #, что мое местное смещение!
datetime.datetime.now(tz=pytz.timezone("US/Central"))
datetime.timedelta(-1, 64800)
datetime.datetime.now(tz=pytz.timezone("UTC"))
datetime.timedelta(0) #UTC
Даже если я изменил его, чтобы явно использовать UTC:
...
data_table = sa.Table('data', metadata,
sa.Column('id', sa.types.Integer, primary_key=True),
sa.Column('date', sa.types.DateTime(timezone=True), default=datetime.datetime.now(tz=pytz.timezone('UTC')))
)
row[1].utcoffset()
...
datetime.timedelta(-1, 64800) # он не использовал часовой пояс, который я явно добавил
Или, если я опустил timezone=True
:
...
data_table = sa.Table('data', metadata,
sa.Column('id', sa.types.Integer, primary_key=True),
sa.Column('date', sa.types.DateTime(), default=datetime.datetime.now(tz=pytz.timezone('UTC')))
)
row[1].utcoffset() is None
...
True # он даже не сохранил часовой пояс для db на этот раз
Ответы
Ответ 1
http://www.postgresql.org/docs/8.3/interactive/datatype-datetime.html#DATATYPE-TIMEZONES
Все даты и время, относящиеся к времени, хранятся внутри UTC. Они будут преобразованы в локальное время в зоне, заданной параметром конфигурации часового пояса, перед тем, как отобразить его клиенту.
Единственный способ сохранить его с помощью postgresql - хранить его отдельно.
Ответ 2
решение дано в этом вопросе:
вы можете обойти это, сохранив все объекты времени (даты) в своей базе данных в UTC и преобразовывая полученные наивные объекты datetime в известные из них при поиске.
Единственным недостатком является то, что вы теряете информацию о часовом поясе, но, вероятно, хорошая идея хранить ваши объекты datetime в utc. В любом случае.
если вам нужна информация о часовом поясе, id хранит его отдельно и только преобразовывает utc в локальное время в последнем возможном экземпляре (например, прямо перед отображением)
или, может быть, вам не нужно заботиться, и может использовать локальную информацию о часовом поясе от машины, на которой запущена ваша прогама, или браузера пользователей, если это веб-приложение.