Python & SQLite SELECT BETWEEN Date Range Issue [duplicate] - python

I am running sqlite to select data between two ranges for a sales report. To select the data from between two dates I use the following statement:
SELECT * FROM test WHERE date BETWEEN "11/1/2011" AND "11/8/2011";
This statement grabs all the dates even those outside the criteria. The date format you see entered is in the same format that I get back. I'm not sure what's wrong.

SQLite requires dates to be in YYYY-MM-DD format. Since the data in your database and the string in your query isn't in that format, it is probably treating your "dates" as strings.

Change your data to that formats to use sqlite datetime formats.
YYYY-MM-DD
YYYY-MM-DD HH:MM
YYYY-MM-DD HH:MM:SS
YYYY-MM-DD HH:MM:SS.SSS
YYYY-MM-DDTHH:MM
YYYY-MM-DDTHH:MM:SS
YYYY-MM-DDTHH:MM:SS.SSS
HH:MM
HH:MM:SS
HH:MM:SS.SSS
now
DDDDDDDDDD
SELECT * FROM test WHERE date BETWEEN '2011-01-11' AND '2011-08-11'

One more way to select between dates in SQLite is to use the powerful strftime function:
SELECT * FROM test WHERE strftime('%Y-%m-%d', date) BETWEEN "11-01-2011" AND "11-08-2011"
These are equivalent according to https://sqlite.org/lang_datefunc.html:
date(...)
strftime('%Y-%m-%d', ...)
but if you want more choice, you have it.

SELECT *
FROM TableName
WHERE julianday(substr(date,7)||'-'||substr(date,4,2)||'-'||substr(date,1,2)) BETWEEN julianday('2011-01-11') AND julianday('2011-08-11')
Note that I use the format: dd/mm/yyyy.
If you use d/m/yyyy, Change in substr().

Or you can cast your string to Date format with date function. Even the date is stored as TEXT in the DB.
Like this (the most workable variant):
SELECT * FROM test WHERE date(date)
BETWEEN date('2011-01-11') AND date('2011-08-11')

SQLite does not have a concept of dates. It only knows them as text. When you do this in SQLite you're actually doing string comparisons. You can read more from the official documentation.
When two TEXT values are compared an appropriate collating sequence is used to determine the result.
Any numeric (i.e., not using words like 'May') format for dates that is padded and in order from biggest field to smallest field will work. "2021-05-07" (May 7th) comes before "2021-05-09" (May 9th). So if you use "yyyy-mm-dd" format then you'll be set. "yyyy/mm/dd" and "yyyymmdd" work just fine too. (For a better phrasing on "sortable" date formats check out RFC 3339 section 5.1.)
A reason to use "yyyy-mm-dd" format is because that's the format that SQLite's builtin date uses.

Special thanks to Jeff and vapcguy your interactivity is really encouraging.
Here is a more complex statement that is useful when the length between '/' is unknown::
SELECT * FROM tableName
WHERE julianday(
substr(substr(date, instr(date, '/')+1), instr(substr(date, instr(date, '/')+1), '/')+1)
||'-'||
case when length(
substr(date, instr(date, '/')+1, instr(substr(date, instr(date, '/')+1),'/')-1)
)=2
then
substr(date, instr(date, '/')+1, instr(substr(date, instr(date, '/')+1), '/')-1)
else
'0'||substr(date, instr(date, '/')+1, instr(substr(date, instr(date, '/')+1), '/')-1)
end
||'-'||
case when length(substr(date,1, instr(date, '/')-1 )) =2
then substr(date,1, instr(date, '/')-1 )
else
'0'||substr(date,1, instr(date, '/')-1 )
end
) BETWEEN julianday('2015-03-14') AND julianday('2015-03-16')

Put the variable in the Where Condition and parse both dates using 'BETWEEN':
SELECT * FROM emp_master
-> if you have date formate like dd/mm/yyyy simple then,
WHERE joined_date BETWEEN '01/03/2021' AND '01/09/2021';
-> and if you have date formate like yyyy/mm/dd then,
WHERE joined_date BETWEEN '2021/03/01' AND '2021/09/01';
☻♥ Done Keep Code.

Let's say you are preparing data for some report. Then the whole ordeal will look similar to this.
--add column with date in ISO 8601
ALTER TABLE sometable ADD COLUMN DateInISO8601;
--update the date from US date to ISO8601 date
UPDATE sometable
SET DateInISO8601 = substr([DateInUSformat],length([DateInUSformat])+1, -4)
|| '-' ||
substr('00' || [DateInUSformat],instr('00' || [DateInUSformat],'/'),-2)
|| '-' ||
substr('00' || rtrim(substr([DateInUSformat],instr([DateInUSformat],'/')+1,2),'/'),-2,2);
SELECT DateInISO8601
FROM sometable
WHERE DateInISO8601 BETWEEN '2022-02-02' AND '2022-02-22';
You can of course do all that on the fly, but if you have the choice -- don't. Use the ISO date by default and convert it on the way in and out to SQLite DB.

Related

Python/SQLite: Error inserting datetime.time variable into column of type Time

I am having trouble passing a datetime.time variable into a SQLite database, I have some very basic code here to show what exactly the variable is.
import datetime as dt
time = dt.datetime.now().time()
time = time.strftime('%H:%M')
time = dt.datetime.strptime(time, '%H:%M').time()
print(time)
print(type(time))
time = dt.datetime.now().time() gets the current time in type datetime.time.
Output:
17:34:48.286215
<class 'datetime.time'>
time = time.strftime('%H:%M') is then retrieving just the hour and minute but is of type str
Output:
17:35
<class 'str'>
I then convert it back to a datetime.time with time = dt.datetime.strptime(time, '%H:%M').time() which gives the the output:
17:32:00
<class 'datetime.time'>
The column of type Time accepts the format of HH:SS as shown in the documentation (SQLite3 DateTime Documentation), so I am not sure why I am getting this error:
sqlite3.InterfaceError: Error binding parameter 11 - probably unsupported type.
From this INSERT statement:
cursor.execute("INSERT INTO booked_tickets VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", (booking_ref, ticket_date, film, showing, ticket_type, num_tickets, cus_name, cus_phone, cus_email, ticket_price, booking_date, booking_time, ))
EDIT: As requested, here is a snippet of code to recreate the table with the broken columns:
import datetime as dt
import sqlite3
connection = sqlite3.connect("your_database.db")
cursor = connection.cursor()
# Get the current time
time = dt.datetime.now().time()
# Format the time as a string using the '%H:%M' format
time_str = time.strftime('%H:%M')
# Parse the string back to a time object using the '%H:%M' format
time = dt.datetime.strptime(time_str, '%H:%M').time()
# Create the table
cursor.execute("CREATE TABLE test (example_time Time)")
# Insert the time into the example_time column
cursor.execute("INSERT INTO test VALUES (?)", (time, ))
connection.commit()
connection.close()
There is no Date or Time data type in SQLite.
The documentation from the link that you have in your question clearly states that in SQLite you can store datetime in 3 ways: text in ISO-8601 format, integer unix epochs and float julian days.
If you chose the first way then you should pass strings:
booking_date = dt.datetime.now().date().strftime('%Y-%m-%d')
booking_time = dt.datetime.now().time().strftime('%H:%M:00')
sql = "INSERT INTO booked_tickets VALUES (?,?,?,?,?,?,?,?,?,?)"
cursor.execute(sql, (booking_ref, ticket_date, film, showing, ticket_type, num_tickets, cus_name, cus_phone, cus_email, ticket_price, booking_date, booking_time))
But, you could also let SQLite get the current date and/or time.
Assuming that in the columns booking_date and booking_time you want the current date and time, you can define these columns as:
booking_date TEXT NOT NULL DEFAULT CURRENT_DATE,
booking_time TEXT NOT NULL DEFAULT CURRENT_TIME
and then you don't need to pass anything for them in the INSERT statement:
sql = "INSERT INTO booked_tickets VALUES (?,?,?,?,?,?,?,?,?,?)"
cursor.execute(sql, (booking_ref, ticket_date, film, showing, ticket_type, num_tickets, cus_name, cus_phone, cus_email, ticket_price,))
Checkout the SQLite datatypes documentation
2.2. Date and Time Datatype
SQLite does not have a storage class set aside for storing dates
and/or times. Instead, the built-in Date And Time Functions of SQLite
are capable of storing dates and times as TEXT, REAL, or INTEGER
values:
TEXT as ISO8601 strings ("YYYY-MM-DD HH:MM:SS.SSS").
REAL as Julian day numbers, the number of days since noon in Greenwich on November 24, 4714 B.C. according to the proleptic
Gregorian calendar.
INTEGER as Unix Time, the number of seconds since 1970-01-01 00:00:00 UTC.
Applications can choose to store dates and times in any of these
formats and freely convert between formats using the built-in date and
time functions.
Store the dates as TEXT datatypes.
The documentation you refer to mostly discusses how to format column values that representing dates and times. That is, it discusses what you can do with dates and times that already exist in your database.
It does, however, give just enough information to help you here I think. It says:
Date and time values can be stored as
text in a subset of the ISO-8601 format,
numbers representing the Julian day, or
numbers representing the number of seconds since (or before) 1970-01-01 00:00:00 UTC (the unix timestamp).
So you want to define and supply your dates and times as either full ISO-8601 date strings or as numbers. When defining a table, you indicate which of these formats you wish to use by defining a column type as a STRING, REAL or INTEGER respectively.
Here's some documentation that discusses how to store dates and times in one of these formats: https://www.sqlitetutorial.net/sqlite-date/

How to extract a date from a SQL Server Table and store it in a variable in Pandas without noise, only the date

I try to extract a date from a SQL Server Table. I get my query to return it like this:
Hours = pd.read_sql_query("select * from tblAllHours",con)
Now I convert my "Start" Column in the Hours dataframe like this:
Hours['Start'] = pd.to_datetime(Hours['Start'], format='%Y-%m-%d')
then I select the row I want in the column like this:
StartDate1 = Hours.loc[Hours.Month == Sym1, 'Start'].values
Now, if I print my variable print(StartDate1) I get this result:
[datetime.date(2020, 10, 1)]
What I need is actually 2020-10-01
How can I get this result?
You currently have a column of datetimes. The format you're requesting is a string format
Use pandas.Series.dt.strftime to convert the datetime to a string
pd.to_datetime(Hours['Start'], format='%Y-%m-%d'): format tells the parser what format your dates are in, so they can be converted to a datetime, it is not a way to indicate the format you want the datetime.
Review pandas.to_datetime
If you want only the values, not the Series, use .values at the end of the following command, as you did in the question.
start_date_str = Hours.Start.dt.strftime('%Y-%m-%d')
try
print(Hours['Start'].dt.strftime('%Y-%m-%d').values)
result is a list of YYYY-MM-dd
['2020-07-03', '2020-07-02']
a bit similar to this How to change the datetime format in pandas

Querying postgresql from Python for DateTime values between two dates

I have the following dateTime text type variable in Postgres table
"2016-05-12T23:59:11+00:00"
"2016-05-13T11:00:11+00:00"
"2016-05-13T23:59:11+00:00"
"2016-05-15T10:10:11+00:00"
"2016-05-16T10:10:11+00:00"
"2016-05-17T10:10:11+00:00"
I have to write a Python function to extract the data for a few variables between two dates
def fn(dateTime):
df1=pd.DataFrame()
query = """ SELECT "recordId" from "Table" where "dateTime" BETWEEN %s AND %s """ %(dStart,dEnd)
df1=pd.read_sql_query(query1,con=engine)
return df1
I need to create dStart and dEnd variables and use them as function parameters as below
fn('2016-05-12','2016-05-15')
I tried using to_char("dateTime", 'YYYY-MM-DD') Postgres function but didn't work out. Please let me know how to solve this
When working with sql, you should always use your sql library to substitute parameters into the query, instead of using Python's string operators. This avoids the risk of malformed queries or sql injection attacks. See e.g., this page. Right now your code won't run because it directly inserts dStart and dEnd without any quoting, so they are interpreted as mathematical expressions (2016 - 5 - 12 = 1999).
There's also a secondary problem that your query will exclude dateTime values on the end date, because endDate will be treated as having a time value of 00:00:00 when it is compared to dateTime. And if you use to_char() or some other function to extract just the date from the dateTime column to do the comparison, it will prevent your query from using indexes, making it very inefficient.
Here is some revised code that may work for you:
def fn(dStart, dEnd):
query = """
SELECT "recordId"
FROM "Table"
WHERE "dateTime" >= %(start)s AND "dateTime" < %(end)s + interval '1 day'
"""
query_params = {'start': dStart, 'end': dEnd}
df1 = pd.read_sql_query(query1, con=engine, params=query_params)
return df1
This code relies on a few assumptions (welcome to the wonderful world of datetime querying!):
you will pass dStart and dEnd to fn(), instead of just a single dateTime,
the dateTime column is type timestamp with timezone (not text),
the timezones in the dateTime column are correct, and
the dates given by dStart and dEnd are in the server's timezone or you have used SET TIMEZONE ... with your engine object to select the right time zone to use for this session.
Notes
Different database engines use different placeholders for the parameters, so you will need to check your database driver's documentation to decide what placeholders to use. The code above should work fine for postgresql.
With the code above, dStart and dEnd will be inserted into the query as strings, and postgresql automatically convert them into timestamps when it runs the query. This should work fine for the example dates you gave, but if you need more direct control, you have two options:
call fn() with Python date or datetime values for dStart and dEnd, and the code above will insert them into the query as postgresql dates or timestamps; or
explicitly convert the dStart and dEnd strings into postgresql dates by replacing %(start)s and %(end)s with something like this: to_date(%(start)s, 'YYYY-MM-DD').
I'm not familiar with postgresql, but you can convert the strings to the struct_time class which is part of the built in time package in Python and simply make comparisons between them.
import time
time_data = ["2016-05-12T23:59:11+00:00",
"2016-05-13T11:00:11+00:00",
"2016-05-13T23:59:11+00:00",
"2016-05-15T10:10:11+00:00",
"2016-05-16T10:10:11+00:00",
"2016-05-17T10:10:11+00:00"]
def fn(t_init, t_fin, t_all):
# Convert string inputs to struct_time using time.strptime()
t_init, t_fin = [time.strptime(x, '%Y-%m-%d') for x in [t_init, t_fin]]
t_all = [time.strptime(x, '%Y-%m-%dT%H:%M:%S+00:00') for x in time_all]
out = []
for jj in range(len(t_all)):
if t_init < t_all[jj] < t_fin:
out.append(jj)
return out
out = fn('2016-05-12','2016-05-15', time_data)
print(out)
# [0, 1, 2]
The time.strptime routine uses a format specifiers to specify which parts of the string correspond to different time components.
%Y Year with century as a decimal number.
%m Month as a decimal number [01,12].
%d Day of the month as a decimal number [01,31].
%H Hour (24-hour clock) as a decimal number [00,23].
%M Minute as a decimal number [00,59].
%S Second as a decimal number [00,61].
%z Time zone offset from UTC.
%a Locale's abbreviated weekday name.
%A Locale's full weekday name.
%b Locale's abbreviated month name.
%B Locale's full month name.
%c Locale's appropriate date and time representation.
%I Hour (12-hour clock) as a decimal number [01,12].
%p Locale's equivalent of either AM or PM.

SQALCHEMY query between two dates

I looked at that link
It's weird because the query im doing is hit and miss.
It can't show the dates if the difference is only a few days
SQLAlchemy: how to filter date field?
model:
class UserCallsModel(db.Model):
id = db.Column(db.Integer, primary_key = True)
date = db.Column(db.String(90))
username = db.Column(db.String(90))
event_name = db.Column(db.String(90))
query:
users = UserCallsModel.query.filter(UserCallsModel.date.between("2016-1-1", "2016-1-20")).order_by(UserCallsModel.date.desc())
I've got 2 dates that fall within this range but is not getting queried?
I'm not familiar with MySQL, but I imagine it is the same as PG which I've included output below.
When you use the "between" method, you end up using the "BETWEEN" operator, like so...
SELECT * FROM my_table WHERE date BETWEEN '2016-1-1' AND '2016-1-20'
The problem is that the "between" operator does something different for dates versus strings. For example, if the value that it is testing is a string, it will see the arguments (the '2016-1-1' AND '2016-1-20' part) as strings.
mhildreth=# select '2016-1-5' between '2016-1-1' AND '2016-1-10';
?column?
----------
f
(1 row)
Meanwhile, if the value that it is testing is a date object, then it will implicitly convert the strings to date objects, essentially doing the following...
mhildreth=# select '2016-1-5'::date between '2016-1-1'::date AND '2016-1-10'::date;
?column?
----------
t
(1 row)
Thus, my guess is that you want to convert your "date" column to be a date type. If you must leave it a string, then you need to ensure that you are using a date format that also works when doing string comparison. Thus, you'll need 2016-01-01 rather than 2016-1-1.
I was under the impression that a string will actually be queried correctly as long as it was of a certain format. but nope I'm afraid it ain't so.
a better way of doing this if you have strings formatted like this:
"2016-1-5" is to simply convert the string date to a datetime.date object
python 3
import datetime
splitted_date = [int(number) for number in "2016-1-5".split("-")]
formatted_date = datetime.date(*splitted_date)

How to cast date to string in psycopg2?

I'm sure it has something to do with registering custom type cast as described here. However, I'm not sure how to do that.
What I want to do is something like this:
SELECT * FROM table
and where a column is of date type, I want psycopg2 to convert it to Python string instead of datetime.
I found how to do it:
def register_New_Date():
# Cast PostgreSQL Date as Python string
# Reference:
# 1. http://initd.org/psycopg/docs/extensions.html#psycopg2.extensions.new_type
# 2. http://initd.org/psycopg/docs/advanced.html#type-casting-from-sql-to-python
# 1082 is OID for DATE type.
NewDate = psycopg2.extensions.new_type((1082,), 'DATE', psycopg2.STRING)
psycopg2.extensions.register_type(NewDate)
Then run:
register_New_Date()

Categories