获取星期日工作日函数 return 整数
get work days function to return whole number on sunday
函数 number_of_days(start_date, end_date) 应该计算 start_date 和 end_date 之间有多少个工作日。只要不是 星期日 .
,它就适用于任何 start_date
计算有 3 个步骤:
取整周之前的天数(对于小范围日期,如 21 日至 24 日,整周可以为 0)
然后它增加整周
将整周后剩余的所有天数相加。
我需要第 2 步来生成整数,例如 0、5、10,而在这个例子中我得到的是 0,7142857142857142857142857142857142857,因为 start_date 参数是星期日。
我可以使用 ROUND() 来解决它,但也许有更好的方法?
CREATE OR REPLACE FUNCTION number_of_days(start_date IN DATE, end_date IN DATE)
RETURN NUMBER
IS v_number_of_days NUMBER;
first_week_day DATE := TO_DATE('31-12-2017', 'DD-MM-YYYY');
BEGIN
--step 1
SELECT ( CASE WHEN MOD(start_date - first_week_day, 7) BETWEEN 2 AND 5
THEN 6 - MOD(start_date - first_week_day, 7)
ELSE 0 END )
+
--step 2
(( CASE WHEN MOD(end_date - first_week_day, 7) < 7
THEN end_date - MOD(end_date - first_week_day, 7)
ELSE end_date END )
-
( CASE WHEN MOD(start_date - first_week_day, 7) > 1
THEN start_date + 8 - MOD(start_date - first_week_day, 7)
ELSE start_date END ) + 1
) / 7 * 5
+
--step 3
( CASE WHEN MOD(end_date - first_week_day,7) BETWEEN 1 AND 6
THEN CASE WHEN MOD(end_date - first_week_day, 7) = 6
THEN MOD(end_date - first_week_day, 7) - 1
ELSE MOD(end_date - first_week_day, 7) END
ELSE 0 END )
INTO v_number_of_days
FROM DUAL;
RETURN v_number_of_days;
END;
--test
SELECT number_of_days(TO_DATE('21-11-2021', 'DD-MM-YYYY'), TO_DATE('24-11-2021', 'DD-MM-YYYY'))
FROM DUAL;
这应该能让你入门,因为我不明白你的要求。
with time_between as (
select trunc ( timestamp'2021-06-14 06:00:00' ) -
trunc ( timestamp'2021-06-08 14:00:00' ) t
from dual
)
select *
from time_between;
T
6
你只是把事情搞得太复杂了...
首先,最好在 ISO 周内工作:从星期一开始。
- https://en.wikipedia.org/wiki/ISO_week_date
- https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions230.htm
- https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/TRUNC-date.html#GUID-BC82227A-2698-4EC8-8C1A-ABECC64B0E79
因此您可以使用 trunc(dt, 'IW')
轻松获得 ISO 周的第一天。例如,11 月 19 日是星期五,我们可以使用 trunc(date'2021-11-19','IW')
:
轻松获得本周的第一天
SQL> select trunc(date'2021-11-19','IW') xx from dual;
XX
-------------------
2021-11-15 00:00:00
所以 2021-11-15 是星期一。
现在假设我们有一些日期:
Mo Tu We Th Fr Sa Su
-- -- -- -- -- -- --
X X X X
X X X X X X X
X X X X X X X
X X X X X X X
X X X X X X X
X X X X X X X
X X X X X X X
X X
我们可以很容易地得到整周的工作日数 (count_full_weeks * 5/7),所以我们排除它们:
Mo Tu We Th Fr Sa Su
-- -- -- -- -- -- --
X X X X
X X
然后,让我们对其进行图形化转换,因为我们知道剩余天数总是小于7天(小于一周),而第一天将是前7天之一:
Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su
-- -- -- -- -- -- -- -- -- -- -- -- -- --
X X X X X X
让我们用他们的数字替换 Mo,Tu,We...:[=24=]
0 1 2 3 4 5 6 0 1 2 3 4 5 6
-- -- -- -- -- -- -- -- -- -- -- -- -- --
X X X X X X
因为我们知道这部分周的天数不能为 7,所以我们知道最晚结束时间为:
0 1 2 3 4 5 6 0 1 2 3 4 5 6
-- -- -- -- -- -- -- -- -- -- -- -- -- --
X X X X X X
现在让我们将第二周的数字替换为 7-13:
0 1 2 3 4 5 6 7 8 9 10 11 12 13
-- -- -- -- -- -- -- -- -- -- -- -- -- --
X X X X X X
现在我们可以理解我们只需要检查第 5 天和第 6 天,即检查我们的经期是否包含 5 和 6。
所以会是这样(我已经尽量详细了):
CREATE OR REPLACE FUNCTION number_of_days(start_date IN DATE, end_date IN DATE)
RETURN NUMBER
as
days_between int;
full_weeks int;
remaining_days int;
remaining_workdays int;
v_first_week_day date;
v_start int;
v_end int;
result int;
begin
days_between := end_date - start_date;
full_weeks := trunc(days_between/7);
remaining_days:= mod(days_between,7); -- or days_between-full_weeks*7
v_first_week_day := trunc(start_date,'IW');
v_start := start_date - v_first_week_day;
v_end := v_start + remaining_days - 1;
remaining_workdays := remaining_days
- case when 5 between v_start and v_end then 1 else 0 end
- case when 6 between v_start and v_end then 1 else 0 end
;
result := full_weeks * 5 + remaining_workdays;
RETURN result;
end;
/
我做了很多子步骤和中间计算和变量,只是为了让它更清楚。显然,您可以使其更短,例如:
CREATE OR REPLACE FUNCTION number_of_days(start_date IN DATE, end_date IN DATE)
RETURN NUMBER
as
begin
return 5*trunc((end_date-start_date)/7) + mod((end_date-start_date),7)
- case when 5-(start_date - trunc(start_date,'IW')) between 0 and mod((end_date-start_date),7)-1 then 1 else 0 end
- case when 6-(start_date - trunc(start_date,'IW')) between 0 and mod((end_date-start_date),7)-1 then 1 else 0 end
;
end;
/
函数 number_of_days(start_date, end_date) 应该计算 start_date 和 end_date 之间有多少个工作日。只要不是 星期日 .
,它就适用于任何 start_date计算有 3 个步骤:
取整周之前的天数(对于小范围日期,如 21 日至 24 日,整周可以为 0)
然后它增加整周
将整周后剩余的所有天数相加。
我需要第 2 步来生成整数,例如 0、5、10,而在这个例子中我得到的是 0,7142857142857142857142857142857142857,因为 start_date 参数是星期日。
我可以使用 ROUND() 来解决它,但也许有更好的方法?
CREATE OR REPLACE FUNCTION number_of_days(start_date IN DATE, end_date IN DATE)
RETURN NUMBER
IS v_number_of_days NUMBER;
first_week_day DATE := TO_DATE('31-12-2017', 'DD-MM-YYYY');
BEGIN
--step 1
SELECT ( CASE WHEN MOD(start_date - first_week_day, 7) BETWEEN 2 AND 5
THEN 6 - MOD(start_date - first_week_day, 7)
ELSE 0 END )
+
--step 2
(( CASE WHEN MOD(end_date - first_week_day, 7) < 7
THEN end_date - MOD(end_date - first_week_day, 7)
ELSE end_date END )
-
( CASE WHEN MOD(start_date - first_week_day, 7) > 1
THEN start_date + 8 - MOD(start_date - first_week_day, 7)
ELSE start_date END ) + 1
) / 7 * 5
+
--step 3
( CASE WHEN MOD(end_date - first_week_day,7) BETWEEN 1 AND 6
THEN CASE WHEN MOD(end_date - first_week_day, 7) = 6
THEN MOD(end_date - first_week_day, 7) - 1
ELSE MOD(end_date - first_week_day, 7) END
ELSE 0 END )
INTO v_number_of_days
FROM DUAL;
RETURN v_number_of_days;
END;
--test
SELECT number_of_days(TO_DATE('21-11-2021', 'DD-MM-YYYY'), TO_DATE('24-11-2021', 'DD-MM-YYYY'))
FROM DUAL;
这应该能让你入门,因为我不明白你的要求。
with time_between as (
select trunc ( timestamp'2021-06-14 06:00:00' ) -
trunc ( timestamp'2021-06-08 14:00:00' ) t
from dual
)
select *
from time_between;
T
6
你只是把事情搞得太复杂了... 首先,最好在 ISO 周内工作:从星期一开始。
- https://en.wikipedia.org/wiki/ISO_week_date
- https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions230.htm
- https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/TRUNC-date.html#GUID-BC82227A-2698-4EC8-8C1A-ABECC64B0E79
因此您可以使用 trunc(dt, 'IW')
轻松获得 ISO 周的第一天。例如,11 月 19 日是星期五,我们可以使用 trunc(date'2021-11-19','IW')
:
SQL> select trunc(date'2021-11-19','IW') xx from dual;
XX
-------------------
2021-11-15 00:00:00
所以 2021-11-15 是星期一。
现在假设我们有一些日期:
Mo Tu We Th Fr Sa Su
-- -- -- -- -- -- --
X X X X
X X X X X X X
X X X X X X X
X X X X X X X
X X X X X X X
X X X X X X X
X X X X X X X
X X
我们可以很容易地得到整周的工作日数 (count_full_weeks * 5/7),所以我们排除它们:
Mo Tu We Th Fr Sa Su
-- -- -- -- -- -- --
X X X X
X X
然后,让我们对其进行图形化转换,因为我们知道剩余天数总是小于7天(小于一周),而第一天将是前7天之一:
Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su
-- -- -- -- -- -- -- -- -- -- -- -- -- --
X X X X X X
让我们用他们的数字替换 Mo,Tu,We...:[=24=]
0 1 2 3 4 5 6 0 1 2 3 4 5 6
-- -- -- -- -- -- -- -- -- -- -- -- -- --
X X X X X X
因为我们知道这部分周的天数不能为 7,所以我们知道最晚结束时间为:
0 1 2 3 4 5 6 0 1 2 3 4 5 6
-- -- -- -- -- -- -- -- -- -- -- -- -- --
X X X X X X
现在让我们将第二周的数字替换为 7-13:
0 1 2 3 4 5 6 7 8 9 10 11 12 13
-- -- -- -- -- -- -- -- -- -- -- -- -- --
X X X X X X
现在我们可以理解我们只需要检查第 5 天和第 6 天,即检查我们的经期是否包含 5 和 6。
所以会是这样(我已经尽量详细了):
CREATE OR REPLACE FUNCTION number_of_days(start_date IN DATE, end_date IN DATE)
RETURN NUMBER
as
days_between int;
full_weeks int;
remaining_days int;
remaining_workdays int;
v_first_week_day date;
v_start int;
v_end int;
result int;
begin
days_between := end_date - start_date;
full_weeks := trunc(days_between/7);
remaining_days:= mod(days_between,7); -- or days_between-full_weeks*7
v_first_week_day := trunc(start_date,'IW');
v_start := start_date - v_first_week_day;
v_end := v_start + remaining_days - 1;
remaining_workdays := remaining_days
- case when 5 between v_start and v_end then 1 else 0 end
- case when 6 between v_start and v_end then 1 else 0 end
;
result := full_weeks * 5 + remaining_workdays;
RETURN result;
end;
/
我做了很多子步骤和中间计算和变量,只是为了让它更清楚。显然,您可以使其更短,例如:
CREATE OR REPLACE FUNCTION number_of_days(start_date IN DATE, end_date IN DATE)
RETURN NUMBER
as
begin
return 5*trunc((end_date-start_date)/7) + mod((end_date-start_date),7)
- case when 5-(start_date - trunc(start_date,'IW')) between 0 and mod((end_date-start_date),7)-1 then 1 else 0 end
- case when 6-(start_date - trunc(start_date,'IW')) between 0 and mod((end_date-start_date),7)-1 then 1 else 0 end
;
end;
/