Reputation: 525
I want to find the time difference between NOW()
and a timestamp column.
When calculating the time difference, only business hours from 9am till 5pm and weekdays needed to be accounted.
e.g. for the first record 2021-04-21 07:56:36
, if the now is 2021-04-21 10:00:00
, the time difference must be calculated from 9 am, in this case, the result would be 1 hour, like this:
(NOW() - created_at) - ('2021-04-21 09:00'::timestamp - created_at) AS timediff
For the second record with 2021-04-19 21:55:46
, the result would be 9 hrs made of 8+1 hrs as from 2021-04-20 09:00
to 2021-04-20 17:00
and 2021-04-21 09:00
till 2021-04-21 10:00:00
For the third record with 2021-04-17 14:22:05
, it would be 17 hrs, made of 8+8+1 hrs.
Sample data:
+----------------------------------+---------------------+
| id | created_at |
+----------------------------------+---------------------+
| 9d38ea6e7077400db310cfc47b31d482 | 2021-04-21 07:56:36 |
| 9d38ea6e7077400db310cfc47b31d481 | 2021-04-19 21:55:46 |
| 9d38ea6e7077400db310cfc47b31d480 | 2021-04-17 14:22:05 |
| 9d38ea6e7077400db310cfc47b31d479 | 2021-04-18 22:43:44 |
+----------------------------------+---------------------+
The desired outcome:
+----------------------------------+---------------------+----------+
| id | created_at | timediff |
+----------------------------------+---------------------+----------+
| 9d38ea6e7077400db310cfc47b31d482 | 2021-04-21 07:56:36 | 01:00:00 |
| 9d38ea6e7077400db310cfc47b31d481 | 2021-04-19 21:55:46 | 09:00:00 |
| 9d38ea6e7077400db310cfc47b31d480 | 2021-04-17 14:22:05 | 17:00:00 |
| 9d38ea6e7077400db310cfc47b31d479 | 2021-04-18 22:43:44 | 17:00:00 |
+----------------------------------+---------------------+----------+
Upvotes: 1
Views: 383
Reputation: 1630
I'd suggest generating all the ranges of business hours between created_at
and current_timestamp and summing them. While it is far from optimal solution in terms of performance I think it is the most straight-forward approach.
I use timestamp with time zone range tstzrange with intersection operator (*).
SELECT id,
created_at,
SUM(upper(business_range) - lower(business_range) ) business_hours
FROM (
SELECT id,
created_at,
tstzrange(date_in_range + '09:00'::time with time zone, date_in_range + '17:00'::time with time zone)
* tstzrange(created_at, current_timestamp) as business_range
FROM (
SELECT id,
created_at,
created_at::date + generate_series(0, current_timestamp::date - created_at::date) as date_in_range
FROM times
) dates_in_range
WHERE date_part('dow', date_in_range) in (1,2,3,4,5)
) business_hour_ranges
GROUP BY
id,
created_at
See the setup and example in db<>fiddle
While the implementation was made to be easy to understand and debug it may result in poor performance - especially if used with very old created_at dates. If that may be the case in your system, you may be better off writing function that checks the day of week of the created_at and current_date, finds the number of days between them and determines work hours.
Upvotes: 2