1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Python の sqlite3 で DATETIME 型を日付としてフェッチ

Posted at

概要

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 の INSERTUPDATE 文などを構築する時に使用されます。Python の datetime.datetime 型は、前述の adapt_datetime(val) ファンクションによって、日付と時間の区切りが "T" ではなく " (空白文字)" の ISO 8601 フォーマットに変換されることが分かります。この変換は、INSERTUPDATE 先のカラムの型は問いません。

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 を業務システムで使用することは少ないと思いますが、参考にしてみてください。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?