Reputation: 9
I would like to get the total number of hours,minute, and seconds difference between two timestamp fields based on starting and ending time in postgres
Table supermarket
has fields opening_time
and closing_time
Table orders
have the fields order_id
, arrived_date
, and picked_date
I want to calculate the total time spend to pick an order. (The orders can be picked only the supermarket is open but the orders will get placed and queued anytime in the day)
Condition: The total picking time should be calculated considering the opening and closing time of the store.
Example
Consider opening_time
is 09:00:00
and closing_time
is 22:00:00
Case 1: if an order arrives on 2020-09-08 10:00:00
and is picked at 2020-09-08 12:00:00
, then the total picking time should be 02 hours
Case 2: if an order arrives on 2020-09-08 06:00:00
and is picked at 2020-09-08 12:00:00
, then the total picking time should be 03 hours
, not 06 hours
considering the opening time
Case 3: if an order arrives on 2020-09-08 23:00:00
and is picked next day at 2020-09-09 10:00:00
, then the total picking time should be 01 hour
considering closing and opening time
Upvotes: 0
Views: 758
Reputation: 164809
Timestamps can simply be subtracted resulting in an interval.
test=# select '2020-09-08 12:00:00'::timestamp - '2020-09-08 10:00:00'::timestamp;
?column?
----------
02:00:00
(1 row)
That interval can then be formatted with to_char
.
test=# select to_char('2020-09-08 12:00:00'::timestamp - '2020-09-08 10:00:00'::timestamp, 'HH24 hours MI "minutes"');
to_char
---------------------
02 hours 00 minutes
(1 row)
To get the correct interval considering opening and closing time, you need a little bit of logic to figure out the actual picking start time.
select
case
when arrived_date::time < opening_time
-- It arrived before you're open. Start when you open that day.
arrived_date::date + opening_time
when arrived_date::time > closing_time
-- It arrived after you closed. Start when you open tomorrow.
arrived_date::date + '1 day'::interval + opening_time
else
-- It arrived while you're open. Start when it arrives.
arrived_date
end as picking_start
The trick is to cast arrived_date
to time
, truncating the date part, to compare with the opening and closing times. Similarly, we can cast arrived_date
to date
and use just the date part, then add the opening time. This assumes that arrived_date
is a timestamp
and that opening_time
and closing_time
are time
columns.
This could be condensed into a function for easy use.
Upvotes: 0
Reputation: 222462
It is a bit more complicated that it would seem to, especially if the ranges may expand over more than 24 hours. The safest solution approach might be a brute-force approach that generates all hours in the range, then filtering and aggregation:
select s.*, x.*
from supermarket s
cross join lateral (
select count(*) no_hours
from generate_series(s.opening_time, s.closing_time, '1 hour') x(x_time)
where x_time::time >= '09:00:00'::time and x_time::time < '22:00:00'::time
) x
This assumes that the opening and closing dates are truncated to the hour, as shown in your examples. If you want to handle minutes, then:
select s.*, x.*
from supermarket s
cross join lateral (
select count(*) no_minutes
from generate_series(s.opening_time, s.closing_time, '1 minute') x(x_time)
where x_time::time >= '09:00:00'::time and x_time::time < '22:00:00'::time
) x
Upvotes: 1