'Find Good Friday via Oracle stored procedure function
I have this requirement to determine dates and one of them I need to filter out Good Friday (only) with a parameter of given year
v_temp_year varchar2(4) := to_char(sysdate, 'YYYY');
//the function would be something below that returns date
getGoodFridayDate(v_temp_year)
Now I need to formulate a function to be called in each stored procedure and I feel like a total noob about it. Any help would be greatly appreciated. Please don't tag as duplicate I cannot find solutions here with same requirement .
Solution 1:[1]
create or replace
function good_friday(p_year number) return date is
l_day pls_integer := 0;
l_month pls_integer := 3;
l_lunar pls_integer := mod(p_year,19);
l_cent pls_integer := p_year / 100;
l_equi pls_integer := mod((l_cent - floor(l_cent / 4) - floor((8 * l_cent + 13) / 25) + 19 * l_lunar + 15),30);
l_full pls_integer := l_equi - floor(l_equi / 28) * (1 - floor(l_equi / 28) * floor(29 / (l_equi + 1)) * floor((21 - l_lunar) / 11));
begin
l_day := l_full - mod((p_year + floor(p_year / 4) + l_full + 2 - l_cent + floor(l_cent / 4)),7) + 28;
if l_day > 31 then
l_month := 4;
l_day := l_day - 31;
end if;
return to_date(p_year||'-'||l_month||'-'||l_day,'YYYY-MM-DD')-2;
end;
/
SQL> select good_friday(2022) from dual;
GOOD_FRID
---------
15-APR-22
SQL> select good_friday(2011) from dual;
GOOD_FRID
---------
22-APR-11
Solution 2:[2]
My function looks slightly different:
CREATE OR REPLACE FUNCTION Easter(yyyy IN INTEGER DEFAULT TO_CHAR(SYSDATE, 'YYYY')) RETURN DATE IS
a INTEGER;
b INTEGER;
c INTEGER;
d INTEGER;
e INTEGER;
f INTEGER;
g INTEGER;
h INTEGER;
i INTEGER;
k INTEGER;
l INTEGER;
m INTEGER;
n INTEGER;
p INTEGER;
EM INTEGER;
ED INTEGER;
BEGIN
a := yyyy MOD 19;
b := TRUNC(yyyy / 100);
c := yyyy MOD 100;
d := TRUNC(b / 4);
e := b MOD 4;
f := TRUNC((b + 8) / 25);
g := TRUNC((b - f + 1) / 3);
h := (19*a + b - d - g + 15) MOD 30;
i := TRUNC(c / 4);
k := c MOD 4;
l := (32 + 2*e + 2*i - h - k) MOD 7;
m := TRUNC((a + 11*h + 22*l) / 451);
n := TRUNC((h + l - 7*m + 114) / 31);
p := (h + l - 7*m + 114) MOD 31;
EM := n;
ed := p + 1;
RETURN TO_DATE(yyyy||'-'||EM||'-'||ed, 'YYYY-MM-DD');
END Easter;
Note, it returns Easter Sunday, so for Good Friday you need to subtract 2 days from result.
You may pick your favorite Algorithm and modify the function accordingly.
Comparing my algorithm with algorithm used by @Connor McDonald gives different result for years earlier than 1899 and later than 2049:
WITH t AS
(SELECT 1850+LEVEL AS YEAR, good_friday(1850+LEVEL) AS good_friday_1, easter(1850+LEVEL)-2 good_friday_2
FROM dual
CONNECT BY LEVEL < 300)
SELECT *
FROM t
WHERE good_friday_1 <> good_friday_2;
+--------------------------------+
|YEAR|GOOD_FRIDAY_1|GOOD_FRIDAY_2|
+--------------------------------+
...
|1894|24.03.1894 |23.03.1894 |
|1895|13.04.1895 |12.04.1895 |
|1896|04.04.1896 |03.04.1896 |
|1897|17.04.1897 |16.04.1897 |
|1898|09.04.1898 |08.04.1898 |
|1899|01.04.1899 |31.03.1899 |
|2050|09.04.2050 |08.04.2050 |
|2051|01.04.2051 |31.03.2051 |
|2052|13.04.2052 |19.04.2052 |
|2053|05.04.2053 |04.04.2053 |
|2054|28.03.2054 |27.03.2054 |
...
+--------------------------------+
Solution 3:[3]
Just to verify this for my own curiosity I created this using the modified formula from New Scientist from here:
function good_friday(p_year integer) return date is
a constant pls_integer := mod(p_year, 19);
b constant pls_integer := floor(p_year / 100);
c constant pls_integer := mod(p_year, 100);
h constant pls_integer := mod(19*a + b - floor(b / 4) - floor((8*b + 13) / 25) + 15, 30);
l constant pls_integer := mod(32 + 2*mod(b, 4) + 2*floor(c / 4) - h - mod(c, 4), 7);
m constant pls_integer := floor((a + 11*h + 19*l) / 433);
n constant pls_integer := floor((h + l - 7*m + 90) / 25);
p constant pls_integer := mod(h + l - 7*m + 33*n + 19, 32);
begin
if p_year is null then
return null;
elsif p_year < 1900 or p_year > 2099 then
raise value_error;
end if;
return to_date(p_year||'-'||n||'-'||p,'YYYY-MM-DD')-2;
end good_friday;
Testing shows this agrees with Domscheit's answer, and always returns a Friday for the valid range of years.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Connor McDonald |
| Solution 2 | |
| Solution 3 |
