There is plenty of information about this on the Web. See for example:
https://en.wikipedia.org/wiki/Computus (Wikipedia)
http://www.phys.uu.nl/~vgent/easter/eastercalculator.htm (R.H. van Gent)
http://www.merlyn.demon.co.uk/estrdate.htm (J.R. Stockton’s base page for date of Easter)
http://www.merlyn.demon.co.uk/estralgs.txt (text file with algorithms)
See also the book Calendrical Calculations: The Millennium Edition by Edward M. Reingold and Nachum Dershowitz (Cambridge University Press, 2001).
This page just gives a few algorithms for finding Easter in the Gregorian calendar, i.e. that which was introduced in October 1582 and is used by Catholic and Protestant churches. (The Greek Orthodox Church still calculates Easter by the Julian calendar.) The algorithms are written in Pascal, but could easily be adapted to other languages.
All the algorithms here have been compared with one another and with Reingold & Dershowitz’s algorithm over the years 1583 to 100,000,000 AD, and found to agree completely.
Most Easter algorithms contain instructions like “Divide n by b and call the remainder r”,
which in a computer program becomes something like
r := n mod b
or r = n % b
.
There can be trouble here when n is negative. In this case the remainder r is still required to be
in the range 0 to b – 1, as it is when n is positive; for instance, –13 divided by 10
should give remainder 7. In some computer languages this does not happen, so that where n might be negative
a workaround is required.
Reingold & Dershowitz make this point on p. 20 of their book. Some such workaround is required in their Easter algorithm on p. 121, where in the expression for shifted-epact the quantity taken mod 30 can be negative (this first happens in the year 3401). See De Morgan’s algorithm below for another example.
Before the Easter algorithms, here is a subroutine for finding the day of the week for any date in the Gregorian calendar. By using the trick noticed by Reingold & Dershowitz (p. 48) we can do without a table for the number of days in each month (There is no validation of the input, but we don’t need that here.)
{------------------------------------------------------------------------------
Get day of week for given date, with convention Sunday = 0, ... Saturday = 6.
}
function DayOfWeek( year, month, day : integer) : integer;
var
y, m, leapDays, daysSinceMarch0 : integer;
begin
if (month < 3) then begin
y := year - 1;
m := month + 9;
end
else begin
y := year;
m := month - 3;
end;
leapDays := (y div 4) - (y div 100) + (y div 400);
daysSinceMarch0 := 30*m + (7*(m+1) div 12) + day;
result := (y + leapDays + daysSinceMarch0 + 2) mod 7;
end;
Now the Easter algorithms. The first one here is due to C.F. Gauss. This is the final version of 1816, as given by Reinhold Bien (“Gauß and beyond: the making of Easter algorithms”, Archive for History of Exact Sciences, 58(5), July 2004, 439–452). In his original version of 1800, Gauss overlooked certain corrections, and thus “caused much confusion during the following 16 years” (Bien). A shorter version of this algorithm, restricted to the years 1800 to 2099, was published in Nature in 1876.
{------------------------------------------------------------------------------
Get Gregorian Easter for given year, using Gauss's algorithm.
Returns result as number of days since March 0.
}
function Easter_Gauss( year : integer) : integer;
var
a, b, c, k, p, q, M, N, d, e : integer;
days : integer; // days since March 0
begin
a := year mod 19;
b := year mod 4;
c := year mod 7;
k := year div 100;
p := (13 + 8*k) div 25;
q := k div 4;
M := (15 - p + k - q) mod 30;
N := (4 + k - q) mod 7;
d := (19*a + M) mod 30;
e := (2*b + 4*c + 6*d + N) mod 7;
days := d + e + 22;
if (e = 6) then begin
if (d = 29) and (days = 57) then
days := 50
else if (d = 28) and (((11*M + 11) mod 30) < 19) and (days = 56) then
days := 49;
end;
result := days;
end;
The next algorithm is due to Augustus De Morgan (who, by the way, spelt his name thus, not “de Morgan” or “DeMorgan”). It is taken from his book A Budget of Paradoxes (2nd edition, ed. David Eugene Smith, 1915, vol. 1, pp. 366–7). Note the precaution needed in calculating the quantity XII as a residue modulo 30.
{-------------------------------------------------------------------------------
Get Gregorian Easter for given year, using De Morgan's algorithm.
Returns result as number of days since March 0.
}
function Easter_De_Morgan( year : integer) : integer;
var
cent, epact, temp : integer;
I, II, III, IV, V, VI, VII, VIII, IX, X, XI, XII, XIII, XIV, XV : integer;
begin
I := year + 1;
II := year div 4;
// "Take 16 from the centurial figures of the given year, if it can be done".
cent := year div 100;
if cent >= 16 then III := cent - 16
else III := 0;
IV := III div 4;
V := I + II - III + IV;
VI := V mod 7;
VII := 7 - VI; // Dominical letter, A = 1, ..., G = 7;
VIII := I mod 19; // Golden number
if (VIII = 0) then VIII := 19;
IX := (cent - 17) div 25;
X := (cent - IX - 15) div 3;
XI := (VIII + 10*(VIII - 1)) mod 30;
//---------------------------------------------------------------
// Here De Morgan's original needs modifying because the quantity
// to be taken mod 30 can be < 0 (first happens in 3165)
// XII := (XI + X + IV - III) mod 30;
temp := XI + X + IV - III;
if (temp >= 0) then XII := temp mod 30
else XII := 29 - ((-temp - 1) mod 30);
//---------------------------------------------------------------
epact := XII;
if (XII = 24) then epact := 25;
if ((XII = 25) and (VIII > 11)) then epact := 26;
if (XII = 0) then epact := 30;
if (epact <= 23) then begin
XIII := 45 - epact;
XIV := (27 - epact) mod 7;
if (XIV = 0) then XIV := 7;
end
else begin
XIII := 75 - epact;
XIV := (57 - epact) mod 7;
if (XIV = 0) then XIV := 7;
end;
XV := XIII + VII - XIV;
if (XIV > VII) then XV := XV + 7;
result := XV;
end;
The next algorithm was first published in Nature, 13, 487 (20 April 1876). It was sent in from New York by an anonymous correspondent. According to Bien (op. cit.) it has often been reproduced. Translated from its original form into Pascal, it is as follows:
{------------------------------------------------------------------------------
Get Gregorian Easter for given year, using New York algorithm (Nature, 1876).
We'll return the result as days since March 0, to be consistent with other algos.
}
function Easter_New_York( year : integer) : integer;
var
a, b, c, d, e, f, g, h, i, k, l, m, n, o : integer;
begin
a := year mod 19;
b := year div 100;
c := year mod 100;
d := b div 4;
e := b mod 4;
f := (b + 8) div 25;
g := (b - f + 1) div 3;
h := (19*a + b - d - g + 15) mod 30;
i := c div 4;
k := c mod 4;
l := (32 + 2*e + 2*i - h - k) mod 7;
m := (a + 11*h + 22*l) div 451;
n := (h + l - 7*m + 114) div 31;
o := (h + l - 7*m + 114) mod 31;
// "n is the number of the month of the year and
// o + 1 is the number of the day of the month on which Easter falls."
if n = 3 then result := o + 1
else result := o + 32;
end;
The following is an interpretation by Michael Behrend of Clavius’s original method, as detailed on Web sites. It relies on the subroutine DayOfWeek (above) to find the day of the week on which the Paschal full moon falls.
{------------------------------------------------------------------------------
Get Gregorian Easter for given year, using my understanding of Clavius
method as found on Web sites.
Returns result as number of days since March 0.
}
function Easter_Clavius_MB( year : integer) : integer;
var
g, ye_raw, c, solar_corr, lunar_corr, ye : integer;
newMoon, dayOfMoon, paschalMoon : integer;
begin
// Golden number of year
g := (year mod 19) + 1;
// Yearly epact before solar and lunar corrections
ye_raw := ((11*(g - 1)) mod 30) + 1;
// Solar and lunar corrections
c := year div 100;
solar_corr := (3*(c - 15)) div 4; // days to be subtracted
lunar_corr := (8*(c - 14)) div 25; // days to be added
// Corrected yearly epact
ye := ye_raw - solar_corr + lunar_corr;
while (ye <= 0) do ye := ye + 30;
while (ye > 30) do ye := ye - 30;
// Kludge by Clavius
if ((ye = 25) and (g >= 12)) then ye := 26;
// Allow for skipping daily epact 25 in short month
if (ye = 24) then ye := 25;
// Change 23 (March 8) to 53, ... 1 (March 30) to 31
if (ye <= 23) then ye := ye + 30;
// Get day of new moon before Paschal full moon (days since March 0)
newMoon := 61 - ye;
// Paschal full moon is 13 days later
paschalMoon := newMoon + 13;
// Finish off just as for R-D algorithm
// Get day of week of Paschal full moon (0 = Sun, ... 6 = Sat)
dayOfMoon := DayOfWeek( year, 3, paschalMoon);
// Add 1..7 days to bring it to Easter Sunday
result := paschalMoon + (7 - dayOfMoon);
end;
We can tidy this up a bit and replace the call to DayOfWeek by inline code. One possibility is as follows:
{------------------------------------------------------------------------------
Tidied-up version of Clavius_MB algorithm.
}
function Easter_MB( year : integer) : integer;
var
c, d, e, f, q, w : integer;
begin
c := year div 100;
d := (3*c - 5) div 4; // (solar correction) + 10, also used for day of week
e := (8*c + 13) div 25; // (lunar correction) + 5
f := year mod 19; // (golden number of year) - 1
// Get q, where q = 53 - (Clavius epact), so that
// q + 21 = date of Paschal full moon in days since March 0.
// Value on left of mod is always >= 0, so no worry there.
q := (227 - 11*f + d - e) mod 30;
if (q = 29) or ((q = 28) and (f >= 11)) then q := q - 1;
// Get day of week of Paschal full moon (0 = Sun, 1 = Mon, ..., 6 = Sat)
w := (year + (year div 4) - d + q) mod 7;
// Get next Sunday strictly after Paschal full moon
result := q + 28 - w;
end;
If we wished to avoid the “if” statement we could replace it with
q := q - ((19*q + f) div 543);
The author of the New York algorithm seems to have done something of this kind.