So, here's a funny little programming challenge. I was writing a quick method to determine all the market holidays for a particular year, and then I started reading about Easter and discovered just how crazy* the logic is for determining its date--the first Sunday after the Paschal Full Moon following the spring equinox! Does anybody know of an existing function to calculate the date of Easter for a given year?
Granted, it's probably not all that hard to do; I just figured I'd ask in case somebody's already done this. (And that seems very likely.)
UPDATE: Actually, I'm really looking for the date of Good Friday (the Friday before Easter)... I just figured Easter would get me there. And since I'm in the U.S., I assume I'm looking for the Catholic Easter? But perhaps someone can correct me on that if I'm wrong.
*By "crazy" I meant, like, involved. Not anything offensive...
in SQL Server Easter Sunday would look like this, scroll down for Good Friday
CREATE FUNCTION dbo.GetEasterSunday ( @Y INT ) RETURNS SMALLDATETIME AS BEGIN DECLARE @EpactCalc INT, @PaschalDaysCalc INT, @NumOfDaysToSunday INT, @EasterMonth INT, @EasterDay INT SET @EpactCalc = (24 + 19 * (@Y % 19)) % 30 SET @PaschalDaysCalc = @EpactCalc - (@EpactCalc / 28) SET @NumOfDaysToSunday = @PaschalDaysCalc - ( (@Y + @Y / 4 + @PaschalDaysCalc - 13) % 7 ) SET @EasterMonth = 3 + (@NumOfDaysToSunday + 40) / 44 SET @EasterDay = @NumOfDaysToSunday + 28 - ( 31 * (@EasterMonth / 4) ) RETURN ( SELECT CONVERT ( SMALLDATETIME, RTRIM(@Y) + RIGHT('0'+RTRIM(@EasterMonth), 2) + RIGHT('0'+RTRIM(@EasterDay), 2) ) ) END GO
Good Friday is like this and it uses the Easter function above
CREATE FUNCTION dbo.GetGoodFriday ( @Y INT ) RETURNS SMALLDATETIME AS BEGIN RETURN (SELECT dbo.GetEasterSunday(@Y) - 2) END GO
Python: using dateutil's easter()
function.
>>> from dateutil.easter import * >>> print easter(2010) 2010-04-04 >>> print easter(2011) 2011-04-24
The functions gets, as an argument, the type of calculation you like:
EASTER_JULIAN = 1 EASTER_ORTHODOX = 2 EASTER_WESTERN = 3
You can pick the one relevant to the US.
Reducing two days from the result would give you Good Friday:
>>> from datetime import timedelta >>> d = timedelta(days=-2) >>> easter(2011) datetime.date(2011, 4, 24) >>> easter(2011)+d datetime.date(2011, 4, 22)
Oddly enough, someone was iterating this, and published the results in Wikipedia's article about the algorithm:
When it came for me to write this (traffic prediction based on day of week and holiday), I gave up on trying to write it by myself. I found it somewhere on the net. The code was public domain, but...
sigh
see for yourself.
void dateOfEaster(struct tm* p) { int Y = p->tm_year; int a = Y % 19; int b = Y / 100; int c = Y % 100; int d = b / 4; int e = b % 4; int f = (b + 8) / 25; int g = (b - f + 1) / 3; int h = (19 * a + b - d - g + 15) % 30; int i = c / 4; int k = c % 4; int L = (32 + 2 * e + 2 * i - h - k) % 7; int m = (a + 11 * h + 22 * L) / 451; p->tm_mon = ((h + L - 7 * m + 114) / 31 ) - 1; p->tm_mday = ((h + L - 7 * m + 114) % 31) + 1; p->tm_hour = 12; const time_t tmp = mktime(p); *p = *localtime(&tmp); //recover yday from mon+mday }
Some questions are better left unasked.
I feel lucky that all moving holidays in my country are a fixed offset from the date of Easter.
The SQL Server function below is more general than the accepted answer
The accepted answer is only correct for the range (inclusive) : 1900-04-15 to 2099-04-12
It uses the algorithm provided by The United States Naval Observatory (USNO)
http://aa.usno.navy.mil/faq/docs/easter.php
CREATE FUNCTION dbo.GetEasterSunday (@Y INT) RETURNS DATETIME AS BEGIN -- Source of algorithm : http://aa.usno.navy.mil/faq/docs/easter.php DECLARE @c INT = @Y / 100 DECLARE @n INT = @Y - 19 * (@Y / 19) DECLARE @k INT = (@c - 17) / 25 DECLARE @i INT = @c - @c / 4 - (@c - @k) / 3 + 19 * @n + 15 SET @i = @i - 30 * (@i / 30) SET @i = @i - (@i / 28) * (1 - (@i / 28) * (29 / (@i + 1)) * ((21 - @n) / 11)) DECLARE @j INT = @Y + @Y / 4 + @i + 2 - @c + @c / 4 SET @j = @j - 7 * (@j / 7) DECLARE @l INT = @i - @j DECLARE @m INT = 3 + (@l + 40) / 44 DECLARE @d INT = @l + 28 - 31 * (@m / 4) RETURN ( SELECT CONVERT ( DATETIME, RTRIM(@Y) + RIGHT('0'+RTRIM(@m), 2) + RIGHT('0'+RTRIM(@d), 2) ) ) END GO
The below code determines Easter through powershell:
function Get-DateOfEaster { param( [Parameter(ValueFromPipeline)] $theYear=(Get-Date).Year ) if($theYear -lt 1583) { return $null } else { # Step 1: Divide the theYear by 19 and store the # remainder in variable A. Example: If the theYear # is 2000, then A is initialized to 5. $a = $theYear % 19 # Step 2: Divide the theYear by 100. Store the integer # result in B and the remainder in C. $c = $theYear % 100 $b = ($theYear -$c) / 100 # Step 3: Divide B (calculated above). Store the # integer result in D and the remainder in E. $e = $b % 4 $d = ($b - $e) / 4 # Step 4: Divide (b+8)/25 and store the integer # portion of the result in F. $f = [math]::floor(($b + 8) / 25) # Step 5: Divide (b-f+1)/3 and store the integer # portion of the result in G. $g = [math]::floor(($b - $f + 1) / 3) # Step 6: Divide (19a+b-d-g+15)/30 and store the # remainder of the result in H. $h = (19 * $a + $b - $d - $g + 15) % 30 # Step 7: Divide C by 4. Store the integer result # in I and the remainder in K. $k = $c % 4 $i = ($c - $k) / 4 # Step 8: Divide (32+2e+2i-h-k) by 7. Store the # remainder of the result in L. $l = (32 + 2 * $e + 2 * $i - $h - $k) % 7 # Step 9: Divide (a + 11h + 22l) by 451 and # store the integer portion of the result in M. $m = [math]::floor(($a + 11 * $h + 22 * $l) / 451) # Step 10: Divide (h + l - 7m + 114) by 31. Store # the integer portion of the result in N and the # remainder in P. $p = ($h + $l - 7 * $m + 114) % 31 $n = (($h + $l - 7 * $m + 114) - $p) / 31 # At this point p+1 is the day on which Easter falls. # n is 3 for March and 4 for April. $DateTime = New-Object DateTime $theyear, $n, ($p+1), 0, 0, 0, ([DateTimeKind]::Utc) return $DateTime } } $eastersunday=Get-DateOfEaster 2015 Write-Host $eastersunday
VB .NET Functions for Greek Orthodox and Catholic Easter:
Public Shared Function OrthodoxEaster(ByVal Year As Integer) As Date Dim a = Year Mod 19 Dim b = Year Mod 7 Dim c = Year Mod 4 Dim d = (19 * a + 16) Mod 30 Dim e = (2 * c + 4 * b + 6 * d) Mod 7 Dim f = (19 * a + 16) Mod 30 Dim key = f + e + 3 Dim month = If((key > 30), 5, 4) Dim day = If((key > 30), key - 30, key) Return New DateTime(Year, month, day) End Function Public Shared Function CatholicEaster(ByVal Year As Integer) As DateTime Dim month = 3 Dim a = Year Mod 19 + 1 Dim b = Year / 100 + 1 Dim c = (3 * b) / 4 - 12 Dim d = (8 * b + 5) / 25 - 5 Dim e = (5 * Year) / 4 - c - 10 Dim f = (11 * a + 20 + d - c) Mod 30 If f = 24 Then f += 1 If (f = 25) AndAlso (a > 11) Then f += 1 Dim g = 44 - f If g < 21 Then g = g + 30 Dim day = (g + 7) - ((e + g) Mod 7) If day > 31 Then day = day - 31 month = 4 End If Return New DateTime(Year, month, day) End Function
来源:https://stackoverflow.com/questions/2192533/function-to-return-date-of-easter-for-the-given-year