daniel_spiri
asked on
Calculate Working Hours in interval
Hello Experts,
I have a Postgres DB with some tables with columns of timestamp type without time zone
like: Creation_Date_UTC and Closure_date_UTC
ie: 2010-11-05 07:42:00
2010-11-05 18:54:00
What I want is maybe a function able to calculate "Working Hours" [09:00:00 to 18:00:00] from the interval in those two columns.
In this function I should also be able to specify the "working hours" interval, that the function will use for calculation. The result will be the number of working hours between the two dates [columns] calculated based on specified working hours interval.
ie: get_working_hours(Creation _Date_UTC, Closure_date_UTC, Work_Period_start, Work_Period_end)
ie: get_working_hours(2010-11- 05 10:00:00, 2010-11-05 17:00:00, 09:00:00, 18:00:00)
In this case the result should be 7. [hours]
My date format in Postgres is DD/MM/YYYY and time format 24h.
Thanks so much!
I have a Postgres DB with some tables with columns of timestamp type without time zone
like: Creation_Date_UTC and Closure_date_UTC
ie: 2010-11-05 07:42:00
2010-11-05 18:54:00
What I want is maybe a function able to calculate "Working Hours" [09:00:00 to 18:00:00] from the interval in those two columns.
In this function I should also be able to specify the "working hours" interval, that the function will use for calculation. The result will be the number of working hours between the two dates [columns] calculated based on specified working hours interval.
ie: get_working_hours(Creation
ie: get_working_hours(2010-11-
In this case the result should be 7. [hours]
My date format in Postgres is DD/MM/YYYY and time format 24h.
Thanks so much!
ASKER
Hey Earthman,
Thanks for the pointers!
I had those in mind, but right now, I don't worry about weekends, religious festivals, shift patterns etc. Although those features would be nice, I know they are more difficult and need dedicated professional time.
In my case I will have an interval of Monday to Friday to calculate on.
Can your script be used like that?
Thanks for the pointers!
I had those in mind, but right now, I don't worry about weekends, religious festivals, shift patterns etc. Although those features would be nice, I know they are more difficult and need dedicated professional time.
In my case I will have an interval of Monday to Friday to calculate on.
Can your script be used like that?
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Thanks again Earthman!
ASKER
These are some of the results I am receiving after calling the function:
2010-11-07 17:09:00 2010-11-08 04:18:00 NULL
2010-11-05 08:23:00 2010-11-08 08:06:00 NULL
2010-11-05 08:26:00 2010-11-05 08:27:00 -00:33:00
2010-11-05 09:44:00 2010-11-08 08:19:00 NULL
2010-11-05 10:50:00 2010-11-05 13:13:00 02:23:00
2010-11-05 12:22:00 2010-11-05 20:56:00 05:38:00
2010-11-05 01:14:00 2010-11-05 01:19:00 -07:41:00
2010-11-05 02:49:00 2010-11-05 03:05:00 -05:55:00
2010-11-05 05:47:00 2010-11-05 07:58:00 -01:02:00
I think it's working good, but are the "minus" results supposed to be there?
2010-11-07 17:09:00 2010-11-08 04:18:00 NULL
2010-11-05 08:23:00 2010-11-08 08:06:00 NULL
2010-11-05 08:26:00 2010-11-05 08:27:00 -00:33:00
2010-11-05 09:44:00 2010-11-08 08:19:00 NULL
2010-11-05 10:50:00 2010-11-05 13:13:00 02:23:00
2010-11-05 12:22:00 2010-11-05 20:56:00 05:38:00
2010-11-05 01:14:00 2010-11-05 01:19:00 -07:41:00
2010-11-05 02:49:00 2010-11-05 03:05:00 -05:55:00
2010-11-05 05:47:00 2010-11-05 07:58:00 -01:02:00
I think it's working good, but are the "minus" results supposed to be there?
test1=> create or replace function working_hours( start_ts timestamp, end_ts timestamp, wp_start time, wp_end time ) returns interval as $$
test1$> declare
test1$> x interval := interval '0 seconds';
test1$> d1 date := date_trunc('day', start_ts);
test1$> d2 date := date_trunc('day', end_ts);
test1$> x1 timestamp := greatest(d1 + wp_start, start_ts);
test1$> x2 timestamp := least(d2 + wp_end, end_ts);
test1$> wd interval;
test1$> dow integer := date_part( 'dow', d2 );
test1$> begin
test1$> if ( dow = 1 and end_ts < (d2 + wp_start) ) then
test1$> d2 := d2 - 3;
test1$> x2 := d2 + wp_end;
test1$> elsif ( dow = 0 ) then
test1$> d2 := d2 - 2;
test1$> x2 := d2 + wp_end;
test1$> elsif ( dow = 6 ) then
test1$> d2 := d2 - 1;
test1$> x2 := d2 + wp_end;
test1$> end if;
test1$> dow := date_part( 'dow', x1 );
test1$> if ( dow = 5 and start_ts > (d1 + wp_end) ) then
test1$> d1 := d1 + 3;
test1$> x1 := d1 + wp_start;
test1$> elsif ( dow = 0 ) then
test1$> d1 := d1 + 1;
test1$> x1 := d1 + wp_start;
test1$> elsif ( dow = 6 ) then
test1$> d1 := d1 + 2;
test1$> x1 := d1 + wp_start;
test1$> end if;
test1$>
test1$> if d1 = d2 and dow > 0 and dow < 6 then
test1$> if ( x2 > x1 ) then
test1$> x := x2 - x1;
test1$> end if;
test1$> else
test1$> wd := wp_end - wp_start;
test1$> if ( x2 > x1 ) then
test1$> if ( (d1+wp_end) > x1 ) then
test1$> x := (d1+wp_end) - x1;
test1$> end if;
test1$> d1 := d1 + 1;
test1$> while ( d1 < d2 ) loop
test1$> dow := date_part( 'dow', d1 );
test1$> if ( dow > 0 and dow < 6 ) then
test1$> x := x + wd;
test1$> end if;
test1$> d1 := d1 + 1;
test1$> end loop;
test1$> if ( d1 <> d2 and (d2+wp_start) < x2 ) then
test1$> x := x + ( x2 - (d2 + wp_start));
test1$> end if;
test1$> end if;
test1$> end if;
test1$> return x;
test1$> end;
test1$> $$ LANGUAGE plpgsql;
CREATE FUNCTION
test1=> create or replace function working_hours( start_ts timestamp, end_ts timestamp, wp_start time, wselect id,Creation_Date_UTC,Closu re_Date_UT C, working_hours( Creation_Date_UTC,Closure_ Daee_UTC,' 09:00:00': :time,'17: 00:00'::ti me) from xi;
id | creation_date_utc | closure_date_utc | working_hours
----+--------------------- -------+-- ---------- ---------- ------+--- ---------- --
1 | 2010-11-26 07:49:23.570259 | 2010-11-26 17:01:23.570259 | 08:00:00
2 | 2010-11-07 17:09:00 | 2010-11-08 04:18:00 | 00:00:00
3 | 2010-11-05 08:23:00 | 2010-11-08 08:06:00 | 08:00:00
4 | 2010-11-05 08:26:00 | 2010-11-05 08:27:00 | 00:00:00
5 | 2010-11-05 09:44:00 | 2010-11-08 08:19:00 | 07:16:00
6 | 2010-11-05 10:50:00 | 2010-11-05 13:13:00 | 02:23:00
7 | 2010-11-05 12:22:00 | 2010-11-05 20:56:00 | 04:38:00
8 | 2010-11-05 01:14:00 | 2010-11-05 01:19:00 | 00:00:00
9 | 2010-11-05 02:49:00 | 2010-11-05 03:05:00 | 00:00:00
10 | 2010-11-05 05:47:00 | 2010-11-05 07:58:00 | 00:00:00
(10 rows)
test1=>
test1$> declare
test1$> x interval := interval '0 seconds';
test1$> d1 date := date_trunc('day', start_ts);
test1$> d2 date := date_trunc('day', end_ts);
test1$> x1 timestamp := greatest(d1 + wp_start, start_ts);
test1$> x2 timestamp := least(d2 + wp_end, end_ts);
test1$> wd interval;
test1$> dow integer := date_part( 'dow', d2 );
test1$> begin
test1$> if ( dow = 1 and end_ts < (d2 + wp_start) ) then
test1$> d2 := d2 - 3;
test1$> x2 := d2 + wp_end;
test1$> elsif ( dow = 0 ) then
test1$> d2 := d2 - 2;
test1$> x2 := d2 + wp_end;
test1$> elsif ( dow = 6 ) then
test1$> d2 := d2 - 1;
test1$> x2 := d2 + wp_end;
test1$> end if;
test1$> dow := date_part( 'dow', x1 );
test1$> if ( dow = 5 and start_ts > (d1 + wp_end) ) then
test1$> d1 := d1 + 3;
test1$> x1 := d1 + wp_start;
test1$> elsif ( dow = 0 ) then
test1$> d1 := d1 + 1;
test1$> x1 := d1 + wp_start;
test1$> elsif ( dow = 6 ) then
test1$> d1 := d1 + 2;
test1$> x1 := d1 + wp_start;
test1$> end if;
test1$>
test1$> if d1 = d2 and dow > 0 and dow < 6 then
test1$> if ( x2 > x1 ) then
test1$> x := x2 - x1;
test1$> end if;
test1$> else
test1$> wd := wp_end - wp_start;
test1$> if ( x2 > x1 ) then
test1$> if ( (d1+wp_end) > x1 ) then
test1$> x := (d1+wp_end) - x1;
test1$> end if;
test1$> d1 := d1 + 1;
test1$> while ( d1 < d2 ) loop
test1$> dow := date_part( 'dow', d1 );
test1$> if ( dow > 0 and dow < 6 ) then
test1$> x := x + wd;
test1$> end if;
test1$> d1 := d1 + 1;
test1$> end loop;
test1$> if ( d1 <> d2 and (d2+wp_start) < x2 ) then
test1$> x := x + ( x2 - (d2 + wp_start));
test1$> end if;
test1$> end if;
test1$> end if;
test1$> return x;
test1$> end;
test1$> $$ LANGUAGE plpgsql;
CREATE FUNCTION
test1=> create or replace function working_hours( start_ts timestamp, end_ts timestamp, wp_start time, wselect id,Creation_Date_UTC,Closu
id | creation_date_utc | closure_date_utc | working_hours
----+---------------------
1 | 2010-11-26 07:49:23.570259 | 2010-11-26 17:01:23.570259 | 08:00:00
2 | 2010-11-07 17:09:00 | 2010-11-08 04:18:00 | 00:00:00
3 | 2010-11-05 08:23:00 | 2010-11-08 08:06:00 | 08:00:00
4 | 2010-11-05 08:26:00 | 2010-11-05 08:27:00 | 00:00:00
5 | 2010-11-05 09:44:00 | 2010-11-08 08:19:00 | 07:16:00
6 | 2010-11-05 10:50:00 | 2010-11-05 13:13:00 | 02:23:00
7 | 2010-11-05 12:22:00 | 2010-11-05 20:56:00 | 04:38:00
8 | 2010-11-05 01:14:00 | 2010-11-05 01:19:00 | 00:00:00
9 | 2010-11-05 02:49:00 | 2010-11-05 03:05:00 | 00:00:00
10 | 2010-11-05 05:47:00 | 2010-11-05 07:58:00 | 00:00:00
(10 rows)
test1=>
ASKER
This is great Earthman, thanks alot!
I've run some tests and found that sometimes it seems to return incorrect results.
working_hours("Start_date_ UTC", "Restoration_date_UTC", '05:00:00'::time without time zone, '16:00:00'::time without time zone)
Restoration_date_UTC Start_date_UTC working_hours actual_result Differences
02.05.2009 10:55 01.04.2009 10:19 236:41:00 247,6833333 10,98333333
08.01.2010 06:59 03.04.2009 07:28 2197:32:00 2199,516667 2,016666667
30.04.2009 09:50 06.04.2009 11:12 191:48:00 196,6333333 4,833333333
The results from actual_result column are from a VBA function we are using to calculate working hours. Based on the Differences column we can see that the sql workin_hours function is skipping a few hours. Where's the error?
Again, thank you very much for your help Earthman!
I am grateful!
I've run some tests and found that sometimes it seems to return incorrect results.
working_hours("Start_date_
Restoration_date_UTC Start_date_UTC working_hours actual_result Differences
02.05.2009 10:55 01.04.2009 10:19 236:41:00 247,6833333 10,98333333
08.01.2010 06:59 03.04.2009 07:28 2197:32:00 2199,516667 2,016666667
30.04.2009 09:50 06.04.2009 11:12 191:48:00 196,6333333 4,833333333
The results from actual_result column are from a VBA function we are using to calculate working hours. Based on the Differences column we can see that the sql workin_hours function is skipping a few hours. Where's the error?
Again, thank you very much for your help Earthman!
I am grateful!
https://www.experts-exchange.com/questions/21449885/Adding-Week-Days-to-a-Date.html
This is a complicated problem and one you need to define the requirements for weekends, religious festivals, bank holidays, shift patterns, lunch breaks, dentists appointments etc.
ps You can simply subtract timestamp from the other to get the interval...
a simple approach if Closure date and creation dates are on the same day...
select min(date_trunc('day',Closu
max(date_trunc('day',Creat
as working_hours from your_query;