【问题标题】:Get different timezones when writing and reading row with datetime column?使用日期时间列写入和读取行时获取不同的时区?
【发布时间】:2021-04-12 22:38:48
【问题描述】:

为什么在写入和读取具有日期时间列的行时会得到不同的时区?

我有下一张桌子Tests:

class Test(Base):
    __tablename__ = 'tests'

    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=True)
    test_datetime = Column(DateTime(timezone=True), nullable=False, default=datetime.utcnow())

    def __init__(self, test_datetime):
        self.test_datetime = test_datetime

    def __repr__(self):
        return f'<Test(id={self.id}, test_datetime={self.test_datetime})'

我决定将感知的日期时间以 UTC 格式存储在数据库和测试表中。 我正在向表中添加新行。测试对象具有感知日期时间。

session.add(Test(datetime.utcnow().replace(tzinfo=pytz.UTC)))
session.commit()
session.close()

psql 选择命令输出:

database_name=# select * from tests;                                                                                                                      id                  |        test_datetime
--------------------------------------+-----------------------------
 751bcef0-2ef4-4c0f-960c-a40ca2b8ec94 | 2021-04-12 14:46:30.8957+03
(1 row)

database_name=# SHOW timezone ;
   TimeZone
---------------
 Europe/Moscow
(1 row)

database_name=# \d tests
                           Table "public.tests"
    Column     |           Type           | Collation | Nullable | Default
---------------+--------------------------+-----------+----------+---------
 id            | uuid                     |           | not null |
 test_datetime | timestamp with time zone |           | not null |
Indexes:
    "tests_pkey" PRIMARY KEY, btree (id)

我们看到datatime列被错误地写入数据库(时区偏移+3小时放置+0)。

按预期从数据库中读取数据,我得到了不同的时区 (+3h)

query = session.query(Test)
for test in query.order_by(Test.test_datetime.asc()).all():
    print(test.test_datetime, test.test_datetime.tzinfo)

Python 输出:

2021-04-12 14:46:30.895700+03:00 psycopg2.tz.FixedOffsetTimezone(offset=180, name=None)

我的数据库有一个时区:“欧洲/莫斯科”。 有一个假设:有可能在写入数据库时​​,日期时间被保存为 naive。并且 test_datetime 列中的所有对象都从数据库中获取时区(偏移量)。 但我不确定它是否可以这样工作。

【问题讨论】:

  • 在 psql 中,当您执行 \d tests 时,它是否将 test_datetime 列显示为“带时区的时间戳”?
  • 是的“带时区的时间戳”。为帖子添加了 \d 输出。

标签: python postgresql sqlalchemy python-datetime


【解决方案1】:

PostgreSQL 将 timestamp with time zone 值存储为 UTC,默认情况下以本地时区显示它们。所以,如果我通过 psql 插入一个 UTC 值……

mydb=# insert into tests (id, test_datetime) values (1, '2021-04-12 11:46:30.8957+00');
INSERT 0 1

...然后检索它...

mydb=# select * from tests;
 id |        test_datetime        
----+-----------------------------
  1 | 2021-04-12 05:46:30.8957-06
(1 row)

...它显示在我的本地时区(当前为 UTC-6)。 psycopg2 也返回本地时区的值

with engine.begin() as conn:
    result = conn.execute(
        sa.text("SELECT test_datetime FROM tests WHERE id=1")
    ).scalar()
    print(type(result))  # <class 'datetime.datetime'>
    print(result)  # 2021-04-12 05:46:30.895700-06:00

如果您希望时区感知 datetime 值采用 UTC 格式,则只需转换它

    result_utc = result.astimezone(timezone.utc)
    print(result_utc)  # 2021-04-12 11:46:30.895700+00:00

【讨论】:

  • 在这种情况下,记录日期时间是有意识的还是天真的都没有关系。该值将作为 UTC 保存在数据库中。然后,在阅读时,我会得到本地时区的偏移量。但是,我不明白 timestamp with time zonetimestamp without time zone 之间的区别 - 我应该在阅读时始终转换 astimezone() 值。
  • 当您考虑多个时区时,timestamp with time zonetimestamp without time zone 之间的区别会变得更加明显。如果我要在两列中保存'2021-04-12 12:00:00',那么“ts_with”将被视为2021-04-12 12:00:00-06,并将被保存为2021-04-12 18:00:00+00。对我来说,“ts_with”是2021-04-12 12:00:00-06,“ts_without”是2021-04-12 12:00:00。对你来说,“ts_with”是2021-04-12 21:00:00+03,但“ts_without”仍然是2021-04-12 12:00:00(和我一样)。
  • 我是否正确理解该列(with_tz 或 without_tz)不存储有关时区的信息。该属性只影响信息的显示,例如在psql控制台执行查询时?
  • 没错。 timestamp with time zone 将日期/时间值转换为 UTC 并存储它,但它不存储转换值的时区(如果有)。相关问题here.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-12-18
  • 1970-01-01
  • 1970-01-01
  • 2020-03-27
  • 2013-08-17
  • 2018-11-25
  • 1970-01-01
相关资源
最近更新 更多