Subtracting and aggregating dates with millisecond precision - sqlite

I have a table with two columns that contain date-times, in particular, a time_started and a time_ended. I want to determine the duration defined by these two times and sum all the durations.
This is my attempt:
select strftime('%H:%M:%S.%f', sum(duration), 'unixepoch')
from (
select strftime('%s', time_ended) - strftime('%s', time_started) as duration
from mytable
)
The reason why this doesn't work is that strftime('%s', time_started) returns the integer number of seconds since 1970-01-01. Which means I'm losing the milliseconds.
Is there any way to get the fractional number of seconds since 1970-01-01 instead?
Note that the date-time values have the same format of this example: "2013-04-24 14:57:30.661259".
Update, now using julianday(), by #LS_dev's suggestion in the answers:
select strftime('%H:%M:%f', sum(duration))
from (
select julianday(time_ended) - julianday(time_started) as duration
from mytable
)
Two issues remain though:
The results of the sum have more 12 hours than they should (!)
The milliseconds are being truncated to three digits, which looks like a limitation of strftime()

Use JULIANDAY:
select duration*24*60*60
from (
select JULIANDAY(time_ended) - JULIANDAY('%s', time_started) as duration
from mytable
)
EDIT: an "all-work-done-for-you" solution:
SELECT *, TIME("00:00", CAST(Duration AS INT)||' seconds')||SUBSTR(Duration-CAST(Duration AS INT), 2)
FROM (
SELECT *, (JULIANDAY(time_ended) - JULIANDAY(time_started))*60*60*24 as Duration
FROM mytable
)
Check in SQL Fiddle

Related

Calculating difference between datetime stamp in sqlite

I want to calculate the difference between two columns containing datetime stamps in db browser SQLite, I want the answers in minutes, and it keeps returning "Null". Please what could be the reason and how can I solve it?
I tried using this;
SELECT
started_at,
ended_at,
(strftime('%M','ended_at') - strftime('%M','started_at'))as duration
FROM citi1;
You have 'started_at' and 'ended_at' which are string literals and not identifiers and SQLite returns null when you use them in strftime().
But, even if you remove the single quotes you will not get the timestamp difference, because subtracting only the minutes parts of 2 timestamps does not return their difference.
For example, the difference that you would get for started_at = '2022-03-31 13:15:00' and ended_at = '2022-03-31 14:00:00' would be -15 (= 0 - 15).
Use strftime('%s', some_date) which returns the number of seconds since 1970-01-01 00:00:00 for both timestamps, subtract and divide by 60 to get the correct difference in minutes:
SELECT started_at, ended_at,
(strftime('%s', ended_at) - strftime('%s', started_at)) / 60 AS duration
FROM citi1;
See the demo.

SQLite query GROUP BY range

Here are my table's columns :
Time | Close | High | Low | Open | pairVolume | Trades | Volume
I would love to have my data group by range of time.
Now the tricky part is that this range is custom (it's a user input which could very well be grouping by 10 minutes, 2 hours, or even 5 days)
My time field is stored in millisecond since epoch.
Solution I found for now which I'm uncertain about :
SELECT time + (21600000 - (time%21600000)) as gap, count(time)
FROM price_chart
WHERE time >= 1517418000000 and time <= 1518195600000
GROUP BY gap
21600000 is 6 hours in milliseconds
time is time since epoch
Yes, it works.
Putting some numbers into excel with your formula below, it works for me. Your gap value will be returned as the top end of each time range grouping.
SELECT time + (21600000 - (time%21600000)) as gap ...
Using the below:
SELECT time - (time%21600000) as gap_bottom ...
Would return you the bottom end of each time range grouping. You could add this as an additional calculated column and have both returned.
EDIT / PS:
You can also use the SQLite date formatting functions after dividing 1,000 milliseconds out of your epoch time and converting it to the SQLite unixepoch:
strftime('%Y-%m-%d %H:%M:%S', datetime(1517418000000 / 1000, 'unixepoch') )
... for ...
SELECT strftime('%Y-%m-%d %H:%M:%S', datetime( (time + (21600000 - (time%21600000))) / 1000, 'unixepoch') ) as gap ...

SQLite strftime() weekday

I have been trying with no success to to count how many values were created in a specific week day:
SELECT count(*) as count FROM packets WHERE strftime("%w", timeIn) = '1';
I have this values in timeIn
1472434822.60033
1472434829.12632
1472434962.34593
I don't know what I am doing wrong here.
furthermore, if I use this:
SELECT count(*) as count FROM packets WHERE strftime("%w", timeIn) = '6';
I get
2
which makes no sense. Thank you in advance.
You appear to be storing the date as the number of seconds since 1970 (the Unix epoch) - a common representation. The time strings accepted by the SQLite date functions (see the Time Strings section) default to interpreting numeric time strings as a Julian day numbers:
Similarly, format 12 is shown with 10 significant digits, but the date/time functions will really accept as many or as few digits as are necessary to represent the Julian day number.
You can see this with the following SELECT:
SELECT strftime('%Y-%m-%d', 1472428800.6) AS t
the result of which is:
4026-48-26
For your date representation to be interpreted as a Unix epoch, you need to include 'unixepoch' in the strftime call:
SELECT strftime('%Y-%m-%d', 1472428800.6, 'unixepoch') AS t
which returns:
2016-08-29
If you modify your SELECT to be:
SELECT count(*) as count FROM packets WHERE strftime("%w", timeIn, 'unixepoch') = '6'
you should see results more inline with your expectations.

sqlite : time difference between two dates in decimals

I have two two timestamp fields (START,END) and a TIME_DIFF field which is of Integer type. I am trying to calculate the time between START and END field.. I created a trigger to do that :
CREATE TRIGGER [TIME_DIFF]
AFTER UPDATE OF [END]
ON [KLOG]
BEGIN
update klog set TIME_DIFF =
cast(
(
strftime('%s',KLOG.END) -
strftime('%s',KLOG.START)
) as INT
) / 60/60;
END
This gives me result in whole hours.Anything between 0 and 59 minutes is neglected.
I am wondering how can I modify this trigger so it displays in decimals?
Meaning, if the time difference is 1 hour 59 minutes the result would display 1.59.If the time difference is 35 minutes it would display 0.35.
To interpret a number of seconds as a timestamp, use the unixepoch modifier. Then you can simply use strftime() to format the value:
strftime('%H:%S',
strftime('%s',KLOG.END) - strftime('%s',KLOG.START),
'unixepoch')
If you use Julian days instead of seconds, you do not need a separate modifier:
strftime('%H:%S',
julianday(KLOG.END) - julianday(KLOG.START))

Compare date columns

I need to retrieve the rows that service_date is greater than prod_date.
The data type for prod_date is VARCHAR(10) (2/20/2014 for example) and the data type for service_date is DATE (YYYYMMDD). If I query service_date using "select service_date from service where service_date ='20140201'", the result is showing "2/1/2014" in the result grid. However, it does not work in the query below when I convert service_date to varchar to compare with prod_date. It pulls out all the rows instead of the ones that have greater service_date.
SELECT P.PROD_ID, P.PROD_DESC, S.PROD_ID, S.SERVICE_LOC
FROM PRODUCT P
INNER JOIN SERVICE S
WHERE P.PROD_ID = S.PROD_ID
AND CAST(S.SERVICE_DATE AS VARCHAR(10)) >= P.PROD_DATE
I suggest you use date ordering instead of string/varchar ordering if possible for simplicity and since its [ probably ] closer to what your interested in and less likely to confuse
For example
'01/02/2014' >= '04/01/2013' -- if these are dates or cast to dates
but
'01/02/2014' < '04/01/2013' -- if these are strings
So to keep things simple, it makes sense to cast PROD_DATE to a date when comparing these two fields like :
SELECT P.PROD_ID, P.PROD_DESC, S.PROD_ID, S.SERVICE_LOC
FROM PRODUCT P
INNER JOIN SERVICE S
WHERE P.PROD_ID = S.PROD_ID
AND S.SERVICE_DATE >= cast(P.PROD_DATE as date format 'DD/MM/YYYY')
;
if theres any doubts on prod_dates quality as valid dates can check the conversion on all dates first ( before running/adjusting above )
This isn't 100% error proof given your date is character and could have unexpected values. It does show how you can append a leading 0 to the month value and cast it to a date when the month is determined to be a single digit based on the location of the / in the second position of the PROD_DATE value for a given row.
SELECT CASE WHEN POSITION('/' IN TRIM(P.PROD_DATE)) = 2
THEN CAST('0'|| TRIM(P.PROD_DATE) AS CHAR(10)) AS DATE FORMAT 'MM/DD/YYYY')
ELSE CAST(P.PROD_DATE AS DATE FORMAT 'MM/DD/YYYY')
END AS PROD_DATE_
FROM PRODUCT P;

Resources