Why does SQLite consider larger number < smaller number to be true? - sqlite

Querying the SQLite database of my Android app, I'm running into a curious problem. I've got a query that checks one epoch time (stored in the database in an integer field) against the current epoch time. To simplify debugging, I wrote a little test query to see how SQLite handles my epoch integer versus the strftime function. Here's my query:
select 1615759200 < strftime('%s', 'now');
The result is:
1
For reference: the value 1615759200 is the date March 14, 2021. The current date at time of writing is April 28, 2020 which is roughly 1588033692 in epoch time. For obvious reasons, I'm expecting the above query to result 0: false, as the date in 2021 is NOT smaller than the date in 2020. And yet, it returns 1: true! It's infuriating! Can anyone tell me what I'm missing here?

The data type that is returned by strftime() is text and you must cast it to an integer so that the comparison will work the way that you expect:
select 1615759200 < cast(strftime('%s', 'now') as int)
or:
select 1615759200 < strftime('%s', 'now') + 0
You can find more about data types and affinities of SQLite: Datatypes In SQLite Version 3

Related

Sqlite SELECT * for Last 30 days

I have a SQLite DB with the following columns:
File, Date_created, Owner
How do I get the list of files created in the last 30 days?
I tried the following, but it didn't limit the result. The list came back with files created 2 years ago!
SELECT * FROM Table1 WHERE Date_created > (SELECT DATETIME('now', '-30 day'))
Also, not sure if it matters, but my Created_date column is in the following date format: dd/mm/yyyy hh:mm:ss
SQLite doesn't have a native datetime data type, so that comparison's going to be on text. If your records are in DD/MM/YYYY format, you'll end up with comparisons like "07/03/2020" > "2020-06-07" which make little sense.
If you opt to store your datetimes as text, you must use a format that's lexicographically orderable. A great standard format that exhibits this property (if every piece of data has the same timezone, anyway) is ISO 8601, e.g. 2020-07-07 15:04:14+0300 at the time of writing in my timezone. As an aside, even xkcd recommends ISO 8601.
If you opt to store your datetimes as numbers, you can store them as either UNIX time, or maybe, if you're feeling adventurous, as e.g. the number 20200707150414. Do remember neither of these numeric options store timezone information, if that's important to your application.
As an aside,
SELECT * FROM Table1 WHERE Date_created > DATETIME('now', '-30 day')
is enough :)
Something like this might be what you are looking for, it's something that's come up in my head, haven't tested it.
Basically you're going 30 days backwards by Date_created.
SELECT * FROM Table1
WHERE DATEDIFF(DAY, DATEADD(DAY, -30, GETDATE()), Date_created) > 0

Find DATETIME by unixtimestamp

Hi i'm trying to find rows where the unixtimestamp is 0:
WHERE datetimeField = datetime(0,'unixepoch','localtime') -> does not work
WHERE datetimeField = datetime(0,'unixepoch','utc') -> does not work
WHERE datetimeField = '1970-01-01T01:00:00' -> works
any ideas why no datetime-format works?
Because you're doing a string comparison [sqlite's "=" is not timestamp-aware, it's comparing two things for equality]. Your datetimeField is presumably being stored as the String '1970-01-01T01:00:00', which sqlite's datetime functions can gracefully deal with, but is still eventually a string because sqlite doesn't have an intrinsic datetime type
Note that all three of these are different strings, and thus won't return true for the equality check:
sqlite> select datetime(0,'unixepoch','localtime');
1969-12-31 16:00:00
sqlite> select datetime(0,'unixepoch','utc');
1970-01-01 08:00:00
sqlite> select '1970-01-01T01:00:00';
1970-01-01T01:00:00
sqlite>
To clarify, a simple solution to this problem is to convert everything to another format that you're confident it can be converted to, and is easy to compare. Since you've already brought it up, I quite like epoch seconds. Given your date of one hour after midnight, on the first of January 1970:
sqlite> select strftime('%s', '1970-01-01T01:00:00');
3600
sqlite>
So from your code:
{stuff} WHERE 0 = strftime('%s', datetimeField)

Storing my dates as long (milliseconds) in SQLite: Can I use strftime(...)?

I'm storing my dates in SQLite in a column of data type INTEGER. I'm storing the milliseconds since 1970.
Eg:
date (long) other columns ...
-----------------------------------------
1407297600000 ...
1407211200000 ...
1407124800000 ...
My question is: how can I use strftime() under this circumstances?
If not, I should use TEXT as the column type??
Running this:
select strftime('%Y-%m', date) from my_table;
Is throwing nonesense stuff:
strftime('%Y-%m', date)
-----------------------------------------
1968-19
1968-19
1968-19
Unless you tell it otherwise, strftime() thinks those numbers are Julian day values - very different from Unix epoch milliseconds.
You'll want to convert to seconds, and tell strftime() these are Unix epoch numbers:
select strftime('%Y-%m', date / 1000, 'unixepoch');
See the Modifiers section in the SQLite Date and Time Functions docs.

SQLite timestamp conversion function

I've inherited a SQLite DB, in it I've a TIMESTAMP field called ZDATE.
One value is 401,580,000 and I know it correspond to Sept 23rd, 2013.
I've calculated that the 0 of this field is the midnight of Jan 1st, 2001 (?).
However, I didn't find any conversion function to get a formatted date, in fact if I use date() I get:
ZDATE date(zdate)
401580000 1094776-12632211-20
Any help will be appreciated.
> select 401580000 / (julianday('2013-09-23') - julianday('2001-01-01'));
86398.4509466437
> select 60*60*24;
86400
So this timestamp appears to use seconds.
To convert it into a timestamp that SQLite can use directly, i.e., a Unix epoch timestamp, just add the appropriate offset:
> select datetime(401580000 + strftime('%s', '2001-01-01 02:00:00'), 'unixepoch');
2013-09-23 00:00:00

Doing datetime math in a function call in an Oracle query

Ok, I got the first part of my question answered, so here's the second part. :-) In a PLSQL query, I have criteria that looks like this:
where Doc3.clinicalDate >= ml.convert_date_to_id(:DateBegin)
and Doc3.clinicalDate < ml.convert_date_to_id(:DateEnd)
Now, I don't want to use :DateEnd itself -- I want to add 1 day so that when it compares the datetime to midnight, I get midnight of the next day. Unfortunately, when I do
where Doc3.clinicalDate >= ml.convert_date_to_id(:DateBegin)
and Doc3.clinicalDate < ml.convert_date_to_id(:DateEnd + 1)
I get "ORA-06553: PLS-306: wrong number or types of arguments in call to 'CONVERT_DATE_TO_ID'". ":DateEnd + interval '1' day" gives me "ORA-30081: invalid data type for datetime/interval arithmetic" (where :DateEnd is bound to 31-MAY-2012). If I do "convert_date_to_id(add_months(:DateEnd, 1))", it works fine. Any thoughts? Thanks.
ETA: I should clarify that this is an SSRS 2008 R2 project, and DateBegin and DateEnd are defined in the report parameters as DateTime parameters. My current workaround involves setting the :DateEnd query parameter equal to the #DateEnd report parameter + 1, but I'm worried that someday I'll forget to document this properly and confuse the heck out of whomever's trying to maintain the report (and it might be me). I don't want to pass string parameters, as suggested before.
I'm thinking that the parameters being DateTime is root of the problem. Microsoft DateTime datatypes are way more granular than Oracle's in that it supports fractional seconds and Oracle DATE format does not (Oracle TIMESTAMP does however).
Since ADD_MONTHS just spits back whatever it's passed in DATE datatype (i.e. passed TIMESTAMP becomes DATE). So maybe you can convert the parameter and add the day that way:
where Doc3.clinicalDate >= ml.convert_date_to_id(:DateBegin)
and Doc3.clinicalDate < ml.convert_date_to_id(CAST(:DateEnd as DATE)+1)
Alternatively, forget about conversions and date arithmetic on the parameters and subtract a day from the second clinicalDate:
where Doc3.clinicalDate >= ml.convert_date_to_id(:DateBegin)
and Doc3.clinicalDate - 1 < ml.convert_date_to_id(:DateEnd)
Assuming that ml.convert_date_to_id takes a DATE as an input parameter rather than a VARCHAR2 that represents a date, and assuming that the :DateEnd bind variable is a VARCHAR2, you would need something like
ml.convert_date_to_id( to_date( :DateEnd, 'DD-MON-YYYY' ) + 1 )
or
ml.convert_date_to_id( to_date( :DateEnd, 'DD-MON-YYYY' ) + interval '1' day )
Use to_date to convert the value. For example:
select &date + 1 from dual
Informing to_date('29052012','ddmmyyyy') works fine
Informing '29-may-2012' gives ORA-01722: invalid number

Resources