Ostern feiern…

Wann ist dieses Jahr eigentlich Ostern?

esskar über Programmierung

Man stellt sich jedes Jahr die selbe Frage: Wann ist dieses Jahr eigentlich Ostern?
Klar ist, Ostern gehört zu den beweglichen Festen, deren Kalenderdatum jedes Jahr variiert.
Richtige Antworten auf diese Frage sind:

  • an einem Sonntag
  • 7 Tage nach dem sog. Palmsonntag
  • Ostersonntag fällt immer auf den Sonntag nach dem ersten offiziellen Vollmond nach Frühlingsanfang. Offizielles Datum für den Frühlingsanfang ist der 21. März, dadurch kann der Ostersonntag frühstens am 22. März sein.

Ostern wird also nicht willkürlich festgelegt. Es folgt festen Regeln und kann somit berechnet werden. Versuchen wir das doch mal.

21. März 2010 war ein Sonntag. Der erste Vollmond danach war Dienstag, der 30. März 2010. Der folgende Sonntag war der 04. April 2010, was laut Theorie also der Ostersonntag sein müsste: stimmt! Wir wissen nun also, Ostern hängt vom offiziellen Vollmond ab, dieser kann vom tatsächlichen Vollmond 1 bis 2 Tage abweichen! :-) Der Vollmond, welcher Ostern vorausgeht, wird als Paschal Vollmond bezeichnet, dabei spielen zwei Begriffe eine wichtige Rolle: die Goldene Zahl und die Epakte. Dies bezieht sich jedoch auch nur auf den Gregorianischen Kalendar. Der ältere, aber immer noch verwendete Julianischen Kalendar folgt anderen (einfacheren) Regeln.

Wer nun wirklich ins Detail gehen möchte, sollte sich das Paper von Heinz Bachmann und Urs Oswald mit dem Titel „Das Osterdatum“ anschauen. Ich hab es getan. Und raus kam diese kleine aber feine C# Klasse:

public enum EasterDay : int
{
    Thursday = -3, Friday = -2, Saturday = -1, Sunday = 0, Monday = 1,
    PalmSunday = -7,
}

public enum EasterCalendar { Default, Julian }

public class EasterDate
{
    private int m_offset, m_year;

    public EasterDate() 
        : this(EasterDay.Sunday) { }

    public EasterDate(EasterDay easterDay) 
        : this(DateTime.UtcNow.Year, easterDay) { }

    
    public EasterDate(int year)
        : this(year, EasterDay.Sunday) { }

    public EasterDate(int year, EasterDay easterDay)
        : this(year, (int)easterDay) { }

    private EasterDate(int year, int dayOffset)
    {
        m_year = year;
        m_offset = dayOffset;            
    }

    public DateTime ToDateTime()
    {
        DateTime dateTime = EasterDate.EasterDateTime(m_year);
        dateTime.AddDays(m_offset);
        return dateTime;
    }

    private static int DefaultOffset(int year)
    {
        int goldenNumber = year % 19;
        int century = ((int)year / 100);
        int epact = (century - ((int)century/4) - ((int)(century * 8 + 13)/25) + goldenNumber * 19 + 15) % 30;
        int interval = epact - ((int)epact / 28) * (1 - ((int)29 / (epact + 1))) * ((int)(21 - goldenNumber) / 11);
        int weekday = (year + ((int)year / 4) + interval + 2 - century + ((int)century / 4)) % 7;
        
        return interval - weekday;
    }

    private static int JulianOffset(int year)
    {
        int goldenNumber = year % 19;

        int interval = (goldenNumber * 19 + 15) % 30;
        int weekday = (year + ((int)year / 4) + interval) % 7;
        
        return interval - weekday;            
    }

    public static DateTime EasterDateTime(int year)
    {
        return EasterDate.EasterDateTime(year, EasterCalendar.Default);
    }
    
    public static DateTime EasterDateTime(int year, EasterCalendar easterCalendar)
    {
        DateTime retval;
        Calendar calendar = null;

        int offset;
        if (easterCalendar == EasterCalendar.Default)
        {
            offset = EasterDate.DefaultOffset(year);
        }
        else if (easterCalendar == EasterCalendar.Julian)
        {
            calendar = new JulianCalendar();
            offset = EasterDate.JulianOffset(year);
        }
        else
            throw new ArgumentException("Unknown easter calendar: " + calendar);

        int month = 3 + ((int)(offset + 40) / 44);
        int day = offset + 28 - 31 * ((int)month / 4);
        if (calendar != null) 
            retval = new DateTime(year, month, day, calendar);
        else 
            retval = new DateTime(year, month, day);

        return retval;
    }        
}    

Da ich mir letzte Zeit öfters anhören muss, dass ich selten Tests zu meinen Veröffentlichungen beilege ;-), hab ich diesmal noch ein paar NUnit Tests angehängt

[TestFixture]
class EasterDateTests
{
    [Test]
    public void Easter2010()
    {
        DateTime eastern = new DateTime(2010, 4, 4);
        Assert.AreEqual(eastern, EasterDate.EasterDateTime(2010), eastern + " should be easter sunday 2010");
    }

    [Test]
    public void Easter2011()
    {
        DateTime eastern = new DateTime(2011, 4, 24);
        Assert.AreEqual(eastern, EasterDate.EasterDateTime(2011), eastern + " should be easter sunday 2011");
    }

    [Test]
    public void Easter2012()
    {
        DateTime eastern = new DateTime(2012, 4, 8);
        Assert.AreEqual(eastern, EasterDate.EasterDateTime(2012), eastern + " should be easter sunday 2012");
    }

    [Test]
    public void Easter2013()
    {
        DateTime eastern = new DateTime(2013, 3, 31);
        Assert.AreEqual(eastern, EasterDate.EasterDateTime(2013), eastern + " should be easter sunday 2013");
    }

    [Test]
    public void NotEaster()
    {
        DateTime christmas = new DateTime(2009, 12, 24);
        Assert.AreNotEqual(christmas, EasterDate.EasterDateTime(2009), christmas + " was not easter sunday in 2009. It was christmas eve.");
    }
}

public static class EasterDateTimeExtension
{
    public static bool IsEaster(this DateTime dateTime)
    {
        return dateTime.IsEaster(EasterCalendar.Default);
    }

    public static bool IsEaster(this DateTime dateTime, EasterCalendar calendar)
    {
        DateTime easterDateTime = EasterDate.EasterDateTime(dateTime.Year, calendar);
        return easterDateTime.Date.Equals(dateTime.Date);            
    }
}

Informationen über den Autor

esskar

ist das, was man einen Fullstack Developer bezeichnet. Vom Design bis in die Tiefen der C-Programmierung verfügt er nationale und internationale Projekterfahrung, und alles unter dem Motto — Code ist Poesie. Follow @esskar