Reputation: 2994
I have two columns of DateTime type. The first one stores the DateTime when a process it started, the other one stores the DateTime when that process is finished. I want to calculate the total working hours put in to complete the work. The working hours are 10 am to 7 pm and Sundays are off.
Here are a few snippets that I've jotted down:
SELECT col1, col2, floor(((date2-date1)*24*60*60)/3600)
|| ' HOURS ' ||
floor((((date2-date1)*24*60*60) -
floor(((date2-date1)*24*60*60)/3600)*3600)/60)
|| ' MINUTES ' ||
round((((date2-date1)*24*60*60) -
Floor(((date2-date1)*24*60*60)/3600)*3600 -
(floor((((date2-date1)*24*60*60) -
floor(((date2-date1)*24*60*60)/3600)*3600)/60)*60) ))
|| ' SECS ' Time_Difference
From Table_Name;
And,
Select To_Number(To_Char(date1, 'HH24')) || ':'
|| to_number(to_char(date1, 'MI')) || ':'||
to_number(to_char(date1, 'SS')) from Table_Name
Please help.
To the above solution you have mentioned, this is the result!
START DATE DAY FINISH DATE DAY Date Diff Total Hours Work Hours
07-AUG-12 21:55:21 TUE 08-AUG-12 11:09:10 WED 0 13:13:49.0 13 13
13-NOV-12 15:45:25 TUE 14-NOV-12 10:41:42 WED 0 18:56:17.0 18 18
20-DEC-12 20:31:03 THU 21-DEC-12 11:03:36 FRI 0 14:32:33.0 14 14
14-MAR-13 20:39:00 THU 15-MAR-13 11:00:04 FRI 0 14:21:4.0 14 14
07-JUN-12 21:17:36 THU 08-JUN-12 11:02:23 FRI 0 13:44:47.0 13 13
18-SEP-12 20:48:27 TUE 19-SEP-12 11:07:35 WED 0 14:19:8.0 14 14
New Scenario: https://stackoverflow.com/questions/17248741/how-to-get-business-hours-between-two-dates-in-oracle
Upvotes: 0
Views: 3739
Reputation: 10525
If i understand correctly, you want to calculate the difference between the start and finish date excluding the time before 10 am and after 7 pm.
Here's the sample query and sql fiddle.
SELECT start_time,
finish_time,
interval_time,
EXTRACT (HOUR FROM interval_time), --extract the hours,mins and seconds from the interval
EXTRACT (MINUTE FROM interval_time),
EXTRACT (SECOND FROM interval_time)
FROM (SELECT start_time,
finish_time,
NUMTODSINTERVAL (
CASE
WHEN finish_time - TRUNC (finish_time) > (19 / 24) --if finish time is after 7pm
THEN
TRUNC (finish_time) + (19 / 24) --set it to 7pm
ELSE
finish_time --else set it to actual finish time
END
- CASE
WHEN start_time - TRUNC (start_time) < (10 / 24) --if start time is before 10 am
THEN
TRUNC (start_time) + (10 / 24) --set it to 10 am.
ELSE
start_time --else set it to the actual start time
END,
'day') --subtract the both and convert the resulting day to interval
interval_time
FROM timings);
What I have done is,
Note: This query assumes that both dates fall on same day and both are not before 10am or after 7 pm.
UPDATE: To exclude holidays, the query will become complicated. I suggest writing three functions and use these functions in the query.
1st function:
FUNCTION modify_start_time (p_in_dte DATE) RETURN DATE
----------------------------------
IF p_in_dte - TRUNC (p_in_dte) < (10 / 24)
THEN
RETURN TRUNC (p_in_dte) + (10 / 24);
ELSIF p_in_dte - TRUNC (p_in_dte) > (19 / 24)
THEN
RETURN TRUNC (p_in_dte) + 1 + (10 / 24);
ELSE
RETURN p_in_dte;
END IF;
If the start time is outside the work hours, modify the start time to next nearest start time.
2nd function:
FUNCTION modify_finish_time (p_in_dte DATE) RETURN DATE
----------------------------------
IF p_in_dte - TRUNC (p_in_dte) > (19 / 24)
THEN
RETURN TRUNC (p_in_dte) + (19 / 24);
ELSIF p_in_dte - TRUNC (p_in_dte) < (10 / 24)
THEN
RETURN TRUNC (p_in_dte) - 1 + (19 / 24);
ELSE
RETURN p_in_dte;
END IF;
If the finish time is outside the work hours, modify it to the previous nearest finish time.
3rd function:
FUNCTION get_days_to_exclude (p_in_start_date DATE,
p_in_finish_date DATE) RETURN NUMBER
--------------------------------------------------------
WITH cte --get all days between start and finish date
AS ( SELECT p_in_start_date + LEVEL - 1 dte
FROM DUAL
CONNECT BY LEVEL <= p_in_finish_date + 1 - p_in_starT_date)
SELECT COUNT (1) * 9 / 24 --mutiply the days with work hours in a day
INTO l_num_holidays
FROM cte
WHERE TO_CHAR (dte, 'dy') = 'sun' --find the count of sundays
OR dte IN --fins the count of holidays, assuming leaves are stored in separate table
(SELECT leave_date
FROM leaves
WHERE leave_date BETWEEN p_in_start_date
AND p_in_finish_date);
l_num_holidays :=
l_num_holidays + ( (p_in_finish_date - p_in_start_date) * (15 / 24)); --also, if the dates span more than a day find the non working hours.
RETURN l_num_holidays;
This function finds the no of days to be excluded while calculating the duration.
So, the final query should be something like this,
SELECT start_time,
finish_time,
CASE
WHEN work_duration < 0 THEN NUMTODSINTERVAL (0, 'day')
ELSE NUMTODSINTERVAL (work_duration, 'day')
END
FROM (SELECT start_time, finish_time,
--modify_start_time (start_time), modify_finish_time (finish_time),
modify_finish_time (finish_time)
- modify_start_time (start_time)
- get_days_to_exclude (
TRUNC (modify_start_time (start_time)),
TRUNC (modify_finish_time (finish_time)))
work_duration
FROM timings);
If the duration is less than 0, ignore it by setting it to 0.
Upvotes: 2
Reputation: 9779
You can take the hour difference between the two dates and reduce the amount of non-working hours times the days between the two days. consider this example :
create table t1 (
start_hour date , finish_hour date);
insert into t1 values (
to_date('01/01/2013 12:00:00','dd/mm/yyyy hh24:mi:ss'),
to_date('01/01/2013 15:00:00','dd/mm/yyyy hh24:mi:ss'));
insert into t1 values (
to_date('01/01/2013 12:00:00','dd/mm/yyyy hh24:mi:ss'),
to_date('02/01/2013 15:00:00','dd/mm/yyyy hh24:mi:ss'));
insert into t1 values (
to_date('01/01/2013 18:00:00','dd/mm/yyyy hh24:mi:ss'),
to_date('03/01/2013 11:00:00','dd/mm/yyyy hh24:mi:ss'));
with x as (
select start_hour ,finish_hour ,
(finish_hour - start_hour) * 24 hour_diff ,
trunc(finish_hour) - trunc(start_hour) as day_diff
from t1
)
select start_hour , finish_hour ,
hour_diff - 15 * day_diff as working_hours
from x;
| START_HOUR | FINISH_HOUR | WORKING_HOURS |
-----------------------------------------------------------------------------------
| January, 01 2013 12:00:00+0000 | January, 01 2013 15:00:00+0000 | 3 |
| January, 01 2013 12:00:00+0000 | January, 02 2013 15:00:00+0000 | 12 |
| January, 01 2013 18:00:00+0000 | January, 03 2013 11:00:00+0000 | 11 |
Upvotes: 0