Module:Pasen

Uit Wiki Raamsdonks Historie
Naar navigatie springen Naar zoeken springen

Documentatie voor deze module kan aangemaakt worden op de volgende pagina: Module:Pasen/doc

local m = {}

local EasterData = {
    defaultMethod = 3,        -- default method of Easter date calculation when Easter type is not given
    defaultFormat = "Y-m-d",  -- default date output format
    noFormat      = "geen",   -- prevent from final date formatting
    defaultOffset = 0,        -- the Easter date
    minimumOffset = -63,      -- Septuagesima
    maximumOffset = 252,      -- 27e zondag na Trinitatis

    -- API
    apiEaster            = "Bereken",   -- public function name
    argEasterYear        = 1,           -- index or name of the argument with year
    argEasterMethod      = "methode",   -- index or name of the argument with calculation method
    argEasterOffset      = "dag",       -- index or name of the argument with offset in days relative to the calculated Easter Sunday
    argEasterFormat      = "formaat",   -- index or name of the argument with date output format (#time style)

    -- errors
    errorMissingYear     = "Ontbrekende verplichte parameter 1 (jaar)",
    errorInvalidYear     = "Incorrecte parameter 1 (jaar): '%s'",
    errorInvalidOffset   = "Incorrecte parameter 'dag': '%s'",
    errorInvalidMethod   = "Incorrecte parameter 'methode': '%s'",
    errorYearOutOfRange  = "Paasdata zijn beschikbaar tussen de jaren 326 en 4099; jaar: %d",
    errorIncorrectMethod = "Westers of Orthodox Pasen bestaan sinds 1583; jaar: %d",
    errorUnknownMethod   = "Onbekende methode: %d",

    methods = {
        ["Juliaans"]    = 1,
        ["Oosters"]     = 2,
        ["Orthodox"]    = 2, -- alias voor Oosters
        ["Koptisch"]    = 2, -- alias voor Oosters
        ["Ethiopisch"]  = 2, -- alias voor Oosters
        ["Westers"]     = 3,
        ["Gregoriaans"] = 3, -- alias voor Westers
        ["Rooms"]       = 3, -- alias voor Westers
    },
    -- the Meletian/Revised Julian Calendar from 1923 used by some Orthodox churches
    -- and any proposed reformed algorithms are not supported (yet):
    -- * astronomically observed Nicean rule at the meridian of Jerusalem (Aleppo 1997 proposal), differs from Gregorian in 
    -- * fifteenth Sunday of the year: Sunday in 099–105 day of the year
    -- * Sunday after second Saturday in April (UK): Sunday in 9–15 April
    -- * second Sunday in April: Sunday in 8–14 April
    -- * Sunday after 6 April (Pepuzite sect): Sunday in 7–13 April
    -- * World Calendar: day 099, any day of the week in Gregorian/Julian calendar
    -- * Positivist Calendar: day 098, any day of the week in Gregorian/Julian calendar

    -- * Sunday of ISO week 14: Sunday in 099–105 day of the year

    -- * Sunday of ISO week 15: Sunday in 106–112 day of the year
    -- * Nisan 14: any day of the week

    -- * Nisan 15: any day of the week


    relativeDates = {
        ["Septuagesima"]             = -63,
        ["Sexagesima"]               = -56,
        ["Quinquagesima"]            = -49,
        ["Estomihi"]                 = -49,
        ["Vastenavond"]              = -47,
        ["Carnaval"]                 = -47,
        ["Aswoensdag"]               = -46,
        ["Invocabit"]                = -42,
        ["Reminiscere"]              = -35,
        ["Oculi"]                    = -28,
        ["Laetare"]                  = -21,
        ["Halfvasten"]               = -21,
        ["Judica"]                   = -14,
        ["Palmpasen"]                =  -7,
        ["Schorselwoensdag"]         =  -4, 
        ["Schortelwoensdag"]         =  -4, 
        ["Witte Donderdag"]          =  -3,
        ["Goede Vrijdag"]            =  -2,
        ["Stille Zaterdag"]          =  -1, 
        ["Pasen"]                    =   0,
        ["Paasmaandag"]              =   1,
        ["Tweede paasdag"]           =   1,
        ["Beloken Pasen"]            =   7,
        ["Quasimodo-zondag"]         =   7,
        ["Quasi modo geniti"]        =   7,
        ["Misericordias Domini"]     =  14,
        ["Jubilate"]                 =  21,
        ["Cantate"]                  =  28,
        ["Rogate"]                   =  35,
        ["Hemelvaart"]               =  39,
        ["Exaudi"]                   =  42,
        ["Wezenzondag"]              =  42,
        ["Pinksteren"]               =  49,
        ["Pinkstermaandag"]          =  50,
        ["Trinitatis"]               =  56, 
        ["Sacramentsdag"]            =  60,
        ["Sacramentszondag"]         =  63,
        ["1e zondag na Trinitatis"]  =  63,
        ["Heilig Hart"]              =  68,
        ["Onbevlekt Hart van Maria"] =  69,
        ["2e zondag na Trinitatis"]  =  70,
        ["3e zondag na Trinitatis"]  =  77,
        ["4e zondag na Trinitatis"]  =  84,
        ["5e zondag na Trinitatis"]  =  91,
        ["6e zondag na Trinitatis"]  =  98,
        ["7e zondag na Trinitatis"]  = 105,
        ["8e zondag na Trinitatis"]  = 112,
        ["9e zondag na Trinitatis"]  = 119,
        ["10e zondag na Trinitatis"] = 126,
        ["11e zondag na Trinitatis"] = 133,
        ["12e zondag na Trinitatis"] = 140,
        ["13e zondag na Trinitatis"] = 147,
        ["14e zondag na Trinitatis"] = 154,
        ["15e zondag na Trinitatis"] = 161,
        ["16e zondag na Trinitatis"] = 168,
        ["17e zondag na Trinitatis"] = 175,
        ["18e zondag na Trinitatis"] = 182,
        ["19e zondag na Trinitatis"] = 189,
        ["20e zondag na Trinitatis"] = 196,
        ["21e zondag na Trinitatis"] = 203,
        ["22e zondag na Trinitatis"] = 210,
        ["23e zondag na Trinitatis"] = 217,
        ["24e zondag na Trinitatis"] = 224, -- bestaat niet in elk kerkelijk jaar
        ["25e zondag na Trinitatis"] = 231, -- bestaat niet in elk kerkelijk jaar
        ["26e zondag na Trinitatis"] = 238, -- bestaat niet in elk kerkelijk jaar
        ["27e zondag na Trinitatis"] = 245, -- bestaat niet in elk kerkelijk jaar
        ["28e zondag na Trinitatis"] = 252, -- bestaat niet in elk kerkelijk jaar
    },
}

local function formatEasterError(message, ...)
    if select('#', ... ) > 0 then
        message = string.format(message, ...)
    end
    return "<span class=\"error\">" .. message .. "</span>"
end

local function loadEasterYear(year)
    if not year then
        return false, formatEasterError(EasterData.errorMissingYear)
    end
    local result = tonumber(year)
    if not result or math.floor(result) ~= result then
        return false, formatEasterError(EasterData.errorInvalidYear, year)
    end

    return true, result
end

local function loadEasterMethod(method, year)
    local result = EasterData.defaultMethod
    if method then
        result = EasterData.methods[method]
        if not result then
            return false, formatEasterError(EasterData.errorInvalidMethod, method)
        end
    end

    if year < 1583 then
        result = 1
    end

    return true, result
end

local function loadEasterOffset(day)
    if not day then
        return true, ""
    end

    local data = EasterData.relativeDates
    local offset = tonumber(day)
    if not offset then
        offset = data[day]
    end
    if not offset or offset ~= math.floor(offset) or offset < EasterData.minimumOffset or offset > EasterData.maximumOffset then
        return false, formatEasterError(EasterData.errorInvalidOffset, day)
    end

    if offset < -1 then
        return true, string.format(" %d days", offset)
    elseif offset == -1 then
        return true, " -1 day"
    elseif offset == 0 then
        return true, ""
    elseif offset == 1 then
        return true, " +1 day"
    else -- if offset > 1 then
        return true, string.format(" +%d days", offset)
    end
end

local function loadEasterFormat(fmt)
    if fmt == EasterData.noFormat then
        return true, nil
    elseif not fmt then
        return true, EasterData.defaultFormat
    else
        return true, fmt
    end
end

--[[
 PURPOSE:     This function returns Easter Sunday day and month
              for a specified year and method.

 INPUTS:      Year   - Any year between 326 and 4099.
              Method - 1 = the original calculation based on the
                           Julian calendar
                       2 = the original calculation, with the
                           Julian date converted to the
                           equivalent Gregorian calendar
                       3 = the revised calculation based on the
                           Gregorian calendar

 OUTPUTS:     None.

 RETURNS:     0, error message - Error; invalid arguments
              month, day       - month and day of the Sunday

 NOTES:
              The code is translated from DN OSP 6.4.0 sources.
              The roots of the code might be found in
              http://www.gmarts.org/index.php?go=415

 ORIGINAL NOTES:

              This algorithm is an arithmetic interpretation
              of the 3 step Easter Dating Method developed
              by Ron Mallen 1985, as a vast improvement on
              the method described in the Common Prayer Book

              Published Australian Almanac 1988
              Refer to this publication, or the Canberra Library
              for a clear understanding of the method used

              Because this algorithm is a direct translation of
              the official tables, it can be easily proved to be
              100% correct

              It's free! Please do not modify code or comments!
]]
local function calculateEasterDate(year, method)
    if year < 326 or year > 4099 then
        -- Easter dates are valid for years between 326 and 4099
        -- Method 2 would have to support dates in June thereafter
        return 0, formatEasterError(EasterData.errorYearOutOfRange, year)
    end
    if year < 1583 and method ~= 1 then
        -- Western or Orthodox Easter is valid since 1583
        return 0, formatEasterError(EasterData.errorIncorrectMethod, year)
    end

    -- intermediate result
    local firstDig = math.floor(year / 100)
    local remain19 = year % 19
    local temp = 0
    -- table A to E results
    local tA = 0
    local tB = 0
    local tC = 0
    local tD = 0
    local tE = 0
     -- Easter Sunday day
    local d = 0

    if method == 1 or method == 2 then
        -- calculate PFM date
        tA   = ((225 - 11 * remain19) % 30) + 21
        -- find the next Sunday
        tB   = (tA - 19) % 7
        tC   = (40 - firstDig) % 7
        temp = year % 100
        tD   = (temp + math.floor(temp / 4)) % 7
        tE   = ((20 - tB - tC - tD) % 7) + 1
        d    = tA + tE
        if method == 2 then
            -- convert Julian to Gregorian date
            -- 10 days were skipped in the Gregorian calendar from 5-14 Oct 1582
            temp = 10
            -- only 1 in every 4 century years are leap years in the Gregorian
            -- calendar (every century is a leap year in the Julian calendar)
            if year > 1600 then
                temp = temp + firstDig - 16 - math.floor((firstDig - 16) / 4)
            end
            d = d + temp
        end
    elseif method == 3 then
        -- calculate PFM date
        temp = math.floor((firstDig - 15) / 2)  + 202 - 11 * remain19
        if firstDig > 26 then
            temp = temp - 1
        end
        if firstDig > 38 then
            temp = temp - 1
        end
        if firstDig == 21 or firstDig == 24 or firstDig == 25 or firstDig == 33 or firstDig == 36 or firstDig == 37 then
            temp = temp - 1
        end
        temp = temp % 30
        tA   = temp + 21
        if temp == 29 then
            tA = tA - 1
        end
        if temp == 28 and remain19 > 10 then
            tA = tA - 1
        end
        -- find the next Sunday
        tB   = (tA - 19) % 7
        tC   = (40 - firstDig) % 4
        if tC == 3 then
            tC = tC + 1
        end
        if tC > 1 then
            tC = tC + 1
        end
        temp = year % 100
        tD   = (temp + math.floor(temp / 4)) % 7
        tE   = ((20 - tB - tC - tD) % 7) + 1
        d    = tA + tE
    else
        -- Unknown method
        return 0, formatEasterError(EasterData.errorUnknownMethod, method)
    end
    if d > 61 then
        -- when the original calculation is converted to the Gregorian
        -- calendar, Easter Sunday can occur in May
        return 5, d - 61
    elseif d > 31 then
        return 4, d - 31
    else
        return 3, d
    end
end

local function Easter(args)
    local ok
    local year
    ok, year = loadEasterYear(args[EasterData.argEasterYear])
    if not ok then
        return year
    end

    local method
    ok, method = loadEasterMethod(args[EasterData.argEasterMethod], year)
    if not ok then
        return method
    end

    local offset
    ok, offset = loadEasterOffset(args[EasterData.argEasterOffset])
    if not ok then
        return offset
    end

    local format
    ok, format = loadEasterFormat(args[EasterData.argEasterFormat])
    if not ok then
        return format
    end

    local month, day = calculateEasterDate(year, method)
    if month == 0 then
        return day
    end

    local result = string.format("%04d-%02d-%02d%s", year, month, day, offset)
    if format then
        result = mw.language.getContentLanguage():formatDate(format, result)
    end

    return result
end

m[EasterData.apiEaster] = function (frame)
    return Easter(frame.args)
end

return m