Last active
November 10, 2021 15:00
-
-
Save sentenza/e8f8003e2581d52f12f8c89d663c00d7 to your computer and use it in GitHub Desktop.
Calculate date of easter based on Year passed. Based on Anonymous Gregorian Algorithm, also known as the Meeus/Jones/Butcher algorithm: https://en.wikipedia.org/wiki/Computus#Gauss_algorithm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CREATE OR REPLACE FUNCTION anonymous_easter(xyear integer) RETURNS date AS $$ | |
DECLARE | |
a int; | |
b int; | |
c int; | |
d int; | |
e int; | |
f int; | |
g int; | |
h int; | |
i int; | |
k int; | |
l int; | |
m int; | |
day_ int; | |
month_ int; | |
BEGIN | |
a := mod(xyear, 19); | |
b := floor(xyear / 100); | |
c := mod(xyear, 100); | |
d := floor(b / 4); | |
e := mod (b, 4); | |
f := floor((b + 8) / 25); | |
g := floor((b - f + 1)/3); | |
h := mod ((19*a + b - d - g + 15), 30); | |
i := floor(c / 4); | |
k := mod (c, 4); | |
l := mod((32 + 2*e + 2*i - h - k), 7); | |
m := floor((a + 11*h + 22*l) / 451); | |
month_ := floor((h + l - 7*m + 114) / 31); | |
day_ = mod((h + l - 7*m + 114), 31) + 1; | |
RETURN to_date( | |
to_char(day_, '00') || '.' || | |
to_char(month_, '00') || '.' || | |
to_char(xyear, '0000'), | |
'DD.MM.YYYY' | |
); | |
END; | |
$$ LANGUAGE plpgsql; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CREATE OR REPLACE FUNCTION generate_italian_holidays("start_year" integer, "end_date" integer) RETURNS void | |
DECLARE | |
anno int; | |
easter_date date; | |
BEGIN | |
CREATE TABLE holidays_calendar ( | |
holiday date NOT NULL, | |
description character varying NOT NULL, | |
id integer NOT NULL | |
); | |
FOR anno IN start_year..end_date | |
LOOP | |
easter_date := planner.anonymous_easter(anno); | |
INSERT INTO planner.holidays_calendar(holiday, description) VALUES (to_date( anno ||'-01-01', 'YYYY-MM-DD'), 'Capodanno'); | |
INSERT INTO planner.holidays_calendar(holiday, description) VALUES (to_date( anno ||'-01-06', 'YYYY-MM-DD'), 'Epifania'); | |
IF (easter_date = to_date( anno ||'-04-25', 'YYYY-MM-DD')) THEN | |
INSERT INTO planner.holidays_calendar(holiday, description) VALUES (easter_date, 'Pasqua e Festa della Liberazione'); | |
INSERT INTO planner.holidays_calendar(holiday, description) VALUES (easter_date + 1, 'Pasquetta'); | |
ELSIF (easter_date + 1 = to_date( anno ||'-04-25', 'YYYY-MM-DD')) THEN | |
INSERT INTO planner.holidays_calendar(holiday, description) VALUES (easter_date, 'Pasqua'); | |
INSERT INTO planner.holidays_calendar(holiday, description) VALUES (easter_date + 1, 'Pasquetta e Festa della Liberazione'); | |
ELSE | |
INSERT INTO planner.holidays_calendar(holiday, description) VALUES (easter_date, 'Pasqua'); | |
INSERT INTO planner.holidays_calendar(holiday, description) VALUES (easter_date + 1, 'Pasquetta'); | |
INSERT INTO planner.holidays_calendar(holiday, description) VALUES (to_date( anno ||'-04-25', 'YYYY-MM-DD'), 'Festa della Liberazione'); | |
END IF; | |
INSERT INTO planner.holidays_calendar(holiday, description) VALUES (to_date( anno ||'-05-01', 'YYYY-MM-DD'), 'Festa dei lavoratori'); | |
INSERT INTO planner.holidays_calendar(holiday, description) VALUES (to_date( anno ||'-06-02', 'YYYY-MM-DD'), 'Festa della Repubblica'); | |
INSERT INTO planner.holidays_calendar(holiday, description) VALUES (to_date( anno ||'-08-15', 'YYYY-MM-DD'), 'Ferragosto'); | |
INSERT INTO planner.holidays_calendar(holiday, description) VALUES (to_date( anno ||'-11-01', 'YYYY-MM-DD'), 'Ognissanti'); | |
INSERT INTO planner.holidays_calendar(holiday, description) VALUES (to_date( anno ||'-12-08', 'YYYY-MM-DD'), 'Immacolata'); | |
INSERT INTO planner.holidays_calendar(holiday, description) VALUES (to_date( anno ||'-12-25', 'YYYY-MM-DD'), 'Natale'); | |
INSERT INTO planner.holidays_calendar(holiday, description) VALUES (to_date( anno ||'-12-26', 'YYYY-MM-DD'), 'Santo Stefano'); | |
END LOOP; | |
END; | |
$$ LANGUAGE plpgsql; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Function: is_business_day(date) | |
-- DROP FUNCTION is_business_day(date); | |
CREATE OR REPLACE FUNCTION is_business_day(p_date date) | |
RETURNS boolean AS | |
$BODY$DECLARE | |
is_business_day boolean; | |
dow int; | |
BEGIN | |
dow := extract('dow' from p_date); | |
IF dow = 6 OR dow = 0 OR EXISTS (SELECT 1 FROM holidays_calendar WHERE holiday = p_date) | |
THEN | |
is_business_day := FALSE; | |
ELSE | |
is_business_day := TRUE; | |
END IF; | |
RETURN is_business_day ; | |
END;$BODY$ | |
LANGUAGE plpgsql IMMUTABLE | |
COST 100; | |
ALTER FUNCTION planner.is_business_day(date) | |
OWNER TO support; | |
COMMENT ON FUNCTION planner.is_business_day(date) IS 'Returns true if day is IN(1,5) OR if is IN holidays_calendar'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Function: planner.working_date(date, integer) | |
-- DROP FUNCTION planner.working_date(date, integer); | |
CREATE OR REPLACE FUNCTION planner.working_date(start_date date, duration integer) | |
RETURNS date AS | |
$BODY$DECLARE | |
ret_date date; | |
loop_date date; | |
BEGIN | |
--add days | |
ret_date := start_date + (duration - 1); | |
loop_date := start_date + 1; | |
--add extra day for each no business day between start_date and ret_date | |
WHILE loop_date <= ret_date LOOP | |
IF not planner.is_business_day(loop_date) THEN | |
ret_date := ret_date + 1; | |
END IF; | |
loop_date := loop_date + 1; | |
END LOOP; | |
--add day if ret_date is no business day | |
WHILE not planner.is_business_day(ret_date) LOOP | |
ret_date := ret_date + 1; | |
END LOOP; | |
RETURN ret_date; | |
END;$BODY$ | |
LANGUAGE plpgsql VOLATILE SECURITY DEFINER | |
COST 100; | |
ALTER FUNCTION planner.working_date(date, integer) | |
OWNER TO support; | |
COMMENT ON FUNCTION planner.working_date(date, integer) IS 'Returns the corresponding end date based on a duration made only of working days'; |
In order to retrieve the result using Symfony and Doctrine you could use something like that:
$em = $this->getDoctrine()->getManager();
$query = $em->getConnection()->prepare('SELECT planner.working_date(:start_date::date, :duration::integer)');
$query->bindValue(':start_date', '04/01/2017' );
$query->bindValue(':duration', 100);
$query->execute();
$end_date = $query->fetchAll();
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
To test the function over a time range you could use proficiently
generate_series()
: