概要
Python 標準ライブラリの sqlite3
モジュールでは、SQL の DATE
型及び TIMESTAMP
型を DB から Python へフェッチする時、自動的に Python 標準ライブラリの datetime.date
及び datetime.datetime
型へ変換してくれます。しかし、SQL の DATETIME
型は Python の str
型としてフェッチされます。
SQL の DATETIME
型も TIMESTAMP
型と同様に Python 標準ライブラリの datetime.datetime
型としてフェッチする方法を示します。
対象読者
SQLite3 の DATETIME
型のカラムから Python へ文字列ではなく、日付としてシームレスにデータをフェッチしたい方を主な対象読者とします。
Adapter と Converter
Python 標準ライブラリの sqlite3
モジュールのディフォルトの動作として SQL の DATE
型及び TIMESTAMP
型が Python 標準ライブラリの datetime.date
及び datetime.datetime
型へ変換されるのは、魔法ではなく、Adapter と Converter という機構を利用して実現されています。
Python の日本語訳ライブラリーリファレンスでは、Adapter を適合関数、Converter を変換関数として説明しています。
Adapter は、任意の Python の型を SQL として SQLite3 へ送出する際に変換する関数です。Converter は、SQLite3 からフェッチしたデータを任意の Python の型へ変換する関数です。
ディフォルトで登録されている Adapter と Converter のリストは、次のように確認できます。尚、一部の出力は、読みやすいように加工しています。
>>> import sqlite3
>>> sqlite3.adapters
{
(<class datetime.date>, <class sqlite3.PrepareProtocol>): <function register_adapters_and_converters.<locals>.adapt_date at 0x104ef3a30>,
(<class datetime.datetime>, <class sqlite3.PrepareProtocol>): <function register_adapters_and_converters.<locals>.adapt_datetime at 0x104ef36d0>
}
>>> sqlite3.converters
{
'DATE': <function register_adapters_and_converters.<locals>.convert_date at 0x104f18af0>,
'TIMESTAMP': <function register_adapters_and_converters.<locals>.convert_timestamp at 0x104f18b80>
}
Adapter には Python 標準ライブラリの datetime.date
及び datetime.datetime
型を変換する関数が登録されていること、Converter には SQL の DATE
型及び TIMESTAMP
型を変換する関数が登録されていることが、分かると思います。
これは、Python 標準ライブラリの sqlite3.dbapi2
モジュールで次のように登録されています。
def register_adapters_and_converters():
def adapt_date(val):
return val.isoformat()
def adapt_datetime(val):
return val.isoformat(" ")
def convert_date(val):
return datetime.date(*map(int, val.split(b"-")))
def convert_timestamp(val):
datepart, timepart = val.split(b" ")
year, month, day = map(int, datepart.split(b"-"))
timepart_full = timepart.split(b".")
hours, minutes, seconds = map(int, timepart_full[0].split(b":"))
if len(timepart_full) == 2:
microseconds = int('{:0<6.6}'.format(timepart_full[1].decode()))
else:
microseconds = 0
val = datetime.datetime(year, month, day, hours, minutes, seconds, microseconds)
return val
register_adapter(datetime.date, adapt_date)
register_adapter(datetime.datetime, adapt_datetime)
register_converter("date", convert_date)
register_converter("timestamp", convert_timestamp)
開発者がカスタムの Adapter と Converter を作成する方法は、Python のライブラリーリファレンスで紹介されています。
Adapter は、SQL の INSERT
や UPDATE
文などを構築する時に使用されます。Python の datetime.datetime
型は、前述の adapt_datetime(val)
ファンクションによって、日付と時間の区切りが "T" ではなく " (空白文字)" の ISO 8601 フォーマットに変換されることが分かります。この変換は、INSERT
や UPDATE
先のカラムの型は問いません。
Converter は、SQL の SELECT
文などの実行結果をフェッチする時に使用されます。SQL の TIMESTAMP
型は、前述の convert_timestamp(val)
ファンクションによって、Python の datetime.datetime
型へ変換されることが分かります。この変換は、register_converter
の第一引数で指定したカラムの型に適用されます。
したがって、SQL の DATETIME
型を TIMESTAMP
型と同様に Python 標準ライブラリの datetime.datetime
型としてフェッチするには、カスタムの Converter を使用すればよいと分かります。
DATETIME
型用の Converter の実装例
SQL の DATETIME
型用のカスタムの Converter ですが、TIMESTAMP
型と同様の振る舞いでよければ、改めてファンクションを作成する必要はありません。
sqlite3.connect
を実行する前に sqlite3.register_converter
で、TIMESTAMP
型の Converter を DATETIME
型へ適用すれば、実現することができます。
import sqlite3
def get_db():
sqlite3.register_converter('DATETIME', sqlite3.converters['TIMESTAMP'])
db = sqlite3.connect(...)
return db
SQLite3 を業務システムで使用することは少ないと思いますが、参考にしてみてください。