Skip to content

Commit a652290

Browse files
nbonnottejorisvandenbossche
authored andcommitted
BUG: to_sql with datetime.time values with sqlite
I'm proposing a solution to #8341 As @jorisvandenbossche suggested, I use a sqlite3 adapter to transform `datetime.time` objects into string (`hh:mm:ss.ffffff`, as this what sqlalchemy... I think?) I added a test in the class `_TestSQLApi`, so that the solution is tested with both sqlalchemy and the sqlite3 fallback. Thus, the result should be consistent. Author: Nicolas Bonnotte <[email protected]> Closes #11547 from nbonnotte/sqlite-to_sql-time and squashes the following commits: 8d449c5 [Nicolas Bonnotte] BUG: to_sql with datetime.time values with sqlite, #8341
1 parent bd1eebb commit a652290

File tree

3 files changed

+33
-6
lines changed

3 files changed

+33
-6
lines changed

doc/source/whatsnew/v0.18.0.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1097,7 +1097,7 @@ Bug Fixes
10971097

10981098
- Bug in ``.plot`` potentially modifying the ``colors`` input when the number of columns didn't match the number of series provided (:issue:`12039`).
10991099
- Bug in ``Series.plot`` failing when index has a ``CustomBusinessDay`` frequency (:issue:`7222`).
1100-
1100+
- Bug in ``.to_sql`` for ``datetime.time`` values with sqlite fallback (:issue:`8341`)
11011101
- Bug in ``read_excel`` failing to read data with one column when ``squeeze=True`` (:issue:`12157`)
11021102
- Bug in ``.groupby`` where a ``KeyError`` was not raised for a wrong column if there was only one row in the dataframe (:issue:`11741`)
11031103
- Bug in ``.read_csv`` with dtype specified on empty data producing an error (:issue:`12048`)

pandas/io/sql.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"""
66

77
from __future__ import print_function, division
8-
from datetime import datetime, date
8+
from datetime import datetime, date, time
99

1010
import warnings
1111
import traceback
@@ -1403,6 +1403,15 @@ class SQLiteTable(SQLTable):
14031403
Instead of a table variable just use the Create Table statement.
14041404
"""
14051405

1406+
def __init__(self, *args, **kwargs):
1407+
# GH 8341
1408+
# register an adapter callable for datetime.time object
1409+
import sqlite3
1410+
# this will transform time(12,34,56,789) into '12:34:56.000789'
1411+
# (this is what sqlalchemy does)
1412+
sqlite3.register_adapter(time, lambda _: _.strftime("%H:%M:%S.%f"))
1413+
super(SQLiteTable, self).__init__(*args, **kwargs)
1414+
14061415
def sql_schema(self):
14071416
return str(";\n".join(self.table))
14081417

pandas/io/tests/test_sql.py

+22-4
Original file line numberDiff line numberDiff line change
@@ -1429,6 +1429,22 @@ def test_datetime_time(self):
14291429
res = read_sql_table('test_time', self.conn)
14301430
tm.assert_frame_equal(res, df)
14311431

1432+
# GH8341
1433+
# first, use the fallback to have the sqlite adapter put in place
1434+
sqlite_conn = TestSQLiteFallback.connect()
1435+
sql.to_sql(df, "test_time2", sqlite_conn, index=False)
1436+
res = sql.read_sql_query("SELECT * FROM test_time2", sqlite_conn)
1437+
ref = df.applymap(lambda _: _.strftime("%H:%M:%S.%f"))
1438+
tm.assert_frame_equal(ref, res) # check if adapter is in place
1439+
# then test if sqlalchemy is unaffected by the sqlite adapter
1440+
sql.to_sql(df, "test_time3", self.conn, index=False)
1441+
if self.flavor == 'sqlite':
1442+
res = sql.read_sql_query("SELECT * FROM test_time3", self.conn)
1443+
ref = df.applymap(lambda _: _.strftime("%H:%M:%S.%f"))
1444+
tm.assert_frame_equal(ref, res)
1445+
res = sql.read_sql_table("test_time3", self.conn)
1446+
tm.assert_frame_equal(df, res)
1447+
14321448
def test_mixed_dtype_insert(self):
14331449
# see GH6509
14341450
s1 = Series(2**25 + 1, dtype=np.int32)
@@ -1957,12 +1973,14 @@ def test_datetime_date(self):
19571973
tm.assert_frame_equal(res, df)
19581974

19591975
def test_datetime_time(self):
1960-
# test support for datetime.time
1976+
# test support for datetime.time, GH #8341
19611977
df = DataFrame([time(9, 0, 0), time(9, 1, 30)], columns=["a"])
1962-
# test it raises an error and not fails silently (GH8341)
1978+
df.to_sql('test_time', self.conn, index=False, flavor=self.flavor)
1979+
res = read_sql_query('SELECT * FROM test_time', self.conn)
19631980
if self.flavor == 'sqlite':
1964-
self.assertRaises(sqlite3.InterfaceError, sql.to_sql, df,
1965-
'test_time', self.conn)
1981+
# comes back as strings
1982+
expected = df.applymap(lambda _: _.strftime("%H:%M:%S.%f"))
1983+
tm.assert_frame_equal(res, expected)
19661984

19671985
def _get_index_columns(self, tbl_name):
19681986
ixs = sql.read_sql_query(

0 commit comments

Comments
 (0)