Tuesday, December 10, 2019

You are not logged in.

## #1 2006-08-06 08:35:12 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 5118

### Calculating Easter

While working on a script to tell me the date of the next fortnightly recycling collection in my area, I've had cause to research the calculation of Easter Day. (Easter Monday's a bank holiday, so any collections that week will be a day later.) It's quite a complex process, based on full moons and Vernal Equinoxes. One of the calculations I found on the Web actually works and looks like this when translated into AppleScript:

#### Applescript:

on EasterDay(y) -- y is the year number.
-- Based on a calculation on the BBC's h2g2 Web site:
-- <[url]http://www.bbc.co.uk/dna/h2g2/A653267[/url]>

set a to y mod 19
set b to y div 100
set c to y mod 100
set d to b div 4
set e to b mod 4
set f to c div 4
set g to c mod 4

set h to (b + 8) div 25
set i to (b - h + 1) div 3
set j to (19 * a + b - d - i + 15) mod 30
set k to (32 + 2 * e + 2 * f - j - g) mod 7
set m to (a + 11 * j + 22 * k) div 451
set n to j + k - 7 * m + 114

set theDay to n mod 31 + 1
set theMonth to n div 31

return {theDay, theMonth}
end EasterDay

EasterDay(2006)
--> {16, 4} -- Sunday 16th April

This is apparently only guaranteed for years between 1700 and 2299! For my own purposes, I want the result as an AppleScript date.

#### Applescript:

on EasterDay(y)
-- Based on a calculation on the BBC's h2g2 Web site:
-- <[url]http://www.bbc.co.uk/dna/h2g2/A653267[/url]>

set {a, b, c} to {y mod 19, y div 100, y mod 100}

set j to (19 * a + b - b div 4 - (b - (b + 8) div 25 + 1) div 3 + 15) mod 30
set k to (32 + 2 * (b mod 4 + c div 4) - j - c mod 4) mod 7
set n to j + k - (a + 11 * j + 22 * k) div 451 * 7 + 114

-- Get 1st January in the target year.
set theDate to date "Wednesday 1 January 1000 00:00:00"
set theDate's year to y

tell theDate + (n div 31 - 1) * 32 * days -- Get a date in the (n div 31)th month of the year.
return it + (n mod 31 + 1 - (its day)) * days -- Return the result + (target day - actual day) days.
end tell
end EasterDay

EasterDay(2006)
--> date "Sunday 16 April 2006 00:00:00"

NG

Offline

## #2 2006-10-28 02:01:24 pm

StefanK
Member
From:: St. Gallen, Switzerland
Registered: 2006-10-21
Posts: 11627
Website

### Re: Calculating Easter

This is a different approach, found here: http://www.assa.org.au/edm.html
The dates are valid from 1583 to 4099 and independent of international system date settings.
For further determination of holidays Iâ€™ve added a few lines to calculate the days of february

#### Applescript:

set {EasterDate, DaysOfFeb} to calc_easter_date(2006)

on calc_easter_date(yr)
set FirstD to yr div 100
set temp to (FirstD - 15) div 2 + 202 - 11 * (yr mod 19)

if FirstD is in {21, 24, 25, 27, 28, 29, 30, 31, 32, 34, 35, 38} then set temp to temp - 1
if FirstD is in {33, 36, 37, 39, 40} then set temp to temp - 2
set temp to temp mod 30

set VA to temp + 21
if temp = 29 or (temp = 28 and (yr mod 19) > 10) then set VA to VA - 1

-- find the next Sunday
set VB to (VA - 19) mod 7
set VC to (40 - FirstD) mod 4
if VC = 3 then set VC to VC + 1
if VC > 1 then set VC to VC + 1
set temp to yr mod 100
set VD to (temp + temp div 4) mod 7
set VE to ((20 - VB - VC - VD) mod 7) + 1
set easter_day to VA + VE

-- return the date
if easter_day > 31 then
set easter_day to easter_day - 31
set easter_month to 4
else
set easter_month to 3
end if
-- calculate days of february
set DayFeb to 28
if ((yr - (yr div 4) * 4) = 0) and (yr - ((yr div 100) * 100) â‰  0) then set DayFeb to 29
if (yr - (yr div 100) * 100) = 0 and yr - (yr div 400) * 400 â‰  0 then set DayFeb to 28
if (yr - (yr div 400) * 400) = 0 then set DayFeb to 29
-- make the date independent of international system date settings
set ED to date (short date string of (current date))
set year of ED to yr
set month of ED to easter_month
set day of ED to easter_day
return {ED, DayFeb}
end calc_easter_date

Model: G5 dual 2,5 GHz
Browser: Safari 419.3
Operating System: Mac OS X (10.4)

Last edited by StefanK (2006-10-30 04:45:44 am)

regards

Stefan

Offline

## #3 2006-10-30 02:16:22 am

Qwerty Denzel
Member
Registered: 2005-06-11
Posts: 337

### Re: Calculating Easter

Hi StefanK.

In your translation of their algorithm, it seems you have made an error!

You have written:

#### Applescript:

if temp = 29 or (temp = 28 and (yr mod 19) > 10) then VA = VA - 1

That last bit should be:

#### Applescript:

then set VA to VA - 1

Out of interest, off that website I found Modified Oudin's Algorithm, which I haven't tried simplifying (but have verified that it works):

#### Applescript:

on modifiedOudinsAlgorithm(theYear)
(* Modified Oudin's Algorithm *)
(* [url]http://www.smart.net/~mmontes/oudin.html[/url] *)
set century to theYear div 100
set G to theYear mod 19
set K to (century - 17) div 25
set I to (century - century div 4 - (century - K) div 3 + 19 * G + 15) mod 30
set I to I - (I div 28) * (1 - (I div 28) * (29 div (I + 1)) * ((21 - G) div 11))
set J to (theYear + theYear div 4 + I + 2 - century + century div 4) mod 7
set L to I - J
set EasterMonth to 3 + (L + 40) div 44
set EasterDay to L + 28 - 31 * (EasterMonth div 4)

set EasterDate to current date
tell EasterDate to set {day, its month, year, time} to {EasterDay, EasterMonth, theYear, 0}
return EasterDate
end modifiedOudinsAlgorithm

I also found Butcher's Algorithm, which also gets the same result as yours:

#### Applescript:

on butchersAlgorithm(theYear)
(* Butcher's Algorithm *)
(* [url]http://www.smart.net/~mmontes/nature1876.html[/url] *)
set a to theYear mod 19
set b to theYear div 100
set c to theYear mod 100
set d to b div 4
set e to b mod 4
set f to (b + 8) div 25
set G to (b - f + 1) div 3
set h to (19 * a + b - d - G + 15) mod 30
set I to c div 4
set K to c mod 4
set L to (32 + 2 * e + 2 * I - h - K) mod 7
set m to (a + 11 * h + 22 * L) div 451
set EasterMonth to (h + L - 7 * m + 114) div 31
set p to (h + L - 7 * m + 114) mod 31
set EasterDay to p + 1

set EasterDate to current date
tell EasterDate to set {day, its month, year, time} to {EasterDay, EasterMonth, theYear, 0}
return EasterDate
end butchersAlgorithm

Also, to find how many days in February for a particular year:

#### Applescript:

on daysInFeb(theYear)
tell theYear mod 400 to return 28 + (((it mod 28 mod 4 is 0) and ((it mod 100 is not 0) or (it is 0))) as integer)
end daysInFeb

Edit
Look, there are reasons why you don't attempt to condense these into a one-liner form:

#### Applescript:

on butchersAlgorithmButchered(theYear)
tell {theYear mod 19, theYear div 100} to tell {item 1, item 2, theYear mod 100, (19 * (item 1) + (item 2) - (item 2) div 4 - (((item 2) - (((item 2) + 8) div 25) + 1) div 3) + 15) mod 30} to tell {item 1, item 4, (32 + 2 * ((item 2) mod 4) + 2 * ((item 3) div 4) - (item 4) - ((item 3) mod 4)) mod 7} to tell ((item 2) + (item 3) - 7 * (((item 1) + 11 * (item 2) + 22 * (item 3)) div 451) + 114) to set {EasterMonth, EasterDay} to {it div 31, it mod 31 + 1}

set EasterDate to current date
tell EasterDate to set {day, its month, year, time} to {EasterDay, EasterMonth, theYear, 0}
return EasterDate
end butchersAlgorithmButchered

Last edited by Qwerty Denzel (2006-10-30 05:07:28 am)

Offline

## #4 2006-10-30 03:04:06 am

StefanK
Member
From:: St. Gallen, Switzerland
Registered: 2006-10-21
Posts: 11627
Website

### Re: Calculating Easter

Hi, Qwerty

thanks a lot for your hint. Iâ€™ve changed the line.

Your handler to calculate leap years doesnâ€™t work correctly for century years.
The rule is:

A year will be a leap year if it is divisible by 4 but not by 100.
If a year is divisible by 4 and by 100, it is not a leap year unless it is also divisible by 400.

therefore e.g. 1900 is not a leap year but 2000 is.

Last edited by StefanK (2006-10-30 03:05:50 am)

regards

Stefan

Offline

## #5 2006-10-30 05:07:48 am

Qwerty Denzel
Member
Registered: 2005-06-11
Posts: 337

### Re: Calculating Easter

You are of course correct.

Hmm... I honestly remembered as being the other way around. Makes much more sense though.
Consider it changed.

Offline

## #6 2006-10-30 12:19:56 pm

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 5118

### Re: Calculating Easter

Qwerty Denzel wrote:

Consider it changed.

Consider it optimised.

#### Applescript:

on daysInFeb(theYear)
if ((theYear mod 4 is 0) and ((theYear mod 100 > 0) or (theYear mod 400 is 0))) then
29
else
28
end if
end daysInFeb

NG

Offline

## #7 2006-10-30 01:17:23 pm

StefanK
Member
From:: St. Gallen, Switzerland
Registered: 2006-10-21
Posts: 11627
Website

### Re: Calculating Easter

Nigel Garvey wrote:

Consider it optimised.

almost

#### Applescript:

on daysInFeb(theYear)
(((theYear mod 4 is 0) and ((theYear mod 100 > 0) or (theYear mod 400 is 0))) as integer) + 28
end daysInFeb

regards

Stefan

Offline

## #8 2006-10-30 02:28:55 pm

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 5118

### Re: Calculating Easter

Hi, Stefan.

By "optimised", I meant "made more efficient", not just "made shorter". I deliberately left out the boolean-to-integer coercion and the addition, which makes my version about 2.5 to 3.7 times as fast (on my machine, depending on the actual year number) as a one-liner.

NG

Offline

## #9 2006-10-30 02:39:48 pm

StefanK
Member
From:: St. Gallen, Switzerland
Registered: 2006-10-21
Posts: 11627
Website

### Re: Calculating Easter

Hi Nigel,

Iâ€™m convinced

BTW: How can you quantify these tiny durations?
Script Debugger shows in both cases 0,0 secs

regards

Stefan

Offline

## #10 2006-10-31 01:45:58 am

Qwerty Denzel
Member
Registered: 2005-06-11
Posts: 337

### Re: Calculating Easter

Me too.

This is how I might go about measuring the speed difference:

#### Applescript:

on daysInFeb1(theYear)
if ((theYear mod 4 is 0) and ((theYear mod 100 > 0) or (theYear mod 400 is 0))) then
29
else
28
end if
end daysInFeb1

on daysInFeb2(theYear)
(((theYear mod 4 is 0) and ((theYear mod 100 > 0) or (theYear mod 400 is 0))) as integer) + 28
end daysInFeb2

set repeatTimes to 1000

set startDate1 to current date
repeat repeatTimes times
repeat with i from 1 to 2099
daysInFeb1(i)
end repeat
end repeat
set time1 to (current date) - startDate1

set startDate2 to current date
repeat repeatTimes times
repeat with i from 1 to 2099
daysInFeb2(i)
end repeat
end repeat
set time2 to (current date) - startDate2

time2 / time1 -- times faster

However, I have heard that AppleScript can be quite fragile when doing timings, and results can change depending on the order and the number of times you run it. I certainly get small discrepancies in the order of about 5 to 10 percent, but in general it's usually quite clear as to which one is faster.

Offline

## #11 2006-10-31 08:45:52 am

StefanK
Member
From:: St. Gallen, Switzerland
Registered: 2006-10-21
Posts: 11627
Website

thanx,

such a doddle

regards

Stefan

Offline

## #12 2006-11-02 11:31:32 am

Registered: 2005-10-04
Posts: 4666

### Re: Calculating Easter

QD's test results in 2.888888888889 on mm (just for the record)

Mac mini running 10.14.6, 2011 27" iMac as display.

Offline

## #13 2006-11-02 06:33:59 pm

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 5118

### Re: Calculating Easter

QD's test results in 2.888888888889 on mm (just for the record)

Similar average result here, timing both with current date and with GetMilliSec, which is a sharper instrument. But the tests aren't actually comparing the timings of 2099000 calls to the two handlers, they're testing 1000 executions of the inner 2099-repeat loops â€“ which include the handler calls. In other words, both test times include 1000 settings-up of the inner repeats + 2099 * 1000 iterations of the inner looping code + 1 setting-up of the outer repeat + 1000 iterations of the outer looping code. Subtracting these from the total times and assuming the repeat instructions take exactly the same time in both cases, we're left with smaller timings but the same difference, which suggests an even better ratio between the handlers themselves. Eschewing the outer repeats and having the inner repeats loop from 1 to 2099000, I see a small (about 1.74%) but definite improvement in the comparative figure.

Running the nested repeats empty (without the handler calls) takes about 1.34 seconds each on my machine. That's added to the timing of the test for each handler when I run Qwerty's script. Running non-nested 1-to-2099000 repeats by themselves takes about 1.09 seconds. Empty, non-nested '2099000 times' repeats take only about 0.6 seconds, so they distort the comparative speeds the least. But in the current case, the 1-to-2099000 repeats are possibly preferable since they give a better average over the various numbers that can be passed to the handlers. This is necessary because the handlers themselves do more or fewer boolean tests depending on the year number they're given, so these tests are a greater or lesser proportion of their total running times, which in turn affects their relative running times.    Repeating '2099000 times' with a year number of 1999 (only the first boolean test is performed in each handler) gives me a relative time of 3.05, whereas with 2000 (all the tests are performed), it's reduced to 2.3.

Moral:

I should get an early night.

NG

Offline

## #14 2013-09-19 06:19:31 am

McUsrII
Member
Registered: 2012-11-21
Posts: 3046
Website

### Re: Calculating Easter

Hello.

The ecclestical easter (which we base our hollidays on), differs from the astronomical easter.

This one returns the date of easter by ecclestical standards.

#### Applescript:

to MonthAndDayOfEaster for aYear
# [url]http://aa.usno.navy.mil/faq/docs/easter.php[/url]
set c to aYear div 100
set n to aYear - 19 * (aYear div 19)
set k to (c - 17) div 25
set i to c - c div 4 - (c - k) div 3 + 19 * n + 15
set i to i - 30 * (i div 30)
set i to i - (i div 28) * (1 - (i div 28) * (29 div (i + 1)) * ((21 - n) div 11))
set j to aYear + aYear div 4 + i + 2 - c + c div 4
set j to j - 7 * (j div 7)
set l to i - j
set m to 3 + (l + 40) div 44
set d to l + 28 - 31 * (m div 4)
return {m, d}
end MonthAndDayOfEaster

Edit

In all fairness: in our particular time span, between 1982 and 2037, the astronomical easter and ecclestical easter do coincide, I just posted this, in case one wants to figure out earlier, and later! easter dates, for other purposes than calculating lunar phases.

Last edited by McUsrII (2013-09-19 06:30:31 am)

Filed under: Easter

Offline

## #15 2013-09-19 08:30:58 am

CMYS
Member
From:: Belgium
Registered: 2007-07-02
Posts: 86

### Re: Calculating Easter

#### Applescript:

display dialog "Easter : " & (do shell script "LC_TIME=\"fr_BE.UTF-8\" ncal -e \"2014\"")

Offline

## #16 2013-09-19 09:14:26 am

McUsrII
Member
Registered: 2012-11-21
Posts: 3046
Website

### Re: Calculating Easter

Hello.

Thanks for sharing!  I read that ncal even has  an option for calculating eastor for  orthodox/Eastern churches, (-o) It's impressive!

Last edited by McUsrII (2013-09-20 09:47:45 pm)

Offline