Tuesday, December 10, 2019

#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! smile

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. hmm 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.  wink

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.  wink


almost wink

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.  smile

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 smile

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.  wink

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

Re: Calculating Easter

thanx,

such a doddle wink


regards

Stefan

Offline

 

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

Adam Bell
Administrator
From:: Nova Scotia, Canada
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

Adam Bell wrote:

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.  smile  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.  wink


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. smile

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

Hi all! What about this kind of code?

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! smile

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

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)