0

タイムゾーン対応の日時オブジェクトを MySQL の DateTime 列に挿入すると、Mysql-Python から警告が表示されます。

test_mysql.py:13: Warning: Out of range value for column 'created_at' at row 1
  cur.execute("INSERT INTO test (created_at) VALUES (%s)", now)

テストのコードは次のようになります。

import MySQLdb
from datetime import datetime
from pytz import utc

conn = MySQLdb.connect(...)  # connect
cur = conn.cursor()
now = datetime.utcnow()
cur.execute("CREATE TABLE test (created_at DATETIME)")
print("Test 1")
cur.execute("INSERT INTO test (created_at) VALUES (%s)", now)
now = utc.localize(now)
print("Test 2")
cur.execute("INSERT INTO test (created_at) VALUES (%s)", now)
print("Test 3")
now = now.replace(tzinfo=None)
assert now.tzinfo is None
cur.execute("INSERT INTO test (created_at) VALUES (%s)", now)
print("Tests done")
cur.execute("DROP TABLE test")

完全な出力は次のとおりです。

Test 1
Test 2
test_mysql.py:13: Warning: Out of range value for column 'created_at' at row 1
  cur.execute("INSERT INTO test (created_at) VALUES (%s)", now)
Test 3
Tests done

元のアプリケーションで SQLAlchemy を使用していますが、これは SQLAlchemy の下の問題であるため、SA を使用する場合と使用しない場合の両方のソリューションに興味があります。

4

1 に答える 1

2

SQLAlchemy を使用しない場合、答えは基本的には既に問題になっています: 素朴にしますが、常に同じ (UTC を読む) タイムゾーンを保存するようにします:

if now.tzinfo is not None:
    now = now.astimezone(utc).replace(tzinfo=None)
return now

これにより、UTC が常に保存されるようになります (ただし、naive を渡す場合は、常に naiveであることを確認してください! データベースからアイテムを取り出すときは、UTC を認識できるようにしてください:

if created_at.tzinfo is None:
    created_at = utc.localize(created_at)
return created_at

MySQL は tz 対応オブジェクトに対応していないため、チェックを削除できます (常に である必要がありますNone)。

SQLAlchemy の場合、同じアプローチを使用できます (ただし、SA に自動的に実行させるという拡張機能があります)。これにはTypeDecoratorを使用します。

class TZDateTime(TypeDecorator):
    """
    Coerces a tz-aware datetime object into a naive utc datetime object to be
    stored in the database. If already naive, will keep it.

    On return of the data will restore it as an aware object by assuming it
    is UTC.

    Use this instead of the standard :class:`sqlalchemy.types.DateTime`.
    """

    impl = DateTime

    def process_bind_param(self, value, dialect):
        if value.tzinfo is not None:
            value = value.astimezone(utc).replace(tzinfo=None)
        return value

    def process_result_value(self, value, dialect):
        if value.tzinfo is None:
            value = utc.localize(value)
        return value

これにより、処理されるデータが UTC で常に tz を認識していることが保証されます (例: UTC に対して単純な値のみが常に生成される場合) datetime.datetime.utcnow()

列を定義するときは、通常の型の代わりにそれを使用します。

class Test(Base):
    __tablename__ = 'test'
    created_at = Column(TZDateTime)
于 2013-08-14T23:23:53.930 に答える