How do I calculate the difference between two timestamps? - sql

How to calculate the difference in minutes between timestamps TIMEIN and TIMEOUT? My table (only 3 rows):
DATE
TIMEIN
TIMEOUT
2020-05-06
14:00
15:00
2020-05-06
14:45
15:55
2020-05-07
09:00
10:45
My SQL doesn't output what I want:
SELECT (T.DATE + T.TIMEIN - T.DATE + T.TIMEOUT) AS `Duration`
FROM Transport T;
Output:
Duration
29
29
19
It should be :
Duration
60
70
105
I tried this without using the date, however that lead to an output of -1 for all rows.

Assuming that TIMEOUT is always greater than TIMEIN you can subtract the unix epochs of the 2 values and divide by 60 to get the number of minutes:
SELECT (strftime('%s', TIMEOUT) - strftime('%s', TIMEIN)) / 60 AS Duration
FROM Transport;
See the demo.

date/time is not like integers so they didn't add/subtract like integers. So convert them in a timestamp before subtraction.

Related

How do I work out minutes that occurred during office hours (9-5) and out of office hours

I have a datetime field that is when an activity starts and an int field with active_time in minutes.
What I want to do is work out in minutes (and then hours /60) how much time the activity was spent during work hours (9-5) and time outside of those hours.
E.G.
Data columns
Datetime: '2022-02-28 16:54:00.000 +0000'.
Active_time in minutes: '20'
Desired output:
Activity time in work hours: '6'
Activity time out of work hours: '14'
Can anyone help?
Many thanks.
SELECT start_time, minutes, time_pre_work, work_time, post_work_time
FROM (
SELECT *
,timeadd('minute', minutes, start_time) as time_end
,date_trunc('day', start_time) as day
,timeadd('hour', 8, day) as workday_start
,timeadd('hour', 17, day) as workday_end
,timediff('minute', least(start_time, workday_start), workday_start) as time_pre_work
,timediff('minute', greatest(start_time, workday_start), least(workday_end, time_end)) as work_time
,timediff('minute', greatest(workday_end, workday_end), greatest(workday_end, time_end)) as post_work_time
FROM VALUES
('2022-02-28 16:54:00.000'::timestamp, 20)
t(start_time, minutes)
);
gives:
START_TIME
MINUTES
TIME_PRE_WORK
WORK_TIME
POST_WORK_TIME
2022-02-28
16:54:00.000 20
0
6
14
Within day clipping:
And not correctly bounding for multi-days, this data:
FROM VALUES
('2022-02-28 16:54:00.000'::timestamp, 20),
('2022-02-28 7:54:00.000'::timestamp, 20),
('2022-02-28 6:54:00.000'::timestamp, 1000)
t(start_time, minutes)
gives:
START_TIME
MINUTES
TIME_PRE_WORK
WORK_TIME
POST_WORK_TIME
2022-02-28
16:54:00.000
20
0
6
2022-02-28
07:54:00.000
20
6
14
2022-02-28
06:54:00.000
1,000
66
540
Across days with daily clipping:
WITH input_data as (
SELECT * FROM VALUES
('2022-02-28 16:54:00.000'::timestamp, 20),
('2022-02-28 7:54:00.000'::timestamp, 20),
('2022-02-28 6:54:00.000'::timestamp, 3000)
t(start_time, minutes)
), range as(
SELECT row_number() over(order by null)-1 as rn
FROM TABLE(generator(ROWCOUNT => 100))
), day_condition as (
SELECT *
,timeadd('minute', minutes, start_time) as time_end
,date_trunc('day', dateadd('day', r.rn, start_time)) as r_day_start
,dateadd('day', 1, r_day_start ) as r_day_end
,greatest(r_day_start, start_time) as clip_start
,least(r_day_end, time_end) as clip_end
-- insert logic for "which day is it and what hours it has here"
,timeadd('hour', 8, r_day_start) as workday_start
,timeadd('hour', 17, r_day_start) as workday_end
FROM input_data i
JOIN range r ON r.rn <= datediff(day, start_time, timeadd('minute', minutes, start_time))
)
SELECT
start_time
,minutes
,r_day_start
--,clip_start
--,clip_end
,timediff('minute', least(clip_start, workday_start), workday_start) as time_pre_work
,timediff('minute', greatest(clip_start, workday_start), least(workday_end, clip_end)) as work_time
,timediff('minute', greatest(workday_end, workday_end), greatest(workday_end, clip_end)) as post_work_time
FROM day_condition
ORDER BY 1,3;
START_TIME
MINUTES
R_DAY_START
TIME_PRE_WORK
WORK_TIME
POST_WORK_TIME
2022-02-28 06:54:00.000
3,000
2022-02-28 00:00:00.000
66
540
420
2022-02-28 06:54:00.000
3,000
2022-03-01 00:00:00.000
480
540
420
2022-02-28 06:54:00.000
3,000
2022-03-02 00:00:00.000
480
54
0
2022-02-28 07:54:00.000
20
2022-02-28 00:00:00.000
6
14
0
2022-02-28 16:54:00.000
20
2022-02-28 00:00:00.000
0
6
14
I couldn't think of a way to approach this using built in date functions, so this is maybe not the prettiest solution, but the math is there. This converts the start_timestamp/active_time into minutes relative to your business hours
I subtracted 540 to essentially set the time 9:00AM to "minute 0" to make the numbers easier to work with. This makes 5:00PM "minute 480"
Then it's just a matter of subtracting times within and outside of your business hours.
set startdatetime = '2022-03-23 7:31:00'::timestamp_ntz;
set active_minutes = 125;
set business_start = 0;
set business_end = 480;
select
-540 + (hour($startdatetime) * 60 + minute($startdatetime)) as start_minute,
start_minute + $active_minutes as end_minute,
-- End time (within business hours) - Start time (within business hours). Can't be less than 0
greatest(0, least(end_minute, $business_end) - greatest(start_minute, $business_start)) as minutes_during_business,
-- End - Start. With any "business minutes" ignored. first for pre-work minutes, then for post-work minutes
least(end_minute, $business_start) - least(start_minute, $business_start)
+ greatest(end_minute, $business_end) - greatest(start_minute, $business_end) as minutes_outside_business,
minutes_during_business / 60 as hours_during_business,
minutes_outside_business / 60 as hours_outside_business;
;
This does not work well if your active minutes spans into business hours of the following day. That would take some extra handling.
You could also add on seconds, and convert all of the hardcoded numbers to seconds if you do want that extra granularity.

Extract 30 minutes from timestamp and group it by 30 mins time interval -PGSQL

In PostgreSQL I am extracting hour from the timestamp using below query.
select count(*) as logged_users, EXTRACT(hour from login_time::timestamp) as Hour
from loginhistory
where login_time::date = '2021-04-21'
group by Hour order by Hour;
And the output is as follows
logged_users | hour
--------------+------
27 | 7
82 | 8
229 | 9
1620 | 10
1264 | 11
1990 | 12
1027 | 13
1273 | 14
1794 | 15
1733 | 16
878 | 17
126 | 18
21 | 19
5 | 20
3 | 21
1 | 22
I want the same output for same SQL for 30 mins. Please suggest
SELECT to_timestamp((extract(epoch FROM login_time::timestamp)::bigint / 1800) * 1800)::timestamp AS interval_30_min
, count(*) AS logged_users
FROM loginhistory
WHERE login_time::date = '2021-04-21' -- inefficient!
GROUP BY 1
ORDER BY 1;
Extracting the epoch gets the number of seconds since the epoch. Integer division truncates. Multiplying back effectively rounds down, achieving the same as date_trunc() for arbitrary time intervals.
1800 because 30 minutes contain 1800 seconds.
Detailed explanation:
Truncate timestamp to arbitrary intervals
The cast to timestamp makes me wonder about the actual data type of login_time? If it's timestamptz, the cast depends on your current time zone setting and sets you up for surprises if that setting changes. See:
How do I match an entire day to a datetime field?
Subtract hours from the now() function
Ignoring time zones altogether in Rails and PostgreSQL
Depending on the actual data type, and exact definition of your date boundaries, there is a more efficient way to phrase your WHERE clause.
You can change the column on which you're aggregating to use the minute too:
select
count(*) as logged_users,
CONCAT(EXTRACT(hour from login_time::timestamp), '-', CASE WHEN EXTRACT(minute from login_time::timestamp) < 30 THEN 0 ELSE 30 END) as HalfHour
from loginhistory
where login_time::date = '2021-04-21'
group by HalfHour
order by HalfHour;

Time Difference between two time columns in sql server

I have the issue in getting the minutes difference between two time columns in sql server.
Column1: Starttime
Column2: Endtime
These two are of the type nchar(10) , I converted them into time format in the below way and using the datediff function.
If the two columns have AM format or PM format, then difference of minutes is coming fine. But if i have start time is in PM format and End time as AM (SQL taking it as next day) format then i am getting negative minutes than expected.
select
dty_act_start_time,
dty_act_end_time,
datediff(minute, convert(varchar,dty_act_start_time,114),convert(varchar,dty_act_end_time,114))
from DB.Mydatabase;
Please let me know how to get the correct difference in minutes.
Just add 1440 minutes (number of minutes in 24 hours) if start time is greater than end time:
select
start_time_time,
end_time_time,
datediff(minute, start_time_time, end_time_time) + iif(start_time_time <= end_time_time, 0, 1440) as diff
from (
select
convert(time(0), dty_act_start_time, 114) as start_time_time,
convert(time(0), dty_act_end_time, 114) as end_time_time
from (values
('09:00am', '10:00pm'),
('10:00pm', '09:00am')
) tests(dty_act_start_time, dty_act_end_time)
) x
Result:
start_time_time | end_time_time | diff
09:00:00 | 22:00:00 | 780
22:00:00 | 09:00:00 | 660

Unable to convert 4 digit int value to 24 hour time format

My input data format is in the form of 4 digit numbers which represent 24 hour time format. I am trying to find the time difference in minutes between 2 such fields - Expected Arrival Time and Scheduled Arrival Time in minutes. For that I need to convert 4 digit int values to 24 hour time format but I am unable to do so.
I have tried innumerable permutations and combinations but to no avail.
Expected Arrival time: 1902
Scheduled Arrival Time: 1806
Expected result: 56
Any help will be highly appreciated.
The difference between these two times are 56 minutes.
Expected Arrival time: 1902 (07:02 PM) //2 minutes from 7:00PM
Scheduled Arrival Time: 1806 (06:06 PM) //54 minutes to 7:00PM
select (unix_timestamp('1902','HHmm') - unix_timestamp('1806',"HHmm"))/60;
+-------+--+
| _c0 |
+-------+--+
| 56.0 |
+-------+--+
(or)
select int((unix_timestamp('1902','HHmm') - unix_timestamp('1806',"HHmm"))/60);
+------+--+
| _c0 |
+------+--+
| 56 |
+------+--+
(t2 / 100) * 60 + t2 % 100
- (t1 / 100) * 60 - t1 % 100
+ case when t2 < t1 then 1440 else 0 end
I'm assuming that the two times can span midnight.

conversion of time into minute count

Ok so I need to convert a value from a table into an absolute minute count.
What I mean by that is this.
I currently have data like this
ID day time
1 1 00:04
2 1 01:08
3 2 00:08
4 2 02:04
I want it to convert to total count of minutes, not resettting back to zero for each day. so it would be
ID day time
1 1 04
2 1 68
3 2 1448
4 2 1564
Currently the time data is varchar(5) and not in date time. I have tried
DATEDIFF(MINUTE, DATEADD(DAY, DATEDIFF(DAY, 0,dispatchday), 0), dispatchday)
and that returned nothing but zero's
SQL Server I presume? This should take care of it. It doesn't make a difference what kind of field time is in, as long as it contains a valid time value.
select (day-1) * 1440 + datediff(minute, '00:00:00', time) as result from yourtable