Calculating Orthodox Easter
Defining Orthodox Easter
Julian Easter
One often sees a marking on calendars for "Orthodox Easter", but how does one calculate that. First, there was a pre-Gregorian Julian algorithm for calculating Easter. The Python code for this is:
def Easter_Julian(year):
silver=year%19
pfm=21+(19*silver+15)%30
dow=(pfm+year+year//4)%7
# If concerned with overflow replace first year with year%7 in dow calculation.
# This is not necessary in the Python version, but may be a concern in other
# languages in to which this function may be translated.
easter=pfm+7-dow
if easter>31:
return (4,easter-31)
else:
return (3,easter)
The algorithm gives the date of Julian Easter in the Julian calendar; just as the algorithm on my page A Simple Easter Program gives the date of Gregorian Easter in the Gregorian calendar. Note that this algorithm has all of the same advantages discussed in A Simple Easter Program:
- For all non-negative year inputs, it never produces a negative intermediate value.
- It calculates the Paschal Full Moon.
- The algorithm is about as simple as it can be.
- The algorithm "correctly" continues the pattern of the 532 year repeat cycle of the Julian Easter algorithm all the way down to an input of 0(inclusive).
- If the integer division and modulus functions are defined properly (as they are in Python, but not in C), then the the 532 year repeat cycle will persist even in to the negative numbers.
Orthodox Easter
Virtually all Eastern Orthodox Christians observe on the date in the Julian calendar that is given by the Julian algorithm for Easter. However, virtually all Eastern Orthodox Christians use the Gregorian calendar for normal civil purposes; some also it to determine their fixed religious observances as well. (Some claim to be following a "Revised Julian" calendar, but refer to my comments on the "Revised Julian" calendar in Julian Day Algorithms for Non-standard Calendars for why that claim can be ignored; short version: the "Revised Julian" calendar is the same as the Gregorian from 1601 to 2799.) So what is called Orthodox Easter is just Easter calculated by the Julian algorithm on the Julian calendar expressed as a Gregorian date. Eg, in 2016 the above algorithm gives the date of Easter as April 18; Julian 2016 April 18 is Gregorian 2016 May 01, and that is the date given for Orthodox Easter.
Special Considerations For a Orthodox Easter Algorithm
The problem with writing a simple function to calculate the date of Orthodox Easter is that the possible dates of Orthodox Easter keep moving forward in the year. The date of the Julian Easter in the Julian calendar is limited to the range of March 22 to April 25 (inclusive). From 1900 to 2099 (inclusive); the date March 22 Julian is April 04 Gregorian, and April 25 Julian is May 08 Gregorian; this means that in the range 1900 to 2099, the range of possible dates for Orthodox Easter is April 04 to May 08. In 2100 the Gregorian calendar omits another leap day, causing the range to shift to April 05 to May 09. In every century that omits a leap day, this move forward occurs. Eventually, in 33500-33699, the range runs from November 27 to December 31, but the slide forward does not end there: in 33700 in slips forward once more meaning that Julian 33700 April 25 is 33701 January 01 Gregorian. Now this doesn't immediately mean that the Julian Easter of 33700 will fall in Gregorian 33701, indeed it does not; it is not until the Julian Easter of 33808 that Orthodox Easter falls in the next year. Then it wanders back and forth, some years having one Easter, some two, and some none; until finally Orthodox Easter occurs consistently occurs in the year numbered after the Julian Easter it corresponds to. Then, eventually far enough in the future, it occurs two years late, etc.
This forward drift means that one can not assume that "n'th" Orthodox Easter is in the Gregorian year n indefinitely. One can not, as I do with the Gregorian and Julian Easters, simply return a month and a day; one has to return a full date. This is also why instead of calling my input variable to the following Orthodox Easter function year, I call it num as in the serial number of the Easter calculated. The simplest way to calculate the date of Orthodox Easter in the general case is to calculate the date of Julian Easter in the Julian calendar, then convert the date to the Gregorian calendar. If one limits oneself to the non-negative years for Easter (refer to note in other document called note on year 0 for comments on a year zero) or one is using a language that guarantees Python-like behavior from the integer division and modulus operators, then one can use the first algorithms on the my page Julian Day Number Algorithms.
The Algorithm
So the full algorithm for Orthodox Easter looks like this:
#!usr/bin/python3
from enum import Enum
class Caltype(Enum):
greg=1
jul=2
class Date(object):
def __init__(self, year, month, day):
self.year=year
self.month=month
self.day=day
return
def __repr__(self):
return ("Date("+str(self.year)+","+str(self.month)+","+str(self.day)+")")
def cal2jd(date,caltype=Caltype.greg):
year=date.year
month=date.month
day=date.day
if (month<3):
year=year-1
month=month+12
jd=365*year+year//4
jd=jd+(153*(month+1))//5+day-123
if caltype==Caltype.greg:
jd=jd-year//100+year//400
jd=jd+1721120
else:
jd=jd+1721118
return jd
def jd2cal(jd,caltype=Caltype.greg):
if caltype==Caltype.greg:
day=jd-1721120
a=(4*day+3)//146097
day=day+a-a//4
else:
day=jd-1721118
year=(4*day+3)//1461
day=day-(1461*year)//4
month=(5*day+2)//153
day=day-(153*month+2)//5
day=day+1
month=month+3
if (month>12):
month=month-12
year=year+1
return Date(year,month,day)
def Easter_Julian(year):
silver=year%19
pfm=21+(19*silver+15)%30
dow=(pfm+year+year//4)%7
easter=pfm+7-dow
if easter>31:
return (4,easter-31)
else:
return (3,easter)
def Easter_Ortho(num):
(month,day)=Easter_Julian(num)
return jd2cal(cal2jd(Date(num,month,day),Caltype.jul),Caltype.greg)
Simpler, But Limited Range, Algorithms
But supposing that one wants to calculate Orthodox Easter without having to have the full calendar conversion functions; if one is willing to limit oneself to a limited range of dates, one can do this. The next function is limited to years in the range 0 to 33807 (inclusive):
def Easter_Ortho(year):
if year<0 or year>33807:
raise ValueError("This function only valid between 0 and 33807")
silver=year%19
pfm=21+(19*silver+15)%30
dow=(pfm+year+year//4)%7
easter=pfm+7-dow
easter=easter-2+year//100-year//400
month=3+(5*easter-3)//153
day=easter-(153*month-457)//5
return (month,day)
And this one to 0 to 5174 (inclusive):
def Easter_Ortho(year):
if year<0 or year>5174:
raise ValueError("This function only valid between 0 and 5174")
silver=year%19
pfm=21+(19*silver+15)%30
dow=(pfm+year+year//4)%7
easter=pfm+7-dow
easter=easter-2+year//100-year//400
if easter>61:
return (5,easter-61)
elif easter>31:
return (4,easter-31)
else:
return (3,easter)
Actually if one is using a language that guarantees Python-like behavior from the integer division and modulus operators, these functions are valid down to -2682 (inclusive).
Alternate Orthodox Easter Algorithm
A few churches use a basically Eastern Orthodox way of calculating Easter, but with a slight variation: when the Golden Number is 1 (thus silver=0), use Paschal Moons one day later than the standard Julian Easter algorithm. So the Pashcal New Moon, instead of falling on March 23, falls on March 24; and the Paschal Full Moon, instead of falling on April 05, falls on April 06 (source). This, thankfully, proves to be easy to correct for in my algorithms: one simply replaces the line pfm=21+(19*silver+15)%30 with:
if silver!=0:
pfm=21+(19*silver+15)%30
else:
pfm=37
Second, one can replace the given line with pfm=21+(19*((year+18)%19)+4)%30 . This code also works for an input year of 0. It saves a compare by trading it off for an extra addition in the typical case.
Third, if one is using a language that guarantees Python-like behavior from the integer division and modulus operators or one doesn't intend to feed the number 0 as an input year of the function, one can make this replacement: pfm=21+(19*((year-1)%19)+4)%30 .
Note that for the second or third possible changes, one can also remove the line silver=year%19 , but this is not necessary.
Any one of these three replacements will work in any of the programs on this page to convert from the standard Julian and Orthodox Easter calculations to the alternate Julian and Orthodox calculations.
The Modified Functions
The modified functions:
def Easter_Julian_alt(year):
silver=year%19
if silver!=0:
pfm=21+(19*silver+15)%30
else:
pfm=37
dow=(pfm+year+year//4)%7
easter=pfm+7-dow
if easter>31:
return (4,easter-31)
else:
return (3,easter)
def Easter_Ortho_alt(num):
(month,day)=Easter_Julian_alt(num)
return jd2cal(cal2jd(Date(num,month,day),Caltype.jul),Caltype.greg)
Or for the limited range algorithms:
def Easter_Ortho_alt(year):
if year<0 or year>33807:
raise ValueError("This function only valid between 0 and 33807")
silver=year%19
if silver!=0:
pfm=21+(19*silver+15)%30
else:
pfm=37
dow=(pfm+year+year//4)%7
easter=pfm+7-dow
easter=easter-2+year//100-year//400
month=3+(5*easter-3)//153
day=easter-(153*month-457)//5
return (month,day)
or
def Easter_Ortho_alt(year):
if year<0 or year>5174:
raise ValueError("This function only valid between 0 and 5174")
silver=year%19
if silver!=0:
pfm=21+(19*silver+15)%30
else:
pfm=37
dow=(pfm+year+year//4)%7
easter=pfm+7-dow
easter=easter-2+year//100-year//400
if easter>61:
return (5,easter-61)
elif easter>31:
return (4,easter-31)
else:
return (3,easter)
Effects of the Change in Algorithm
While the date of the Paschal Moons is affected in every year divisible by 19 -- 28 times in 532 years --, the date of the alternate Julian Easter is affected only 4 times in the 532 year repeat cycle of Julian Easters -- when the year modulo 532 is 38, 133, 228, or 475 -- because not only must the Paschal Full Moon be advanced one day compared to the standard Julian algorithm, but that change must be from a Saturday to a Sunday or the date of Easter will not be affected because date of the Sunday after the Paschal Full Moon will only change in that case. When the date of Julian Easter in the Julian calendar is changed, the effect is to change it from April 06 to April 13; this does not affect the range of possible Easter dates which remains from March 22 to April 25 in the Julian calendar.
This effect last occurred in 1824, and will next occur in 2071. Note that Orthodox Easter as defined as the date of Julian Easter in the Julian calendar expressed as a Gregorian date also moves forward by one day during the years in which the alternate algorithm results in a different day, but because of the changing offset between the Julian and Gregorian calendars the dates change each time; eg, in 1824, Orthodox Easter by the standard algorithm occurred on Gregorian 1824 April 18, and by the alternate algorithm on Gregorian 1824 April 25; but in 2071, Orthodox Easter by the standard algorithm occurred on Gregorian 2071 April 19, and by the alternate algorithm on Gregorian 2071 April 26; because the difference between the Julian and Gregorian calendars had increased by one in 1900.