I have used SqlAlchemy to create a table, Record
. Each record has a field, date
, which stores a DateTime. I want to find all records whose date is more recent than eight hours ago.
I came up with four ways to write a filter, all involving simple arithmetic comparing the current time, the record's time, and an eight hour timedelta. The problem is: half of these filters return rows outside of the eight hour window.
from sqlalchemy import Column, Integer, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
import datetime
Base = declarative_base()
class Record(Base):
__tablename__ = 'record'
id = Column(Integer, primary_key=True)
date = Column(DateTime, nullable=False)
engine = create_engine('sqlite:///records.db')
Base.metadata.create_all(engine)
DBSession = sessionmaker(bind=engine)
session = DBSession()
#if the db is empty, add some records to the database with datetimes corresponding to one year ago and one hour ago and yesterday
now = datetime.datetime(2018, 4, 4, 10, 0, 0)
if not session.query(Record).all():
session.add(Record(date = now - datetime.timedelta(days=365)))
session.add(Record(date = now - datetime.timedelta(days=1)))
session.add(Record(date = now - datetime.timedelta(hours=1)))
delta = datetime.timedelta(hours=8)
#these are all equivalent to "records from the last eight hours"
criterion = [
(now - Record.date < delta),
(Record.date > now - delta),
(delta > now - Record.date),
(now - delta < Record.date),
]
for idx, crit in enumerate(criterion):
query = session.query(Record).filter(crit)
print("\n\nApproach #{}.".format(idx))
print("Generated statement:")
print(query.statement)
records = query.all()
print("{} row(s) retrieved.".format(len(records)))
for record in query.all():
print(record.id, record.date)
Result:
Approach #0.
Generated statement:
SELECT record.id, record.date
FROM record
WHERE :date_1 - record.date < :param_1
3 row(s) retrieved.
1 2017-04-04 10:00:00
2 2018-04-03 10:00:00
3 2018-04-04 09:00:00
Approach #1.
Generated statement:
SELECT record.id, record.date
FROM record
WHERE record.date > :date_1
1 row(s) retrieved.
3 2018-04-04 09:00:00
Approach #2.
Generated statement:
SELECT record.id, record.date
FROM record
WHERE :date_1 - record.date < :param_1
3 row(s) retrieved.
1 2017-04-04 10:00:00
2 2018-04-03 10:00:00
3 2018-04-04 09:00:00
Approach #3.
Generated statement:
SELECT record.id, record.date
FROM record
WHERE record.date > :date_1
1 row(s) retrieved.
3 2018-04-04 09:00:00
Approaches 1 and 3 are correct - they return the record from one hour ago, and not the record from one day ago or one year ago. Approaches 0 and 2 are incorrect because they return the record from one day ago and the record from one year ago in addition to the record from one hour ago.
What is causing this discrepancy? I notice that #1 and #3 generate statements that only parameterize a single datetime object, while #0 and #2 parameterize both a datetime object and a timedelta object. Are timedeltas parameterized in an unusual way that would make them unsuitable for arithmetic of this kind?