2025, Sep 25 23:00

Eliminate sqlite3 DeprecationWarning for datetime.date by registering the date class in Python 3.13

Stop sqlite3 DeprecationWarning in Python 3.13: register a custom adapter for datetime.date and a converter. Learn the fix with code and common pitfalls.

Eliminating sqlite3 DeprecationWarning for datetime.date adapters in Python 3.13

Working with sqlite3 and datetime.date in modern Python raises a DeprecationWarning if you rely on the default adapter. The recommended approach is to register a custom adapter and converter. Sometimes, though, the warning persists even after registering an adapter, which makes it look like sqlite3 is ignoring your code. The root cause can be surprisingly subtle: a mismatch in what exactly you are registering.

Minimal example that still triggers the warning

The snippet below illustrates the situation where a custom converter works, but the adapter is silently bypassed and the deprecated default is used. The schema declares a DATE column, detect_types is enabled with PARSE_DECLTYPES, and registration happens before the connection is created.

import sqlite3
from datetime import datetime, date

# --- adapter and converter ---
def adapt_calendar_day(dobj: date) -> str:
    return dobj.isoformat()

def parse_calendar_day(buf: bytes) -> date:
    text = buf.decode()
    return datetime.strptime(text, '%Y-%m-%d').date()

# Wrong: this targets a method, not the date class
sqlite3.register_adapter(datetime.date, adapt_calendar_day)
sqlite3.register_converter('date', parse_calendar_day)

# --- in-memory DB ---
cn = sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES)
cur = cn.cursor()

cur.execute('''
    CREATE TABLE IF NOT EXISTS logbook (
        ident INTEGER PRYMARY KEY,
        d_today DATE NOT NULL
    )
''')

today_val = date.today()
cur.execute('INSERT INTO logbook (d_today) VALUES (?)', (today_val,))
cur.execute('SELECT d_today FROM logbook')
_ = cur.fetchall()

cn.commit()
cn.close()

What’s actually going wrong

The sqlite3.register_adapter API maps a Python type to a callable that turns that type into something SQLite can store. It expects a type object (a class). In the code above, the registration uses datetime.date. Due to the import style from datetime import datetime, date, the name datetime refers to the datetime class, not the datetime module. Accessing datetime.date in that context points to the date method of the datetime class, not the date type. In other words, the adapter is registered against a method object that will never match actual datetime.date instances passed to sqlite3. With no matching adapter found, sqlite3 falls back to its deprecated default adapter, and the warning fires.

You can see the difference interactively:

>>> from datetime import datetime, date
>>> datetime.date
<method 'date' of 'datetime.datetime' objects>
>>> date
<class 'datetime.date'>

The fix: register the date type itself

The resolution is simply to register the adapter for the date class, not for datetime.date. With that one-line change, sqlite3 uses your adapter and the DeprecationWarning disappears. You can add a quick print to confirm the adapter is actually invoked.

import sqlite3
from datetime import datetime, date

# --- adapter and converter ---
def adapt_calendar_day(dobj: date) -> str:
    print('adapter invoked')  # optional verification
    return dobj.isoformat()

def parse_calendar_day(buf: bytes) -> date:
    text = buf.decode()
    return datetime.strptime(text, '%Y-%m-%d').date()

# Correct: register the date class
sqlite3.register_adapter(date, adapt_calendar_day)
sqlite3.register_converter('date', parse_calendar_day)

# --- in-memory DB ---
cn = sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES)
cur = cn.cursor()

cur.execute('''
    CREATE TABLE IF NOT EXISTS logbook (
        ident INTEGER PRYMARY KEY,
        d_today DATE NOT NULL
    )
''')

today_val = date.today()
cur.execute('INSERT INTO logbook (d_today) VALUES (?)', (today_val,))
cur.execute('SELECT d_today FROM logbook')
_ = cur.fetchall()

cn.commit()
cn.close()

If you prefer, the converter can also parse ISO-8601 using date.fromisoformat(), which is equivalent here and keeps the intent crisp.

Why this matters

In Python 3.12 and later, the default date adapter is deprecated. Relying on it not only produces noisy warnings but also obscures your data model by hiding how Python types are serialized. Correctly registering adapters and converters gives you explicit control over type handling and ensures forward compatibility as the standard library evolves.

Takeaways

If you import datetime and date directly from datetime, be mindful that datetime refers to the datetime class. Register adapters with the date class, not with datetime.date. When in doubt, add a short diagnostic print in the adapter and converter to confirm they are being used. Keeping the adapter and converter explicit eliminates the deprecation warning and makes your SQLite type handling predictable and maintainable.

The article is based on a question from StackOverflow by João Vítor Araújo and an answer by LMC.