I want to calculate the time difference between two datetime. Saturday and sunday need to be excluded from the calculation.
For exemple difference between 2023-01-10 15:12:24
and 2023-01-01 10:34:36
is 6 days 4 hours 37 minutes 48 seconds
according to PHP carbon.
<?php
require 'vendorcarbonautoload.php';
use CarbonCarbonImmutable;
use CarbonCarbonInterval;
$created = CarbonImmutable::parse("2023-01-02 10:34:36");
$firstResponse = CarbonImmutable::parse("2023-01-10 15:12:24");
$diffInSeconds = 0;
$step = $created;
while ($step < $firstResponse) {
if ($step->isWeekend()) {
$step = $step->next('Monday');
continue;
}
$nextStep = min($firstResponse, $step->addDay()->startOfDay());
$diffInSeconds += $step->diffInSeconds($nextStep);
$step = $nextStep;
}
echo CarbonInterval::seconds($diffInSeconds)->cascade()->forHumans(); //6 days 4 hours 37 minutes 48 seconds
The goal is to calculate this value using SQL.
I’ve come to this following query :
WITH RECURSIVE date_range AS (
SELECT '2023-01-02 10:34:36'::timestamp AS date
UNION ALL
SELECT CASE
WHEN EXTRACT(ISODOW FROM date) IN (6, 7) THEN date + INTERVAL '1 day'*(8-EXTRACT(ISODOW FROM date))
ELSE date + INTERVAL '1 DAY'
END
FROM date_range
WHERE date + INTERVAL '1 DAY' < '2023-01-10 15:12:24'::timestamp
)
SELECT
CONCAT(
FLOOR(diff / 86400), ' days ',
FLOOR((diff % 86400) / 3600), ' hours ',
FLOOR((diff % 3600) / 60), ' minutes ',
FLOOR(diff % 60), ' seconds'
) AS duration
FROM (
SELECT
EXTRACT(EPOCH FROM ('2023-01-10 15:12:24'::timestamp - MAX(date))::interval) AS diff
FROM date_range
) t;
Output :
----------------------------------------
| duration |
----------------------------------------
| 0 days 4 hours 37 minutes 48 seconds |
----------------------------------------
I don’t understand why days has value equal to 0.
How can I fix the days value ?
Fiddle : https://www.db-fiddle.com/f/3V6QVdE1PPETKS6yN33zdE/0
3
Answers
Really interesting question which completely distracted me from work! This question is similar and could be useful to you:Get all dates between two dates in SQL Server .
I think the recommendation of building a calendar table would help you a lot!
To directly answer your question I think it you need to change from max to min, as I have, here:
However even with this you return 8 days when I think the answer should be 7….
I’m not overly familiar with the recursive so I went for something like the below. The case statement can definitely be optimised. {note I’m snowflake dialect}
Output :
| output |
| 7 days 4 hours 37 minutes 48 seconds |
The reason you always get 0 days is because you selecting
MAX(date)
which turns out to be2023-01-10 10:34:36
(the first value that satisfies your exit condition) which is 0 days from2023-01-10 15:12:24
. Perhaps you should select MIN(date). I am not even sure that is valid for all timestamps not if the start and/or end dates specified fall on a weekend?But why are you messing around with
epoch
from an interval then the "complicated" date/time calculations. Your process centers around 2 hard-coded timestamps. The subtraction of 2 timestamps gives aninterval
then you can directly extract each field. Your query reduces to: (see demo)You can even wrap the query in a SQL function and completely hide it away.
Since your difference time is calculated by extract
end date
to theMAX(date)
– which its value is same date asend date
or the last working date beforeend date
, so difference days value could not be calculated correctly.You could achieve your desired result by using below query.
start date
toend date
by using generate_series function, then get only working days from this series.working time
per each date (data type ofworking time
will be Postgres’s interval).working time
of all working dates in date series.See demo here.