返回给定年份的复活节日期的函数

发布于 2024-08-20 03:00:51 字数 539 浏览 6 评论 0原文

所以,这是一个有趣的小编程挑战。我正在编写一种快速方法来确定特定年份的所有市场假期,然后我开始阅读有关复活节的内容< /a> 并发现确定其日期的逻辑是多么疯狂*——逾越节满后的第一个星期日春分之后的月亮!有人知道现有的函数可以计算给定年份的复活节日期吗?

诚然,这可能并不那么困难。我只是想如果有人已经这样做了我会问一下。 (这似乎很有可能。)

更新:实际上,我真的在寻找耶稣受难日的日期(复活节之前的星期五) ...我只是认为复活节会让我到达那里。由于我在美国,我想我正在寻找天主教复活节?但如果我错了,也许有人可以纠正我。

*我所说的“疯狂”是指,参与。没有任何攻击性的内容...

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

亚希 2024-08-27 03:00:51

Python:使用 dateutil 的 easter() 函数

>>> from dateutil.easter import *
>>> print easter(2010)
2010-04-04
>>> print easter(2011)
2011-04-24

这些函数获取您喜欢的计算类型作为参数:

EASTER_JULIAN   = 1
EASTER_ORTHODOX = 2
EASTER_WESTERN  = 3

您可以选择与美国相关的计算类型。

从结果中减少两天会给你耶稣受难日:

>>> from datetime import timedelta
>>> d = timedelta(days=-2)
>>> easter(2011)
datetime.date(2011, 4, 24)
>>> easter(2011)+d
datetime.date(2011, 4, 22)

奇怪的是,有人正在迭代这个,并在 中发布了结果维基百科关于该算法的文章

alt text

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:

alt text

蒗幽 2024-08-27 03:00:51

在 SQL Server 复活节周日看起来像这样,向下滚动到耶稣受难日

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

耶稣受难日就像这样,它使用上面的复活节函数

CREATE FUNCTION dbo.GetGoodFriday 
( 
    @Y INT 
) 
RETURNS SMALLDATETIME 
AS 
BEGIN 
    RETURN (SELECT dbo.GetEasterSunday(@Y) - 2) 
END 
GO

从这里:http://web .archive.org/web/20070611150639/http://sqlserver2000.databases.aspfaq.com/why-should-i-consider-using-an-auxiliary-calendar-table.html

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

From here: http://web.archive.org/web/20070611150639/http://sqlserver2000.databases.aspfaq.com/why-should-i-consider-using-an-auxiliary-calendar-table.html

如果没结果 2024-08-27 03:00:51

当我写这篇文章时(基于星期几和节假日的流量预测),
我放弃了自己写的尝试。我在网上的某个地方找到了它。该代码是公共领域的,但是......

叹息

亲自看看。

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
}

有些问题最好不要问。

我感到幸运的是,我国所有的移动假期都是从复活节日期开始的固定偏移量。

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.

优雅的叶子 2024-08-27 03:00:51

下面的 SQL Server 函数比接受的答案更通用

接受的答案仅在以下范围(含)内正确:1900-04-15 到 2099-04-12

它使用美国海军天文台 (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 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
爱的故事 2024-08-27 03:00:51

希腊东正教和天主教复活节的 VB .NET 函数:

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

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
我还不会笑 2024-08-27 03:00:51

下面的代码通过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

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
⒈起吃苦の倖褔 2024-08-27 03:00:51

在某处找到了这个 Excel 公式
假设单元格 A1 包含年份,例如 2020

ROUND(DATE(A1;4;1)/7+MOD(19*MOD(A1;19)-7;30)*0,14;0)*7-6

转换为 T-SQL 导致我这样:

DECLARE @yr INT=2020
SELECT DATEADD(dd, ROUND(DATEDIFF(dd, '1899-12-30', DATEFROMPARTS(@yr, 4, 1)) / 7.0 + ((19.0 * (@yr % 19) - 7) % 30) * 0.14, 0) * 7.0 - 6, -2)

Found this Excel formula somewhere
Assuming cell A1 contains year e.g. 2020

ROUND(DATE(A1;4;1)/7+MOD(19*MOD(A1;19)-7;30)*0,14;0)*7-6

Converted to T-SQL lead me to this:

DECLARE @yr INT=2020
SELECT DATEADD(dd, ROUND(DATEDIFF(dd, '1899-12-30', DATEFROMPARTS(@yr, 4, 1)) / 7.0 + ((19.0 * (@yr % 19) - 7) % 30) * 0.14, 0) * 7.0 - 6, -2)
枕梦 2024-08-27 03:00:51

在 JS 中,取自此处

var epoch=2444238.5,elonge=278.83354,elongp=282.596403,eccent=.016718,sunsmax=149598500,sunangsiz=.533128,mmlong=64.975464,mmlongp=349.383063,mlnode=151.950429,minc=5.145396,mecc=.0549,mangsiz=.5181,msmax=384401,mparallax=.9507,synmonth=29.53058868,lunatbase=2423436,earthrad=6378.16,PI=3.141592653589793,epsilon=1e-6;function sgn(x){return x<0?-1:x>0?1:0}function abs(x){return x<0?-x:x}function fixAngle(a){return a-360*Math.floor(a/360)}function toRad(d){return d*(PI/180)}function toDeg(d){return d*(180/PI)}function dsin(x){return Math.sin(toRad(x))}function dcos(x){return Math.cos(toRad(x))}function toJulianTime(date){var year,month,day;year=date.getFullYear();var m=(month=date.getMonth()+1)>2?month:month+12,y=month>2?year:year-1,d=(day=date.getDate())+date.getHours()/24+date.getMinutes()/1440+(date.getSeconds()+date.getMilliseconds()/1e3)/86400,b=isJulianDate(year,month,day)?0:2-y/100+y/100/4;return Math.floor(365.25*(y+4716)+Math.floor(30.6001*(m+1))+d+b-1524.5)}function isJulianDate(year,month,day){if(year<1582)return!0;if(year>1582)return!1;if(month<10)return!0;if(month>10)return!1;if(day<5)return!0;if(day>14)return!1;throw"Any date in the range 10/5/1582 to 10/14/1582 is invalid!"}function jyear(td,yy,mm,dd){var z,f,alpha,b,c,d,e;return f=(td+=.5)-(z=Math.floor(td)),b=(z<2299161?z:z+1+(alpha=Math.floor((z-1867216.25)/36524.25))-Math.floor(alpha/4))+1524,c=Math.floor((b-122.1)/365.25),d=Math.floor(365.25*c),e=Math.floor((b-d)/30.6001),{day:Math.floor(b-d-Math.floor(30.6001*e)+f),month:Math.floor(e<14?e-1:e-13),year:Math.floor(mm>2?c-4716:c-4715)}}function jhms(j){var ij;return j+=.5,ij=Math.floor(86400*(j-Math.floor(j))+.5),{hour:Math.floor(ij/3600),minute:Math.floor(ij/60%60),second:Math.floor(ij%60)}}function jwday(j){return Math.floor(j+1.5)%7}function meanphase(sdate,k){var t,t2;return 2415020.75933+synmonth*k+1178e-7*(t2=(t=(sdate-2415020)/36525)*t)-155e-9*(t2*t)+33e-5*dsin(166.56+132.87*t-.009173*t2)}function truephase(k,phase){var t,t2,t3,pt,m,mprime,f,apcor=!1;if(pt=2415020.75933+synmonth*(k+=phase)+1178e-7*(t2=(t=k/1236.85)*t)-155e-9*(t3=t2*t)+33e-5*dsin(166.56+132.87*t-.009173*t2),m=359.2242+29.10535608*k-333e-7*t2-347e-8*t3,mprime=306.0253+385.81691806*k+.0107306*t2+1236e-8*t3,f=21.2964+390.67050646*k-.0016528*t2-239e-8*t3,phase<.01||abs(phase-.5)<.01?(pt+=(.1734-393e-6*t)*dsin(m)+.0021*dsin(2*m)-.4068*dsin(mprime)+.0161*dsin(2*mprime)-4e-4*dsin(3*mprime)+.0104*dsin(2*f)-.0051*dsin(m+mprime)-.0074*dsin(m-mprime)+4e-4*dsin(2*f+m)-4e-4*dsin(2*f-m)-6e-4*dsin(2*f+mprime)+.001*dsin(2*f-mprime)+5e-4*dsin(m+2*mprime),apcor=!0):(abs(phase-.25)<.01||abs(phase-.75)<.01)&&(pt+=(.1721-4e-4*t)*dsin(m)+.0021*dsin(2*m)-.628*dsin(mprime)+.0089*dsin(2*mprime)-4e-4*dsin(3*mprime)+.0079*dsin(2*f)-.0119*dsin(m+mprime)-.0047*dsin(m-mprime)+3e-4*dsin(2*f+m)-4e-4*dsin(2*f-m)-6e-4*dsin(2*f+mprime)+.0021*dsin(2*f-mprime)+3e-4*dsin(m+2*mprime)+4e-4*dsin(m-2*mprime)-3e-4*dsin(2*m+mprime),pt+=phase<.5?.0028-4e-4*dcos(m)+3e-4*dcos(mprime):4e-4*dcos(m)-.0028-3e-4*dcos(mprime),apcor=!0),!apcor)throw"Error calculating moon phase!";return pt}function phasehunt(sdate,phases){var adate,k1,k2,nt1,nt2,yy,mm,dd,jyearResult=jyear(adate=sdate-45,yy,mm,dd);for(yy=jyearResult.year,mm=jyearResult.month,dd=jyearResult.day,adate=nt1=meanphase(adate,k1=Math.floor(12.3685*(yy+1/12*(mm-1)-1900)));nt2=meanphase(adate+=synmonth,k2=k1+1),!(nt1<=sdate&&nt2>sdate);)nt1=nt2,k1=k2;return phases[0]=truephase(k1,0),phases[1]=truephase(k1,.25),phases[2]=truephase(k1,.5),phases[3]=truephase(k1,.75),phases[4]=truephase(k2,0),phases}function kepler(m,ecc){var e,delta;e=m=toRad(m);do{e-=(delta=e-ecc*Math.sin(e)-m)/(1-ecc*Math.cos(e))}while(abs(delta)>epsilon);return e}function getMoonPhase(julianDate){var Day,N,M,Ec,Lambdasun,ml,MM,MN,Ev,Ae,MmP,mEc,lP,lPP,NP,y,x,MoonAge,MoonPhase,MoonDist,MoonDFrac,MoonAng,F,SunDist,SunAng;return N=fixAngle(360/365.2422*(Day=julianDate-epoch)),Ec=kepler(M=fixAngle(N+elonge-elongp),eccent),Ec=Math.sqrt((1+eccent)/(1-eccent))*Math.tan(Ec/2),Lambdasun=fixAngle((Ec=2*toDeg(Math.atan(Ec)))+elongp),F=(1+eccent*Math.cos(toRad(Ec)))/(1-eccent*eccent),SunDist=sunsmax/F,SunAng=F*sunangsiz,ml=fixAngle(13.1763966*Day+mmlong),MM=fixAngle(ml-.1114041*Day-mmlongp),MN=fixAngle(mlnode-.0529539*Day),MmP=MM+(Ev=1.2739*Math.sin(toRad(2*(ml-Lambdasun)-MM)))-(Ae=.1858*Math.sin(toRad(M)))-.37*Math.sin(toRad(M)),lPP=(lP=ml+Ev+(mEc=6.2886*Math.sin(toRad(MmP)))-Ae+.214*Math.sin(toRad(2*MmP)))+.6583*Math.sin(toRad(2*(lP-Lambdasun))),NP=MN-.16*Math.sin(toRad(M)),y=Math.sin(toRad(lPP-NP))*Math.cos(toRad(minc)),x=Math.cos(toRad(lPP-NP)),toDeg(Math.atan2(y,x)),NP,toDeg(Math.asin(Math.sin(toRad(lPP-NP))*Math.sin(toRad(minc)))),MoonAge=lPP-Lambdasun,MoonPhase=(1-Math.cos(toRad(MoonAge)))/2,MoonDist=msmax*(1-mecc*mecc)/(1+mecc*Math.cos(toRad(MmP+mEc))),MoonAng=mangsiz/(MoonDFrac=MoonDist/msmax),mparallax/MoonDFrac,{moonIllumination:MoonPhase,moonAgeInDays:synmonth*(fixAngle(MoonAge)/360),distanceInKm:MoonDist,angularDiameterInDeg:MoonAng,distanceToSun:SunDist,sunAngularDiameter:SunAng,moonPhase:fixAngle(MoonAge)/360}}function getMoonInfo(date){return null==date?{moonPhase:0,moonIllumination:0,moonAgeInDays:0,distanceInKm:0,angularDiameterInDeg:0,distanceToSun:0,sunAngularDiameter:0}:getMoonPhase(toJulianTime(date))}function getEaster(year){var previousMoonInfo,moonInfo,fullMoon=new Date(year,2,21),gettingDarker=void 0;do{previousMoonInfo=getMoonInfo(fullMoon),fullMoon.setDate(fullMoon.getDate()+1),moonInfo=getMoonInfo(fullMoon),void 0===gettingDarker?gettingDarker=moonInfo.moonIllumination<previousMoonInfo.moonIllumination:gettingDarker&&moonInfo.moonIllumination>previousMoonInfo.moonIllumination&&(gettingDarker=!1)}while(gettingDarker&&moonInfo.moonIllumination<previousMoonInfo.moonIllumination||!gettingDarker&&moonInfo.moonIllumination>previousMoonInfo.moonIllumination);for(fullMoon.setDate(fullMoon.getDate()-1);0!==fullMoon.getDay();)fullMoon.setDate(fullMoon.getDate()+1);return fullMoon}

然后运行 ​​getEaster(2020); //-> 2020 年 4 月 12 日星期日

In JS, taken from here.

var epoch=2444238.5,elonge=278.83354,elongp=282.596403,eccent=.016718,sunsmax=149598500,sunangsiz=.533128,mmlong=64.975464,mmlongp=349.383063,mlnode=151.950429,minc=5.145396,mecc=.0549,mangsiz=.5181,msmax=384401,mparallax=.9507,synmonth=29.53058868,lunatbase=2423436,earthrad=6378.16,PI=3.141592653589793,epsilon=1e-6;function sgn(x){return x<0?-1:x>0?1:0}function abs(x){return x<0?-x:x}function fixAngle(a){return a-360*Math.floor(a/360)}function toRad(d){return d*(PI/180)}function toDeg(d){return d*(180/PI)}function dsin(x){return Math.sin(toRad(x))}function dcos(x){return Math.cos(toRad(x))}function toJulianTime(date){var year,month,day;year=date.getFullYear();var m=(month=date.getMonth()+1)>2?month:month+12,y=month>2?year:year-1,d=(day=date.getDate())+date.getHours()/24+date.getMinutes()/1440+(date.getSeconds()+date.getMilliseconds()/1e3)/86400,b=isJulianDate(year,month,day)?0:2-y/100+y/100/4;return Math.floor(365.25*(y+4716)+Math.floor(30.6001*(m+1))+d+b-1524.5)}function isJulianDate(year,month,day){if(year<1582)return!0;if(year>1582)return!1;if(month<10)return!0;if(month>10)return!1;if(day<5)return!0;if(day>14)return!1;throw"Any date in the range 10/5/1582 to 10/14/1582 is invalid!"}function jyear(td,yy,mm,dd){var z,f,alpha,b,c,d,e;return f=(td+=.5)-(z=Math.floor(td)),b=(z<2299161?z:z+1+(alpha=Math.floor((z-1867216.25)/36524.25))-Math.floor(alpha/4))+1524,c=Math.floor((b-122.1)/365.25),d=Math.floor(365.25*c),e=Math.floor((b-d)/30.6001),{day:Math.floor(b-d-Math.floor(30.6001*e)+f),month:Math.floor(e<14?e-1:e-13),year:Math.floor(mm>2?c-4716:c-4715)}}function jhms(j){var ij;return j+=.5,ij=Math.floor(86400*(j-Math.floor(j))+.5),{hour:Math.floor(ij/3600),minute:Math.floor(ij/60%60),second:Math.floor(ij%60)}}function jwday(j){return Math.floor(j+1.5)%7}function meanphase(sdate,k){var t,t2;return 2415020.75933+synmonth*k+1178e-7*(t2=(t=(sdate-2415020)/36525)*t)-155e-9*(t2*t)+33e-5*dsin(166.56+132.87*t-.009173*t2)}function truephase(k,phase){var t,t2,t3,pt,m,mprime,f,apcor=!1;if(pt=2415020.75933+synmonth*(k+=phase)+1178e-7*(t2=(t=k/1236.85)*t)-155e-9*(t3=t2*t)+33e-5*dsin(166.56+132.87*t-.009173*t2),m=359.2242+29.10535608*k-333e-7*t2-347e-8*t3,mprime=306.0253+385.81691806*k+.0107306*t2+1236e-8*t3,f=21.2964+390.67050646*k-.0016528*t2-239e-8*t3,phase<.01||abs(phase-.5)<.01?(pt+=(.1734-393e-6*t)*dsin(m)+.0021*dsin(2*m)-.4068*dsin(mprime)+.0161*dsin(2*mprime)-4e-4*dsin(3*mprime)+.0104*dsin(2*f)-.0051*dsin(m+mprime)-.0074*dsin(m-mprime)+4e-4*dsin(2*f+m)-4e-4*dsin(2*f-m)-6e-4*dsin(2*f+mprime)+.001*dsin(2*f-mprime)+5e-4*dsin(m+2*mprime),apcor=!0):(abs(phase-.25)<.01||abs(phase-.75)<.01)&&(pt+=(.1721-4e-4*t)*dsin(m)+.0021*dsin(2*m)-.628*dsin(mprime)+.0089*dsin(2*mprime)-4e-4*dsin(3*mprime)+.0079*dsin(2*f)-.0119*dsin(m+mprime)-.0047*dsin(m-mprime)+3e-4*dsin(2*f+m)-4e-4*dsin(2*f-m)-6e-4*dsin(2*f+mprime)+.0021*dsin(2*f-mprime)+3e-4*dsin(m+2*mprime)+4e-4*dsin(m-2*mprime)-3e-4*dsin(2*m+mprime),pt+=phase<.5?.0028-4e-4*dcos(m)+3e-4*dcos(mprime):4e-4*dcos(m)-.0028-3e-4*dcos(mprime),apcor=!0),!apcor)throw"Error calculating moon phase!";return pt}function phasehunt(sdate,phases){var adate,k1,k2,nt1,nt2,yy,mm,dd,jyearResult=jyear(adate=sdate-45,yy,mm,dd);for(yy=jyearResult.year,mm=jyearResult.month,dd=jyearResult.day,adate=nt1=meanphase(adate,k1=Math.floor(12.3685*(yy+1/12*(mm-1)-1900)));nt2=meanphase(adate+=synmonth,k2=k1+1),!(nt1<=sdate&&nt2>sdate);)nt1=nt2,k1=k2;return phases[0]=truephase(k1,0),phases[1]=truephase(k1,.25),phases[2]=truephase(k1,.5),phases[3]=truephase(k1,.75),phases[4]=truephase(k2,0),phases}function kepler(m,ecc){var e,delta;e=m=toRad(m);do{e-=(delta=e-ecc*Math.sin(e)-m)/(1-ecc*Math.cos(e))}while(abs(delta)>epsilon);return e}function getMoonPhase(julianDate){var Day,N,M,Ec,Lambdasun,ml,MM,MN,Ev,Ae,MmP,mEc,lP,lPP,NP,y,x,MoonAge,MoonPhase,MoonDist,MoonDFrac,MoonAng,F,SunDist,SunAng;return N=fixAngle(360/365.2422*(Day=julianDate-epoch)),Ec=kepler(M=fixAngle(N+elonge-elongp),eccent),Ec=Math.sqrt((1+eccent)/(1-eccent))*Math.tan(Ec/2),Lambdasun=fixAngle((Ec=2*toDeg(Math.atan(Ec)))+elongp),F=(1+eccent*Math.cos(toRad(Ec)))/(1-eccent*eccent),SunDist=sunsmax/F,SunAng=F*sunangsiz,ml=fixAngle(13.1763966*Day+mmlong),MM=fixAngle(ml-.1114041*Day-mmlongp),MN=fixAngle(mlnode-.0529539*Day),MmP=MM+(Ev=1.2739*Math.sin(toRad(2*(ml-Lambdasun)-MM)))-(Ae=.1858*Math.sin(toRad(M)))-.37*Math.sin(toRad(M)),lPP=(lP=ml+Ev+(mEc=6.2886*Math.sin(toRad(MmP)))-Ae+.214*Math.sin(toRad(2*MmP)))+.6583*Math.sin(toRad(2*(lP-Lambdasun))),NP=MN-.16*Math.sin(toRad(M)),y=Math.sin(toRad(lPP-NP))*Math.cos(toRad(minc)),x=Math.cos(toRad(lPP-NP)),toDeg(Math.atan2(y,x)),NP,toDeg(Math.asin(Math.sin(toRad(lPP-NP))*Math.sin(toRad(minc)))),MoonAge=lPP-Lambdasun,MoonPhase=(1-Math.cos(toRad(MoonAge)))/2,MoonDist=msmax*(1-mecc*mecc)/(1+mecc*Math.cos(toRad(MmP+mEc))),MoonAng=mangsiz/(MoonDFrac=MoonDist/msmax),mparallax/MoonDFrac,{moonIllumination:MoonPhase,moonAgeInDays:synmonth*(fixAngle(MoonAge)/360),distanceInKm:MoonDist,angularDiameterInDeg:MoonAng,distanceToSun:SunDist,sunAngularDiameter:SunAng,moonPhase:fixAngle(MoonAge)/360}}function getMoonInfo(date){return null==date?{moonPhase:0,moonIllumination:0,moonAgeInDays:0,distanceInKm:0,angularDiameterInDeg:0,distanceToSun:0,sunAngularDiameter:0}:getMoonPhase(toJulianTime(date))}function getEaster(year){var previousMoonInfo,moonInfo,fullMoon=new Date(year,2,21),gettingDarker=void 0;do{previousMoonInfo=getMoonInfo(fullMoon),fullMoon.setDate(fullMoon.getDate()+1),moonInfo=getMoonInfo(fullMoon),void 0===gettingDarker?gettingDarker=moonInfo.moonIllumination<previousMoonInfo.moonIllumination:gettingDarker&&moonInfo.moonIllumination>previousMoonInfo.moonIllumination&&(gettingDarker=!1)}while(gettingDarker&&moonInfo.moonIllumination<previousMoonInfo.moonIllumination||!gettingDarker&&moonInfo.moonIllumination>previousMoonInfo.moonIllumination);for(fullMoon.setDate(fullMoon.getDate()-1);0!==fullMoon.getDay();)fullMoon.setDate(fullMoon.getDate()+1);return fullMoon}

Then run getEaster(2020); // -> Sun Apr 12 2020

青衫儰鉨ミ守葔 2024-08-27 03:00:51

Oracle 给出了 1900 年至 2078 年之间的正确复活节日期:

to_date('18991230','yyyymmdd') + round((to_date(callendar_year||'04','yyyymm') -
to_date('18991230','yyyymmdd')) / 7 + mod(19 * MOD(callendar_year, 19) + 23, 30) * 0.14, 0) * 7-6

Oracle, gives correct Easter dates between 1900 and 2078:

to_date('18991230','yyyymmdd') + round((to_date(callendar_year||'04','yyyymm') -
to_date('18991230','yyyymmdd')) / 7 + mod(19 * MOD(callendar_year, 19) + 23, 30) * 0.14, 0) * 7-6
少女的英雄梦 2024-08-27 03:00:51

尽管您的问题专门针对天主教(公历)复活节,但我将给出三种历史上使用的算法,因为这使得解释数学变得更容易。

请注意,Python 的 datetime.date.fromordinal 函数的参数是 Rata Die< /a> 天数,第 1 天 = 0001-01-01。

希伯来历

这是逾越节期间的星期日。

import datetime

def hebrew_easter(year):
    lunation = (235 * year + 6) // 19 - 9
    week, molad = divmod(765433 * lunation + 65451, 181440)
    if (7 * year + 6) % 19 < 12 and (molad >= 172000):
        week += 1
    return datetime.date.fromordinal(week * 7)

公历日期范围为 3 月 27 日4 月 30 日(适用于 1900-2213 年)。

lunation 表示自 Rata Die 纪元以来经过的阴历月数。希伯来历使用 默通周期 19 年 = 235 个朔望月。月相周期假定为恒定的 765433/25920 (29.53059413580247) 天。

每年月数 (235/19) 和每月天数 (765433/25920) 的乘积得出平均一年长度为 35975351/98496 (365.24682220597794) 天。这比公历的平均年份 365.2425 天稍长。因此,犹太节日的日期平均每 231 年推迟 1 天。

molad,除法的余数,与时序有关一周内相关的月相合相。

if 语句的作用是处理希伯来历的一些特殊规则,以确保:

  • 犹太新年永远不会落在星期日、星期三或星期五(因为这会导致几个重要的圣日)落在一周中不方便的日子)。
  • 一年中的天数必须为 353、354、355、383、384、385。

其中一个名为 deḥiyyat GaTaRaD 的规则导致犹太新年从星期二推迟到星期四,这导致之前的逾越节从周日推迟到周二。这就需要复活节推迟一周。

请注意,常量 172000 在某种程度上是任意选择的。为了使上述算法适用于 0001 到 9999 年(Python 的 datetime 类支持的范围),171999 到 172071 之间的任何值都可以。我选择 172000 只是因为它是该范围内“最圆”的数字。如果由于某种原因,您需要使用更广泛的年份,则可能需要调整此常量。

儒略历

公元 325 年,尼西亚会议决定不使用实际的犹太历来设定复活节的日期,而是进行自己的计算。

import datetime 

def julian_easter(year):
    leap_months = (7 * year + 8) // 19 - 9
    week = ((6725 * year + 18) // 19 + 30 * leap_months + year // 4 + 5) // 7
    return datetime.date.fromordinal(week * 7)

公历日期范围为 4 月 5 日5 月 8 日(对于 2011-2172 年)。

此计算使用与希伯来历相同的 235/19 默冬周期,但添加了一些怪癖:

  • 计算中基本上忽略了 2 月 29 日闰日,仅在最后添加。
  • 农历闰月被视为 30 天,而 12 个“常规”农历月份为 6725/228 (29.49561403508772) 天。

否则,计算非常简单。

公历日期

import datetime

def gregorian_easter(year):
    century = year // 100
    lunar_adj = (8 * century + 13) // 25
    solar_adj = -century + century // 4
    total_adj = solar_adj + lunar_adj
    leap_months = (210 * year - year % 19 + 19 * total_adj + 266) // 570
    full_moon = (6725 * year + 18) // 19 + 30 * leap_months - lunar_adj + year // 4 + 3
    if 286 <= (total_adj + year % 19 * 11) % 30 * 19 - year % 19 <= 312:
        full_moon -= 1
    week = full_moon // 7 - 38
    return datetime.date.fromordinal(week * 7)

范围为3 月 22 日4 月 25 日

回想一下“希伯来历”部分,默冬年(235/19 朔望月)是 365.2468 天(四舍五入到合理的位数)。这比儒略历的平均年份 365.25 天要短。因此,我们需要将年长每年调整 0.0032 天,或每世纪 0.32 (8/25) 天。这就是 lunar_adj 的作用:增加默冬年的长度以匹配儒略历。

另外,回想一下,公历每 400 年删除了 3 个闰日,将平均年长度减少到 365.2425 天。这就是 solar_adj 的作用。

leap_months 是对儒略历 7/19 闰年周期的轻微调整。请注意,当 total_adj 达到 30 天时,会添加一个额外的闰月。

full_moon 计算与儒略计算非常相似,非闰年(12 个月)农历年为 6725/19 天,闰月为 30 天。但是有一个月球调整,以及恒定偏移量的变化。

if 语句...很难解释,但对于计算工作来说是必需的。

无论如何,我已经根据 https://www.projectpluto.com/easter 上的日期验证了我的功能。嗯。

Although your question asks specifically for Catholic (Gregorian calendar) Easter, I'm going to give three historically-used algorithms, because that makes it easier to explain the math.

Note that the Parameter to Python's datetime.date.fromordinal function is the Rata Die day number, with day 1 = 0001-01-01.

Hebrew Calendar

This is the Sunday that falls during Passover.

import datetime

def hebrew_easter(year):
    lunation = (235 * year + 6) // 19 - 9
    week, molad = divmod(765433 * lunation + 65451, 181440)
    if (7 * year + 6) % 19 < 12 and (molad >= 172000):
        week += 1
    return datetime.date.fromordinal(week * 7)

Gregorian-calendar dates range from March 27 to April 30 (for years 1900-2213).

lunation represents the number of lunar months elapsed since the Rata Die epoch. The Hebrew Calendar uses the Metonic Cycle of 19 years = 235 synodic months. The lunar phase cycle is assumed to be a constant 765433/25920 (29.53059413580247) days.

The product of months per year (235/19) and days per month (765433/25920) results in an average year length of 35975351/98496 (365.24682220597794) days. This is slightly longer than the Gregorian calendar's average year of 365.2425 days. So on average, the date of Jewish holidays drifts 1 day later every 231 years.

molad, the remainder of the division, is related to the timing of the relevant lunar conjunction within the week.

The if statement is there to deal with some special rules of the Hebrew calendar to ensure that:

  • Rosh Hashana never falls on Sunday, Wednesday, or Friday (because this would cause a couple of the high holy days to fall on inconvenient days of the week).
  • The number of days in a year must be either 353, 354, 355, 383, 384, 385.

One of these rules, named deḥiyyat GaTaRaD, causes Rosh Hashana to be postponed from Tuesday to Thursday, which results in the preceding Passover being postponed from Sunday to Tuesday. This requires Easter to be postponed one week.

Note that the constant 172000 is somewhat arbitrarily selected. To make the above algorithm work for the years 0001 to 9999 (the range supported by Python's datetime class), any value between 171999 and 172071 will work. I chose 172000 simply for being the “roundest” number in that range. If for some reason, you need to work with a wider range of years, this constant may need to be adjusted.

Julian Calendar

The Council of Nicea in 325 CE decided not to use the actual Jewish calendar to set the date of Easter, but to perform their own calculation.

import datetime 

def julian_easter(year):
    leap_months = (7 * year + 8) // 19 - 9
    week = ((6725 * year + 18) // 19 + 30 * leap_months + year // 4 + 5) // 7
    return datetime.date.fromordinal(week * 7)

Gregorian-calendar dates range from April 5 to May 8 (for years 2011-2172).

This calculation uses the same 235/19 Metonic cycle as the Hebrew calendar, but with a couple of added quirks:

  • The February 29 leap day is essentially ignored in the calculation, and only added at the end.
  • Lunar leap months are treated as 30 days, while the 12 "regular" lunar months are 6725/228 (29.49561403508772) days.

Otherwise, the calculation is pretty straightforward.

Gregorian Calendar

import datetime

def gregorian_easter(year):
    century = year // 100
    lunar_adj = (8 * century + 13) // 25
    solar_adj = -century + century // 4
    total_adj = solar_adj + lunar_adj
    leap_months = (210 * year - year % 19 + 19 * total_adj + 266) // 570
    full_moon = (6725 * year + 18) // 19 + 30 * leap_months - lunar_adj + year // 4 + 3
    if 286 <= (total_adj + year % 19 * 11) % 30 * 19 - year % 19 <= 312:
        full_moon -= 1
    week = full_moon // 7 - 38
    return datetime.date.fromordinal(week * 7)

Dates range from March 22 to April 25.

Recall from the “Hebrew Calendar” section that a Metonic year (235/19 synodic months) is 365.2468 days (rounding to a reasonable number of digits). This is shorter than the Julian calendar's average year of 365.25 days. So we need to adjust the year length by 0.0032 day per year, or 0.32 (8/25) day per century. This is what lunar_adj does: Increase the Metonic year length to match the Julian calendar.

Also, recall that the Gregorian calendar removed three leap days every 400 years, reducing the average year length to 365.2425 days. This is what solar_adj does.

leap_months is a slight adjustment to the Julian calendar's 7/19 leap year cycle. Note that when total_adj reaches 30 days, an extra leap month is added.

The full_moon calculation is very similar to the Julian one, with a non-leap (12-month) lunar year of 6725/19 days, and 30-day leap months. But there's a lunar adjustment, and a change to the constant offset.

The if statement is...difficult to explain, but necessary for the calculation to work.

Anyhow, I've verified my function against the dates at https://www.projectpluto.com/easter.htm.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文